2026-08-25 07:51:38 +02:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import Link from "next/link";
|
|
|
|
|
import { useRouter } from "next/navigation";
|
|
|
|
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { Badge } from "@/components/ui/badge";
|
|
|
|
|
import { Play, Trash2 } from "lucide-react";
|
|
|
|
|
import { toast } from "sonner";
|
|
|
|
|
|
|
|
|
|
export interface RecordingRow {
|
|
|
|
|
id: number;
|
|
|
|
|
name: string;
|
|
|
|
|
durationMs: number;
|
|
|
|
|
playCount: number;
|
|
|
|
|
lastPlayedAt: number | null;
|
|
|
|
|
createdAt: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatDuration(ms: number): string {
|
|
|
|
|
const totalSeconds = Math.round(ms / 1000);
|
|
|
|
|
const minutes = Math.floor(totalSeconds / 60);
|
|
|
|
|
const seconds = totalSeconds % 60;
|
|
|
|
|
return `${minutes}:${seconds.toString().padStart(2, "0")}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function RecordingsTable({ recordings }: { recordings: RecordingRow[] }) {
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
|
|
|
|
|
async function handleDelete(id: number) {
|
|
|
|
|
const res = await fetch(`/api/recordings/${id}`, { method: "DELETE" });
|
|
|
|
|
if (res.ok) {
|
|
|
|
|
toast.success("Recording deleted");
|
|
|
|
|
router.refresh();
|
|
|
|
|
} else {
|
|
|
|
|
toast.error("Could not delete recording");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (recordings.length === 0) {
|
|
|
|
|
return (
|
|
|
|
|
<p className="text-sm text-muted-foreground">
|
|
|
|
|
No recordings yet - start a session on the Control page and save it when you're done.
|
|
|
|
|
</p>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Table>
|
|
|
|
|
<TableHeader>
|
|
|
|
|
<TableRow>
|
|
|
|
|
<TableHead>Name</TableHead>
|
|
|
|
|
<TableHead>Duration</TableHead>
|
|
|
|
|
<TableHead>Plays</TableHead>
|
|
|
|
|
<TableHead>Last played</TableHead>
|
|
|
|
|
<TableHead className="text-right">Actions</TableHead>
|
|
|
|
|
</TableRow>
|
|
|
|
|
</TableHeader>
|
|
|
|
|
<TableBody>
|
|
|
|
|
{recordings.map((r) => (
|
|
|
|
|
<TableRow key={r.id}>
|
|
|
|
|
<TableCell className="font-medium">
|
|
|
|
|
<Link href={`/recordings/${r.id}`} className="hover:underline">
|
|
|
|
|
{r.name}
|
|
|
|
|
</Link>
|
|
|
|
|
</TableCell>
|
2026-08-25 16:43:28 +02:00
|
|
|
<TableCell className="bp-readout">{formatDuration(r.durationMs)}</TableCell>
|
2026-08-25 07:51:38 +02:00
|
|
|
<TableCell>
|
2026-08-25 16:43:28 +02:00
|
|
|
<Badge variant="secondary" className="bp-readout">
|
|
|
|
|
{r.playCount}
|
|
|
|
|
</Badge>
|
2026-08-25 07:51:38 +02:00
|
|
|
</TableCell>
|
2026-08-25 16:43:28 +02:00
|
|
|
<TableCell className="bp-readout text-muted-foreground">
|
2026-08-25 07:51:38 +02:00
|
|
|
{r.lastPlayedAt ? new Date(r.lastPlayedAt).toLocaleString() : "Never"}
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell className="flex justify-end gap-1">
|
|
|
|
|
<Button asChild variant="ghost" size="icon-sm">
|
|
|
|
|
<Link href={`/recordings/${r.id}/replay`} aria-label="Replay">
|
|
|
|
|
<Play className="size-3.5" />
|
|
|
|
|
</Link>
|
|
|
|
|
</Button>
|
|
|
|
|
<Button variant="ghost" size="icon-sm" onClick={() => void handleDelete(r.id)} aria-label="Delete">
|
|
|
|
|
<Trash2 className="size-3.5" />
|
|
|
|
|
</Button>
|
|
|
|
|
</TableCell>
|
|
|
|
|
</TableRow>
|
|
|
|
|
))}
|
|
|
|
|
</TableBody>
|
|
|
|
|
</Table>
|
|
|
|
|
);
|
|
|
|
|
}
|