Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | 6x 84x 84x 84x 34x 34x 34x 34x 84x 7x 7x 5x 84x 55x 14x 14x 8x 4x 8x 4x 18x 84x 34x 13x 84x 84x 84x 7x 2x | import { useState, useMemo, useEffect } from "react";
import { FileText, Layers } from "lucide-react";
import { SectionCard } from "../../profile/SectionCard";
import { ReadOnlyField } from "../../profile/ReadOnlyField";
import { BasicInfoModal } from "../modals/BasicInfoModal";
import { ScopeAreasModal } from "../modals/ScopeAreasModal";
import { taxonomyApi, type Project, type ServiceCategory } from "../../../lib/api";
interface DetailsTabProps {
project: Project;
onSave: (data: Partial<Project>) => void | Promise<void>;
}
const SCOPE_LABELS: Record<string, string> = {
design: "Design",
material: "Material",
execution: "Execution",
};
export function DetailsTab({ project, onSave }: DetailsTabProps) {
const [editingSection, setEditingSection] = useState<"basic" | "scope" | null>(null);
const [serviceCategories, setServiceCategories] = useState<ServiceCategory[]>([]);
useEffect(() => {
let cancelled = false;
taxonomyApi.getServiceCategories().then((res) => {
if (!cancelled) setServiceCategories(res.data || []);
}).catch(() => {});
return () => { cancelled = true; };
}, []);
const handleSave = async (data: Partial<Project>) => {
try {
await onSave(data);
setEditingSection(null);
} catch {
// Error is handled by the parent component
}
};
// Resolve worked area IDs to names
const workedAreaNames = useMemo(() => {
if (!project.workedAreaIds || project.workedAreaIds.length === 0) return null;
const idToName: Record<string, string> = {
full_home: "Full Home",
full_office: "Full Office",
};
for (const cat of serviceCategories) {
if (cat.children && cat.children.length > 0) {
for (const child of cat.children) {
idToName[child.id] = child.name;
}
} else {
idToName[cat.id] = cat.name;
}
}
return project.workedAreaIds.map((id) => idToName[id] || id);
}, [project.workedAreaIds, serviceCategories]);
const scopeLabels = useMemo(() => {
if (!project.scope || project.scope.length === 0) return null;
return (project.scope as string[]).map((s) => SCOPE_LABELS[s] || s);
}, [project.scope]);
const isBasicEmpty = !project.title && !project.description;
const isScopeEmpty = (!project.scope || project.scope.length === 0) &&
(!project.workedAreaIds || project.workedAreaIds.length === 0);
return (
<div className="space-y-6">
<SectionCard
title="Basic Info"
icon={<FileText className="h-4 w-4" />}
onEdit={() => setEditingSection("basic")}
isEmpty={isBasicEmpty}
>
<dl className="grid grid-cols-1 gap-4">
<ReadOnlyField label="Title" value={project.title} />
<ReadOnlyField label="Description" value={project.description} />
</dl>
</SectionCard>
<SectionCard
title="Scope & Areas"
icon={<Layers className="h-4 w-4" />}
onEdit={() => setEditingSection("scope")}
isEmpty={isScopeEmpty}
>
<dl className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<ReadOnlyField label="Scope of Work" value={scopeLabels} />
<ReadOnlyField label="Worked Areas" value={workedAreaNames} />
</dl>
</SectionCard>
{editingSection === "basic" && (
<BasicInfoModal
project={project}
onSave={handleSave}
onClose={() => setEditingSection(null)}
/>
)}
{editingSection === "scope" && (
<ScopeAreasModal
project={project}
onSave={handleSave}
onClose={() => setEditingSection(null)}
/>
)}
</div>
);
}
|