Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
128 lines
5.8 KiB
TypeScript
128 lines
5.8 KiB
TypeScript
import { createFileRoute, Link } from "@tanstack/react-router";
|
|
import { useEffect, useState } from "react";
|
|
import { ProtectedLayout } from "@/components/protected-layout";
|
|
import { PageContainer, PageHeader } from "@/components/app-shell";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { supabase } from "@/integrations/supabase/client";
|
|
import { formatDate } from "@/lib/format";
|
|
import { FileText, Gavel, Download, Trash2, FilePlus2 } from "lucide-react";
|
|
import { toast } from "sonner";
|
|
|
|
export const Route = createFileRoute("/documents/")({
|
|
component: PleadingsIndex,
|
|
});
|
|
|
|
function PleadingsIndex() {
|
|
const [pleadings, setPleadings] = useState<any[]>([]);
|
|
|
|
const load = async () => {
|
|
const { data } = await supabase
|
|
.from("generated_documents")
|
|
.select("*")
|
|
.eq("kind", "pleading")
|
|
.order("created_at", { ascending: false });
|
|
setPleadings(data ?? []);
|
|
};
|
|
|
|
useEffect(() => { load(); }, []);
|
|
|
|
const removePleading = async (row: any) => {
|
|
if (!confirm(`Delete ${row.name}?`)) return;
|
|
if (row.storage_path) await supabase.storage.from("generated-documents").remove([row.storage_path]);
|
|
const { error } = await supabase.from("generated_documents").delete().eq("id", row.id);
|
|
if (error) toast.error(error.message); else { toast.success("Deleted"); load(); }
|
|
};
|
|
|
|
const downloadPleading = async (row: any) => {
|
|
if (!row.storage_path) { toast.error("No file stored for this pleading"); return; }
|
|
const { data, error } = await supabase.storage.from("generated-documents").createSignedUrl(row.storage_path, 60);
|
|
if (error) { toast.error(error.message); return; }
|
|
window.open(data.signedUrl, "_blank");
|
|
};
|
|
|
|
return (
|
|
<ProtectedLayout>
|
|
<PageContainer>
|
|
<PageHeader
|
|
title="Pleadings"
|
|
description="Create Florida pleadings and reuse saved pleadings as templates."
|
|
actions={
|
|
<Button asChild>
|
|
<Link to="/documents/pleading/new" search={{} as never}>
|
|
<Gavel className="h-4 w-4 mr-2" /> New Pleading
|
|
</Link>
|
|
</Button>
|
|
}
|
|
/>
|
|
|
|
<div className="grid md:grid-cols-2 gap-4 mb-8">
|
|
<Link to="/documents/pleading/new" search={{} as never}>
|
|
<Card className="hover:border-primary/40 transition-colors cursor-pointer h-full">
|
|
<CardContent className="p-5 flex items-start gap-3">
|
|
<div className="h-10 w-10 rounded-md bg-primary/10 text-primary flex items-center justify-center"><Gavel className="h-5 w-5" /></div>
|
|
<div>
|
|
<div className="font-medium">Florida Pleading</div>
|
|
<div className="text-xs text-muted-foreground mt-1">Court caption header in Bookman Old Style 12pt.</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</Link>
|
|
<Card className="h-full">
|
|
<CardContent className="p-5 flex items-start gap-3">
|
|
<div className="h-10 w-10 rounded-md bg-primary/10 text-primary flex items-center justify-center"><FilePlus2 className="h-5 w-5" /></div>
|
|
<div>
|
|
<div className="font-medium">Saved pleadings</div>
|
|
<div className="text-xs text-muted-foreground mt-1">{pleadings.length} saved · open any below as a new template.</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between">
|
|
<CardTitle className="text-base">Saved pleadings</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="p-0">
|
|
{pleadings.length === 0 ? (
|
|
<div className="text-center py-12 text-muted-foreground text-sm">No pleadings yet. Create one to save it as a reusable template.</div>
|
|
) : (
|
|
<table className="w-full text-sm">
|
|
<thead className="bg-muted/40 text-xs uppercase tracking-wider text-muted-foreground">
|
|
<tr>
|
|
<th className="text-left px-4 py-3 font-medium">Name</th>
|
|
<th className="text-left px-4 py-3 font-medium">Created</th>
|
|
<th className="text-right px-4 py-3 font-medium">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{pleadings.map((d) => (
|
|
<tr key={d.id} className="border-t hover:bg-muted/30">
|
|
<td className="px-4 py-3">
|
|
<Link to="/documents/pleading/new" search={{ from: d.id }} className="flex items-center gap-2 hover:text-primary">
|
|
<FileText className="h-4 w-4 text-muted-foreground" />
|
|
<span className="font-medium">{d.name}</span>
|
|
</Link>
|
|
</td>
|
|
<td className="px-4 py-3 text-muted-foreground">{formatDate(d.created_at)}</td>
|
|
<td className="px-4 py-3 text-right">
|
|
<Button asChild variant="ghost" size="sm">
|
|
<Link to="/documents/pleading/new" search={{ from: d.id }}>Open as template</Link>
|
|
</Button>
|
|
{d.storage_path && (
|
|
<Button variant="ghost" size="icon" onClick={() => downloadPleading(d)}><Download className="h-4 w-4" /></Button>
|
|
)}
|
|
<Button variant="ghost" size="icon" onClick={() => removePleading(d)}><Trash2 className="h-4 w-4 text-destructive" /></Button>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
</PageContainer>
|
|
</ProtectedLayout>
|
|
);
|
|
}
|