This commit is contained in:
2026-03-18 20:54:43 +01:00
parent b3c8b77f12
commit 9fe656b34c
8058 changed files with 912898 additions and 23 deletions
@@ -0,0 +1,110 @@
import { ChevronDown, ChevronRight, Mail } from 'lucide-react'
import { useState } from 'react'
import { PecTypeBadge, PecStateBadge } from '@/components/PecBadge/PecBadge'
import { formatDate } from '@/lib/utils'
import type { MessageResponse } from '@/types/api.types'
interface ReceiptTreeProps {
message: MessageResponse
receipts: MessageResponse[]
}
/**
* Visualizza la gerarchia delle ricevute PEC collegate a un messaggio.
* Mostra in ordine cronologico: accettazione → consegna (o anomalia).
*/
export function ReceiptTree({ message, receipts }: ReceiptTreeProps) {
const [expanded, setExpanded] = useState(true)
if (receipts.length === 0) {
if (message.direction === 'outbound') {
return (
<div className="rounded-lg border border-dashed border-muted-foreground/30 p-4 text-sm text-muted-foreground">
<Mail className="mb-1 inline h-4 w-4 mr-1" />
Nessuna ricevuta ancora ricevuta per questo messaggio.
</div>
)
}
return null
}
return (
<div className="space-y-2">
<button
className="flex items-center gap-2 text-sm font-medium text-foreground hover:text-primary"
onClick={() => setExpanded(!expanded)}
>
{expanded ? (
<ChevronDown className="h-4 w-4" />
) : (
<ChevronRight className="h-4 w-4" />
)}
Ricevute ({receipts.length})
</button>
{expanded && (
<div className="ml-4 space-y-2 border-l-2 border-muted pl-4">
{/* Messaggio originale */}
<ReceiptNode
label="Messaggio inviato"
date={message.sent_at || message.created_at}
state={message.state}
isRoot
/>
{/* Ricevute in ordine cronologico */}
{[...receipts]
.sort(
(a, b) =>
new Date(a.received_at || a.created_at).getTime() -
new Date(b.received_at || b.created_at).getTime(),
)
.map((receipt) => (
<ReceiptNode
key={receipt.id}
label={receipt.subject || 'Ricevuta'}
date={receipt.received_at || receipt.created_at}
type={receipt.pec_type}
messageId={receipt.id}
/>
))}
</div>
)}
</div>
)
}
interface ReceiptNodeProps {
label: string
date: string | null
state?: MessageResponse['state']
type?: MessageResponse['pec_type']
messageId?: string
isRoot?: boolean
}
function ReceiptNode({ label, date, state, type, isRoot }: ReceiptNodeProps) {
return (
<div className="flex items-start gap-3">
{/* Indicatore timeline */}
<div className="mt-1 flex flex-col items-center">
<div
className={`h-3 w-3 rounded-full border-2 ${
isRoot ? 'border-primary bg-primary/20' : 'border-muted-foreground bg-muted'
}`}
/>
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 flex-wrap">
<span className="text-sm font-medium truncate">{label}</span>
{state && !isRoot && <PecStateBadge state={state} />}
{type && type !== 'posta_certificata' && <PecTypeBadge type={type} />}
</div>
{date && (
<p className="text-xs text-muted-foreground mt-0.5">{formatDate(date)}</p>
)}
</div>
</div>
)
}