Add attestato generation UI
"Genera Attestato" button on the association detail page downloads the certificate PDF from the new backend endpoint; the edit dialog gained a ratification-code field (used on the certificate). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hQcuUQFue2bFvrYw2UQhK
This commit is contained in:
parent
cb0c8dc140
commit
9274377a3d
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useParams, useRouter } from "next/navigation";
|
import { useParams, useRouter } from "next/navigation";
|
||||||
import { ArrowLeft, Loader2, Plus, Trash2 } from "lucide-react";
|
import { ArrowLeft, FileDown, Loader2, Plus, Trash2 } from "lucide-react";
|
||||||
import { AppHeader } from "@/components/layout/app-header";
|
import { AppHeader } from "@/components/layout/app-header";
|
||||||
import { PageHeader } from "@/components/layout/page-header";
|
import { PageHeader } from "@/components/layout/page-header";
|
||||||
import { AssociateFormDialog } from "@/components/associations/associate-form-dialog";
|
import { AssociateFormDialog } from "@/components/associations/associate-form-dialog";
|
||||||
@ -14,7 +14,7 @@ import { UserFormDialog } from "@/components/associations/user-form-dialog";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
|
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
|
||||||
import { deleteAssociation, getAssociation, updateAssociation } from "@/lib/api";
|
import { deleteAssociation, downloadAttestato, getAssociation, updateAssociation } from "@/lib/api";
|
||||||
import type { Associate, Association, User } from "@/lib/models";
|
import type { Associate, Association, User } from "@/lib/models";
|
||||||
|
|
||||||
export default function AssociationDetailPage() {
|
export default function AssociationDetailPage() {
|
||||||
@ -31,6 +31,8 @@ export default function AssociationDetailPage() {
|
|||||||
const [userDialogOpen, setUserDialogOpen] = useState(false);
|
const [userDialogOpen, setUserDialogOpen] = useState(false);
|
||||||
const [editingUserIndex, setEditingUserIndex] = useState<number | undefined>(undefined);
|
const [editingUserIndex, setEditingUserIndex] = useState<number | undefined>(undefined);
|
||||||
|
|
||||||
|
const [generatingAttestato, setGeneratingAttestato] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getAssociation(id)
|
getAssociation(id)
|
||||||
.then(setAssociation)
|
.then(setAssociation)
|
||||||
@ -106,6 +108,18 @@ export default function AssociationDetailPage() {
|
|||||||
router.push("/dashboard");
|
router.push("/dashboard");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDownloadAttestato = async () => {
|
||||||
|
if (!association) return;
|
||||||
|
setGeneratingAttestato(true);
|
||||||
|
try {
|
||||||
|
await downloadAttestato(id, association.name);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to generate attestato:", error);
|
||||||
|
} finally {
|
||||||
|
setGeneratingAttestato(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center text-muted-foreground">
|
<div className="flex min-h-screen items-center justify-center text-muted-foreground">
|
||||||
@ -137,6 +151,14 @@ export default function AssociationDetailPage() {
|
|||||||
description={`${association.associates?.length ?? 0} soci registrati`}
|
description={`${association.associates?.length ?? 0} soci registrati`}
|
||||||
actions={
|
actions={
|
||||||
<>
|
<>
|
||||||
|
<Button variant="outline" onClick={handleDownloadAttestato} disabled={generatingAttestato}>
|
||||||
|
{generatingAttestato ? (
|
||||||
|
<Loader2 className="size-4 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<FileDown className="size-4" />
|
||||||
|
)}
|
||||||
|
Genera Attestato
|
||||||
|
</Button>
|
||||||
<EditAssociationDialog association={association} onSaved={setAssociation} />
|
<EditAssociationDialog association={association} onSaved={setAssociation} />
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
trigger={
|
trigger={
|
||||||
|
|||||||
@ -30,6 +30,7 @@ export function EditAssociationDialog({
|
|||||||
const [address, setAddress] = useState(association.address);
|
const [address, setAddress] = useState(association.address);
|
||||||
const [vatNumber, setVatNumber] = useState(association.vatNumber ?? "");
|
const [vatNumber, setVatNumber] = useState(association.vatNumber ?? "");
|
||||||
const [runtsId, setRuntsId] = useState(association.runtsId ?? "");
|
const [runtsId, setRuntsId] = useState(association.runtsId ?? "");
|
||||||
|
const [ratificationCode, setRatificationCode] = useState(association.ratificationCode ?? "");
|
||||||
const [isShown, setIsShown] = useState(association.isShown ?? true);
|
const [isShown, setIsShown] = useState(association.isShown ?? true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -38,6 +39,7 @@ export function EditAssociationDialog({
|
|||||||
setAddress(association.address);
|
setAddress(association.address);
|
||||||
setVatNumber(association.vatNumber ?? "");
|
setVatNumber(association.vatNumber ?? "");
|
||||||
setRuntsId(association.runtsId ?? "");
|
setRuntsId(association.runtsId ?? "");
|
||||||
|
setRatificationCode(association.ratificationCode ?? "");
|
||||||
setIsShown(association.isShown ?? true);
|
setIsShown(association.isShown ?? true);
|
||||||
}
|
}
|
||||||
}, [open, association]);
|
}, [open, association]);
|
||||||
@ -52,6 +54,7 @@ export function EditAssociationDialog({
|
|||||||
address,
|
address,
|
||||||
vatNumber,
|
vatNumber,
|
||||||
runtsId,
|
runtsId,
|
||||||
|
ratificationCode,
|
||||||
isShown,
|
isShown,
|
||||||
});
|
});
|
||||||
onSaved(updated);
|
onSaved(updated);
|
||||||
@ -98,6 +101,16 @@ export function EditAssociationDialog({
|
|||||||
<Input id="e-runtsId" value={runtsId} onChange={(e) => setRuntsId(e.target.value)} />
|
<Input id="e-runtsId" value={runtsId} onChange={(e) => setRuntsId(e.target.value)} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="e-ratificationCode">Codice ratifica (es. LO183)</Label>
|
||||||
|
<Input
|
||||||
|
id="e-ratificationCode"
|
||||||
|
value={ratificationCode}
|
||||||
|
onChange={(e) => setRatificationCode(e.target.value)}
|
||||||
|
placeholder="Usato nell'attestato di adesione"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center justify-between rounded-lg border p-3 sm:col-span-2">
|
<div className="flex items-center justify-between rounded-lg border p-3 sm:col-span-2">
|
||||||
<div>
|
<div>
|
||||||
<Label htmlFor="e-isShown">Associazione attiva</Label>
|
<Label htmlFor="e-isShown">Associazione attiva</Label>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user