feat(01-04): DocumentsSection + NotesSection — link esterni e log decisioni read-only

- DocumentsSection: link clickabili con target="_blank" rel="noopener noreferrer"
  icone SVG inline per documento ed external link, hover state con colore accent
- NotesSection: note read-only con timestamp in locale it-IT (D-12: cliente legge, admin scrive)
  empty state informativo per entrambi i componenti
- SVG inline al posto di lucide-react per compatibilita' massima
This commit is contained in:
Simone Cavalli
2026-05-14 22:13:33 +02:00
parent a4e2de0611
commit 8602bfa92f
2 changed files with 120 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
import type { ClientView } from '@/lib/client-view';
import { Card } from '@/components/ui/card';
interface DocumentsSectionProps {
documents: ClientView['documents'];
}
export function DocumentsSection({ documents }: DocumentsSectionProps) {
if (documents.length === 0) {
return (
<p className="text-sm text-[#999999] italic">
Nessun documento ancora condiviso.
</p>
);
}
return (
<div className="space-y-2">
{documents.map((doc) => (
<Card
key={doc.id}
className="rounded-lg border border-[#e5e5e5] bg-white shadow-none p-0 overflow-hidden"
>
<a
href={doc.url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-between gap-3 px-5 py-4 group hover:bg-[#f9f9f9] transition-colors"
>
{/* Icona documento */}
<div className="flex items-center gap-3 min-w-0">
<svg
className="w-4 h-4 text-[#0066cc] shrink-0"
fill="none"
stroke="currentColor"
strokeWidth={2}
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"
/>
</svg>
<span className="text-sm font-medium text-[#1a1a1a] group-hover:text-[#0066cc] truncate transition-colors">
{doc.label}
</span>
</div>
{/* Icona external link */}
<svg
className="w-4 h-4 text-[#999999] group-hover:text-[#0066cc] shrink-0 transition-colors"
fill="none"
stroke="currentColor"
strokeWidth={2}
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"
/>
</svg>
</a>
</Card>
))}
</div>
);
}
+49
View File
@@ -0,0 +1,49 @@
import type { ClientView } from '@/lib/client-view';
import { Card } from '@/components/ui/card';
interface NotesSectionProps {
notes: ClientView['notes'];
}
export function NotesSection({ notes }: NotesSectionProps) {
if (notes.length === 0) {
return (
<p className="text-sm text-[#999999] italic">
Nessuna nota ancora registrata. Le decisioni appariranno qui man mano che il progetto avanza.
</p>
);
}
return (
<div className="space-y-3">
{notes.map((note) => {
const dateFormatted = new Date(note.created_at).toLocaleDateString('it-IT', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
const timeFormatted = new Date(note.created_at).toLocaleTimeString('it-IT', {
hour: '2-digit',
minute: '2-digit',
});
return (
<Card
key={note.id}
className="rounded-lg border border-[#e5e5e5] bg-white shadow-none p-5"
>
{/* Testo nota — sola lettura (D-12: admin scrive, cliente legge) */}
<p className="text-sm text-[#1a1a1a] leading-relaxed whitespace-pre-wrap">
{note.body}
</p>
{/* Timestamp */}
<p className="text-xs text-[#999999] mt-3">
{dateFormatted} alle {timeFormatted}
</p>
</Card>
);
})}
</div>
);
}