summaryrefslogtreecommitdiff
path: root/makima/frontend/src/components/files/FileList.tsx
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2025-12-23 02:14:58 +0000
committersoryu <soryu@soryu.co>2025-12-23 14:47:18 +0000
commita32dc56d2e5447ef8988cb98b8686476cc94e70c (patch)
tree61307503c4af82103cea2360fe95d3ea324968d6 /makima/frontend/src/components/files/FileList.tsx
parent73649d135efccda7e446775db773e21b458de202 (diff)
downloadsoryu-a32dc56d2e5447ef8988cb98b8686476cc94e70c.tar.gz
soryu-a32dc56d2e5447ef8988cb98b8686476cc94e70c.zip
Add Postgres for persistence and File cabinet
Migrations are local only currently, and must be run manually by setting POSTGRES_CONNECTION_URI
Diffstat (limited to 'makima/frontend/src/components/files/FileList.tsx')
-rw-r--r--makima/frontend/src/components/files/FileList.tsx96
1 files changed, 96 insertions, 0 deletions
diff --git a/makima/frontend/src/components/files/FileList.tsx b/makima/frontend/src/components/files/FileList.tsx
new file mode 100644
index 0000000..7e1eea4
--- /dev/null
+++ b/makima/frontend/src/components/files/FileList.tsx
@@ -0,0 +1,96 @@
+import type { FileSummary } from "../../lib/api";
+
+interface FileListProps {
+ files: FileSummary[];
+ loading: boolean;
+ onSelect: (id: string) => void;
+ onDelete: (id: string) => void;
+}
+
+function formatDuration(seconds: number | null): string {
+ if (seconds === null) return "-";
+ const mins = Math.floor(seconds / 60);
+ const secs = Math.floor(seconds % 60);
+ return `${mins}:${secs.toString().padStart(2, "0")}`;
+}
+
+function formatDate(dateStr: string): string {
+ const date = new Date(dateStr);
+ return date.toLocaleDateString("en-US", {
+ month: "short",
+ day: "numeric",
+ year: "numeric",
+ hour: "2-digit",
+ minute: "2-digit",
+ });
+}
+
+export function FileList({
+ files,
+ loading,
+ onSelect,
+ onDelete,
+}: FileListProps) {
+ if (loading) {
+ return (
+ <div className="panel h-full flex items-center justify-center">
+ <div className="font-mono text-[#9bc3ff] text-sm">Loading files...</div>
+ </div>
+ );
+ }
+
+ return (
+ <div className="panel h-full flex flex-col">
+ <div className="font-mono text-xs text-[#9bc3ff] tracking-wide uppercase p-4 pb-2 border-b border-dashed border-[rgba(117,170,252,0.35)]">
+ FILES//
+ </div>
+
+ <div className="flex-1 overflow-y-auto">
+ {files.length === 0 ? (
+ <div className="text-center text-[#9bc3ff] text-sm font-mono opacity-60 py-8">
+ No saved files yet. Start recording to create one.
+ </div>
+ ) : (
+ <div className="divide-y divide-[rgba(117,170,252,0.15)]">
+ {files.map((file) => (
+ <div
+ key={file.id}
+ className="p-4 hover:bg-[rgba(117,170,252,0.05)] transition-colors"
+ >
+ <div className="flex items-start justify-between gap-4">
+ <button
+ onClick={() => onSelect(file.id)}
+ className="flex-1 text-left"
+ >
+ <h3 className="font-mono text-sm text-[#dbe7ff] mb-1">
+ {file.name}
+ </h3>
+ {file.description && (
+ <p className="font-mono text-xs text-[#9bc3ff] mb-2 line-clamp-2">
+ {file.description}
+ </p>
+ )}
+ <div className="flex gap-4 font-mono text-[10px] text-[#75aafc]">
+ <span>{file.transcriptCount} segments</span>
+ <span>{formatDuration(file.duration)}</span>
+ <span>{formatDate(file.createdAt)}</span>
+ </div>
+ </button>
+ <button
+ onClick={(e) => {
+ e.stopPropagation();
+ onDelete(file.id);
+ }}
+ className="px-2 py-1 font-mono text-[10px] text-red-400 hover:bg-red-400/10 border border-red-400/30 hover:border-red-400/50 transition-colors uppercase"
+ >
+ Delete
+ </button>
+ </div>
+ </div>
+ ))}
+ </div>
+ )}
+ </div>
+ </div>
+ );
+}