diff --git a/bun.lockb b/bun.lockb index 09fe7e3..a6973b1 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/src/app/@modal/(.)paperless/document/[id]/page.tsx b/src/app/@modal/(.)paperless/document/[id]/page.tsx deleted file mode 100644 index 3206bca..0000000 --- a/src/app/@modal/(.)paperless/document/[id]/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import DocumentViewer from "@/components/document-viewer"; -import { Modal } from "../../../../../components/modal"; - -export default function ModalDocumentPage({ - params, -}: { - params: { id: number }; -}) { - return ( - - - - ); -} diff --git a/src/app/@modal/(.)whishper/recording/[name]/page.tsx b/src/app/@modal/(.)whishper/recording/[id]/page.tsx similarity index 74% rename from src/app/@modal/(.)whishper/recording/[name]/page.tsx rename to src/app/@modal/(.)whishper/recording/[id]/page.tsx index 5033e33..2d7a01f 100644 --- a/src/app/@modal/(.)whishper/recording/[name]/page.tsx +++ b/src/app/@modal/(.)whishper/recording/[id]/page.tsx @@ -4,11 +4,11 @@ import AudioPreview from "@/components/audio-preview"; export default function ModalAudioPreview({ params, }: { - params: { name: string }; + params: { id: string }; }) { return ( - + ); } diff --git a/src/app/api/deleteWhishperRecordingByName/route.ts b/src/app/api/whishperRecording/route.ts similarity index 67% rename from src/app/api/deleteWhishperRecordingByName/route.ts rename to src/app/api/whishperRecording/route.ts index 6c79ab6..0277e27 100644 --- a/src/app/api/deleteWhishperRecordingByName/route.ts +++ b/src/app/api/whishperRecording/route.ts @@ -1,12 +1,11 @@ import { getUserData } from "@/app/actions"; -import { getWhishperRecordings } from "@/app/whishper/page"; export async function DELETE(req: Request) { const url = new URL(req.url); - const name = url.searchParams.get("name"); + const id = url.searchParams.get("id"); - if (!name) { - return new Response("Name parameter is missing", { status: 400 }); + if (!id) { + return new Response("id parameter is missing", { status: 400 }); } const userData = await getUserData(); @@ -15,10 +14,8 @@ export async function DELETE(req: Request) { return new Response("Unauthorized", { status: 401 }); } - console.log(getWhishperRecordings(name)); - const response = await fetch( - `${userData.whishperURL}/api/transcriptions/${name}`, + `${userData.whishperURL}/api/transcriptions/${id}`, { method: "DELETE", }, diff --git a/src/app/whishper/page.tsx b/src/app/whishper/page.tsx index 4ab7b4a..022910f 100644 --- a/src/app/whishper/page.tsx +++ b/src/app/whishper/page.tsx @@ -46,7 +46,7 @@ import type { WhishperRecordingType } from "@/types"; const queryClient = new QueryClient(); -export async function getWhishperRecordings( +async function getWhishperRecordings( query: string, ): Promise { const userData = await getUserData(); @@ -224,7 +224,7 @@ function DataTable({ header: "Name", cell: ({ getValue, row }) => ( {getValue() as string} diff --git a/src/app/whishper/recording/[name]/page.tsx b/src/app/whishper/recording/[id]/page.tsx similarity index 72% rename from src/app/whishper/recording/[name]/page.tsx rename to src/app/whishper/recording/[id]/page.tsx index b65f9d1..7d8efbe 100644 --- a/src/app/whishper/recording/[name]/page.tsx +++ b/src/app/whishper/recording/[id]/page.tsx @@ -3,11 +3,11 @@ import AudioPreview from "@/components/audio-preview"; export default function FullAudioPage({ params, }: { - params: { name: string }; + params: { id: string }; }) { return (
- +
); } diff --git a/src/components/audio-preview.tsx b/src/components/audio-preview.tsx index b4d3f47..903e67a 100644 --- a/src/components/audio-preview.tsx +++ b/src/components/audio-preview.tsx @@ -6,21 +6,35 @@ import { QueryClient, } from "@tanstack/react-query"; import type { UsersTableType } from "@/server/db/schema"; -import { Button } from "@/components/ui/button"; +import { Button, buttonVariants } from "@/components/ui/button"; import { useRouter } from "next/navigation"; -import { ChevronLeft, ExternalLink } from "lucide-react"; +import { ChevronLeft } from "lucide-react"; +import { useState } from "react"; +import OpenExternalLink from "./external-link"; +import type { WhishperRecordingType } from "@/types"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; -import { useState } from "react"; +import { toast } from "sonner"; const queryClient = new QueryClient(); const fetchUserData = async (): Promise => { - const response = await fetch(`/api/getUserData`); + const response = await fetch("/api/getUserData"); if (!response.ok) { throw new Error("Network error"); } @@ -53,30 +67,93 @@ function SkeletonLoader() { ); } -function AudioInfo(props: { name: string }) { +async function fetchWhishperRecording(searchId: string, whishperURL: string) { + const response = await fetch(`${whishperURL}/api/transcriptions`); + const data = (await response.json()) as WhishperRecordingType[]; + for (const recording of data) { + if (recording.id === searchId) { + return recording; + } + } +} + +async function deleteWhishperRecording(url: string) { + const response = await fetch(url, { + method: "DELETE", + }); + + if (!response.ok) { + throw new Error("Network error"); + } + + return response; +} + +type AudioInfoProps = { + id: string; +}; + +function AudioInfo({ id }: AudioInfoProps) { const router = useRouter(); const [isPlaying, setIsPlaying] = useState(false); const { data: userData, isLoading: isUserDataLoading, - error, + error: userDataError, } = useQuery({ queryKey: ["userData"], queryFn: fetchUserData, }); - const decodedName = decodeURIComponent(props.name); - const frontPart = decodedName.split("_WHSHPR_")[1] ?? decodedName; - const formattedName = frontPart.replace(".m4a", "") ?? decodedName; + const whishperURL = userData?.whishperURL; + + const { + data: recordingData, + isLoading: isRecordingDataLoading, + error: recordingDataError, + } = useQuery({ + queryKey: ["whishperRecording", id, whishperURL], // Include id in the query key + queryFn: () => fetchWhishperRecording(id, whishperURL!), + enabled: !!whishperURL, // Only fetch recording data when userData is available + }); if (isUserDataLoading) { return ; } - if (!userData?.whishperURL ?? error) { - return

Failed to get whishper url

; + + if (userDataError ?? !userData) { + return ( +
+
+

+ Error loading user data +

+
+
+ ); + } + + if (isRecordingDataLoading) { + return ; } + if (recordingDataError ?? !recordingData) { + return ( +
+
+

+ Error loading recording data +

+
+
+ ); + } + + const decodedName = decodeURIComponent(recordingData.fileName); + const frontPart = decodedName.split("_WHSHPR_")[1] ?? decodedName; + const formattedName = frontPart.replace(".m4a", "") ?? decodedName; + return (
@@ -104,52 +181,81 @@ function AudioInfo(props: { name: string }) { onPause={() => setIsPlaying(false)} >
- - - - - - Comming soon! - - - - - - - - Comming soon! - - - + + - - + + + Download + - Comming soon! + +

+ To download the audio file, right click and select + "Save as". +

+
+ + + + + + + + Are you absolutely sure? + + + This action cannot be undone. This will permanently delete + the recording. + + + + Cancel + { + const response = await deleteWhishperRecording( + `${userData.whishperURL}/api/transcriptions/${id}`, + ); + if (response.ok) { + toast("Recording deleted", { + description: "The recording has been deleted.", + }); + } else { + toast("Error deleting recording", { + description: + "An error occurred while deleting the recording.", + }); + } + router.back(); + }} + > + Continue + + + +
@@ -158,10 +264,10 @@ function AudioInfo(props: { name: string }) { ); } -export default function AudioPreview({ name }: { name: string }) { +export default function AudioPreview({ id }: { id: string }) { return ( - + ); } diff --git a/src/components/document-viewer.tsx b/src/components/document-viewer.tsx index 97d0f02..1f9d774 100644 --- a/src/components/document-viewer.tsx +++ b/src/components/document-viewer.tsx @@ -1,7 +1,7 @@ "use client"; import { useState, useEffect, useRef } from "react"; -import { Button } from "./ui/button"; +import { Button, buttonVariants } from "./ui/button"; import { useRouter } from "next/navigation"; import { getUserData } from "@/app/actions"; import { @@ -11,17 +11,28 @@ import { } from "@tanstack/react-query"; import type { AdviceAPIType } from "@/types"; import OpenInternalLink from "./internal-link"; +import OpenExternalLink from "./external-link"; +import type { UsersTableType } from "@/server/db/schema"; +import { Download } from "lucide-react"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog"; +import { toast } from "sonner"; const queryClient = new QueryClient(); async function getPaperlessDocument( documentId: number, + userData: UsersTableType, ): Promise { - const userData = await getUserData(); - if (!userData) { - console.error("Error getting user data"); - return null; - } try { const url = `${userData.paperlessURL}/api/documents/${documentId}/download/`; const response = await fetch(url, { @@ -85,49 +96,60 @@ function SkeletonLoader() { ); } -function DocumentViewer(props: { id: number }) { - const router = useRouter(); - - const [pdfUrl, setPdfUrl] = useState(null); - const [loading, setLoading] = useState(true); - const fetchDataCalledRef = useRef(false); +const fetchUserData = async (): Promise => { + const response = await fetch(`/api/getUserData`); + if (!response.ok) { + throw new Error("Network error"); + } + const data = (await response.json()) as UsersTableType; + return data; +}; - useEffect(() => { - if (!fetchDataCalledRef.current) { - const fetchData = async () => { - setLoading(true); +async function deleteDocument(documentId: number) { + const userData = await getUserData(); + if (!userData) { + throw new Error("User data not found"); + } + const body = { + documents: [documentId], + method: "delete", + }; + const response = await fetch( + `${userData.paperlessURL}/api/documents/bulk_edit/ `, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Token ${userData.paperlessToken}`, + }, + body: JSON.stringify(body), + }, + ); + return response; +} - try { - const objectUrl = await getPaperlessDocument(props.id); - if (objectUrl) { - setPdfUrl(objectUrl); - } else { - setPdfUrl(null); - } - } catch (error) { - console.error("An error occurred:", error); - setPdfUrl(null); - } finally { - setLoading(false); - } - }; +function DocumentViewer(props: { id: number }) { + const router = useRouter(); - fetchData().catch((error) => { - console.error("An error occurred:", error); - }); + const { data: userData, isLoading: isUserDataLoading } = useQuery({ + queryKey: ["userData"], + queryFn: fetchUserData, + }); - fetchDataCalledRef.current = true; // Mark as fetched - } - }, [props.id]); // Include props.id in the dependency array if refetch is needed on id change + const { data: pdfUrl, isLoading: isPdfUrlLoading } = useQuery({ + queryKey: ["pdfUrl", props.id, userData], // Include id and paperlessURL in the query key + queryFn: () => getPaperlessDocument(props.id, userData!), + enabled: !!userData, + }); - if (loading) { + if (isPdfUrlLoading ?? isUserDataLoading) { return ; } - if (!pdfUrl) { + if (!pdfUrl || !userData) { return (
-
+

Failed to get document

@@ -155,7 +177,7 @@ function DocumentViewer(props: { id: number }) {

-
+
+ + Download + + +
+ + Open + + + + + + + + Are you absolutely sure? + + This action cannot be undone. This will permanently delete + the recording. + + + + Cancel + { + const response = await deleteDocument(props.id); + if (response.ok) { + toast("Pdf deleted", { + description: "The recording has been deleted.", + }); + } else { + toast("Error deleting pdf", { + description: + "An error occurred while deleting the pdf.", + }); + } + router.back(); + }} + > + Continue + + + +
diff --git a/src/types/index.ts b/src/types/index.ts index 9dcbe4c..6d1f774 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -95,7 +95,20 @@ export type WhishperRecordingType = { }[]; text: string; }; - translations: []; + translations: Translation[]; +}; + +type Translation = { + sourceLanguage: string; + targetLanguage: string; + text: string; + segments: []; +}; + +export type SimpleWhishperTranscription = { + id: string; + duration: number; + name: string; }; export type AdviceAPIType = {