From a32dc56d2e5447ef8988cb98b8686476cc94e70c Mon Sep 17 00:00:00 2001 From: soryu Date: Tue, 23 Dec 2025 02:14:58 +0000 Subject: Add Postgres for persistence and File cabinet Migrations are local only currently, and must be run manually by setting POSTGRES_CONNECTION_URI --- makima/frontend/src/routes/files.tsx | 95 +++++++++++++++++++++++++++++++++++ makima/frontend/src/routes/listen.tsx | 8 +-- 2 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 makima/frontend/src/routes/files.tsx (limited to 'makima/frontend/src/routes') diff --git a/makima/frontend/src/routes/files.tsx b/makima/frontend/src/routes/files.tsx new file mode 100644 index 0000000..86a24b8 --- /dev/null +++ b/makima/frontend/src/routes/files.tsx @@ -0,0 +1,95 @@ +import { useState, useCallback, useEffect } from "react"; +import { useParams, useNavigate } from "react-router"; +import { Masthead } from "../components/Masthead"; +import { FileList } from "../components/files/FileList"; +import { FileDetail } from "../components/files/FileDetail"; +import { useFiles } from "../hooks/useFiles"; +import type { FileDetail as FileDetailType } from "../lib/api"; + +export default function FilesPage() { + const { id } = useParams<{ id: string }>(); + const navigate = useNavigate(); + const { files, loading, error, fetchFile, editFile, removeFile } = useFiles(); + const [fileDetail, setFileDetail] = useState(null); + const [detailLoading, setDetailLoading] = useState(false); + + // Load file detail when URL has an id + useEffect(() => { + if (id) { + setDetailLoading(true); + fetchFile(id).then((detail) => { + setFileDetail(detail); + setDetailLoading(false); + }); + } else { + setFileDetail(null); + } + }, [id, fetchFile]); + + const handleSelectFile = useCallback( + (fileId: string) => { + navigate(`/files/${fileId}`); + }, + [navigate] + ); + + const handleBack = useCallback(() => { + navigate("/files"); + }, [navigate]); + + const handleDelete = useCallback( + async (fileId: string) => { + if (confirm("Are you sure you want to delete this file?")) { + const success = await removeFile(fileId); + if (success && id === fileId) { + navigate("/files"); + } + } + }, + [removeFile, id, navigate] + ); + + const handleSave = useCallback( + async (fileId: string, name: string, description: string) => { + await editFile(fileId, { name, description }); + const detail = await fetchFile(fileId); + setFileDetail(detail); + }, + [editFile, fetchFile] + ); + + return ( +
+ + +
+ {error && ( +
+ {error} +
+ )} + + {id && fileDetail ? ( + + ) : id && detailLoading ? ( +
+
Loading...
+
+ ) : ( + + )} +
+
+ ); +} diff --git a/makima/frontend/src/routes/listen.tsx b/makima/frontend/src/routes/listen.tsx index 9ac0a94..aaba90c 100644 --- a/makima/frontend/src/routes/listen.tsx +++ b/makima/frontend/src/routes/listen.tsx @@ -112,10 +112,11 @@ export default function ListenPage() { setIsListening(true); }, [isListening, mic, ws]); - const handleReset = useCallback(() => { + const handleNew = useCallback(() => { + // Stop current session - backend auto-saves transcript on disconnect mic.stop(); if (ws.isConnected) { - ws.stopSession("reset"); + ws.stopSession("new_session"); } ws.clearTranscripts(); ws.disconnect(); @@ -147,8 +148,9 @@ export default function ListenPage() { isConnected={ws.isConnected} micStatus={mic.status} micVolume={mic.volume} + hasTranscripts={ws.transcripts.length > 0} onToggle={handleToggle} - onReset={handleReset} + onNew={handleNew} error={error} /> -- cgit v1.2.3