feat: implement project contacts fetching and display in ProjectViewPage

This commit is contained in:
2025-12-10 14:54:59 +01:00
parent 628ace4ad5
commit b1a1735a12

View File

@@ -30,6 +30,7 @@ export default function ProjectViewPage() {
const [uploadedFiles, setUploadedFiles] = useState([]); const [uploadedFiles, setUploadedFiles] = useState([]);
const [editingNoteId, setEditingNoteId] = useState(null); const [editingNoteId, setEditingNoteId] = useState(null);
const [editText, setEditText] = useState(''); const [editText, setEditText] = useState('');
const [projectContacts, setProjectContacts] = useState([]);
// Helper function to parse note text with links // Helper function to parse note text with links
const parseNoteText = (text) => { const parseNoteText = (text) => {
@@ -163,9 +164,14 @@ export default function ProjectViewPage() {
const filesRes = await fetch(`/api/files?entityType=project&entityId=${params.id}`); const filesRes = await fetch(`/api/files?entityType=project&entityId=${params.id}`);
const filesData = filesRes.ok ? await filesRes.json() : []; const filesData = filesRes.ok ? await filesRes.json() : [];
// Fetch project contacts
const contactsRes = await fetch(`/api/projects/${params.id}/contacts`);
const contactsData = contactsRes.ok ? await contactsRes.json() : [];
setProject(projectData); setProject(projectData);
setNotes(notesData); setNotes(notesData);
setUploadedFiles(filesData); setUploadedFiles(filesData);
setProjectContacts(contactsData);
} catch (error) { } catch (error) {
console.error('Error fetching data:', error); console.error('Error fetching data:', error);
setProject(null); setProject(null);
@@ -221,6 +227,17 @@ export default function ProjectViewPage() {
return "success"; return "success";
}; };
const getContactTypeBadge = (type) => {
const types = {
project: { label: "Projekt", variant: "primary" },
contractor: { label: "Wykonawca", variant: "warning" },
office: { label: "Urząd", variant: "info" },
supplier: { label: "Dostawca", variant: "success" },
other: { label: "Inny", variant: "secondary" },
};
return types[type] || types.other;
};
return ( return (
<PageContainer> <PageContainer>
{/* Mobile: Full-width title, Desktop: Standard PageHeader */} {/* Mobile: Full-width title, Desktop: Standard PageHeader */}
@@ -479,12 +496,53 @@ export default function ProjectViewPage() {
{project.contact && ( {project.contact && (
<div className="border-t pt-4"> <div className="border-t pt-4">
<span className="text-sm font-medium text-gray-500 block mb-1"> <span className="text-sm font-medium text-gray-500 block mb-1">
Kontakt Kontakt (stary format)
</span> </span>
<p className="text-gray-900 font-medium">{project.contact}</p> <p className="text-gray-900 font-medium">{project.contact}</p>
</div> </div>
)} )}
{/* New Contacts System */}
{projectContacts.length > 0 && (
<div className="border-t pt-4">
<span className="text-sm font-medium text-gray-500 block mb-2">
Kontakty
</span>
<div className="space-y-2">
{projectContacts.map((contact) => {
const typeBadge = getContactTypeBadge(contact.contact_type);
return (
<div
key={contact.contact_id}
className="p-3 bg-gray-50 rounded-md border border-gray-200"
>
<div className="flex items-center gap-2 mb-1">
<span className="font-medium text-gray-900">
{contact.name}
</span>
{contact.is_primary === 1 && (
<Badge variant="success" size="xs">
Główny
</Badge>
)}
<Badge variant={typeBadge.variant} size="xs">
{typeBadge.label}
</Badge>
</div>
<div className="text-sm text-gray-600 space-y-0.5">
{contact.phone && <div>📞 {contact.phone}</div>}
{contact.email && <div>📧 {contact.email}</div>}
{contact.company && (
<div className="text-xs text-gray-500">🏢 {contact.company}</div>
)}
</div>
</div>
);
})}
</div>
</div>
)}
{project.coordinates && ( {project.coordinates && (
<div className="border-t pt-4"> <div className="border-t pt-4">
<span className="text-sm font-medium text-gray-500 block mb-1"> <span className="text-sm font-medium text-gray-500 block mb-1">