154 lines
4.4 KiB
TypeScript
154 lines
4.4 KiB
TypeScript
"use client";
|
|
|
|
import { Badge } from "ikoncomponents";
|
|
import { Calendar, User, Building2, Hash, Clock, Activity } from "lucide-react";
|
|
import { Project } from "../../../types/project";
|
|
import Workflow from "../workflow";
|
|
import { calculateDurationInDays } from "@/app/utils/function/projectDuration";
|
|
import { useMemo } from "react";
|
|
import { useAppCache } from "@/app/utils/context/AppCacheContext";
|
|
import ProjectWorkflow from "../../components/projectWorkflow";
|
|
|
|
type Props = {
|
|
project: Project;
|
|
};
|
|
|
|
export default function SummaryTab({ project }: Props) {
|
|
const { userNameMap } = useAppCache();
|
|
|
|
const duration = calculateDurationInDays(
|
|
project.contractedStartDate,
|
|
project.contractedEndDate,
|
|
);
|
|
|
|
const teamNames = useMemo(
|
|
() =>
|
|
(project.projectTeam ?? []).map(
|
|
(id) => userNameMap.get(id) || "Unknown User",
|
|
),
|
|
[project.projectTeam, userNameMap],
|
|
);
|
|
|
|
return (
|
|
<div className="grid gap-6">
|
|
{/* ================= WORKFLOW ================= */}
|
|
<ProjectWorkflow projectId ={project.projectIdentifier}/>
|
|
|
|
<div className="w-full flex gap-6">
|
|
<div className="xl:col-span-2 bg-card border rounded-xl p-6 w-2/3">
|
|
<h2 className="text-lg font-semibold">Project Details</h2>
|
|
|
|
<p className="text-muted-foreground mt-2 mb-4">
|
|
{project.projectDescription || "No description provided for this project."}
|
|
</p>
|
|
|
|
<div className="border-t border-border my-4" />
|
|
|
|
<div className="flex w-full">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-10 gap-y-6 w-1/2">
|
|
|
|
<InfoItem
|
|
icon={<Building2 size={18} />}
|
|
label="Source"
|
|
value={project.source || "Manual"}
|
|
/>
|
|
|
|
<InfoItem
|
|
icon={<User size={18} />}
|
|
label="Project Manager"
|
|
value={
|
|
userNameMap.get(project.projectManager) ||
|
|
project.projectManager ||
|
|
"—"
|
|
}
|
|
/>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-10 gap-y-6 w-1/2">
|
|
|
|
<InfoItem
|
|
icon={<Activity size={18} />}
|
|
label="Status"
|
|
value={project.projectStatus}
|
|
/>
|
|
|
|
<InfoItem
|
|
icon={<Calendar size={18} />}
|
|
label="Start Date"
|
|
value={project.contractedStartDate}
|
|
/>
|
|
|
|
<InfoItem
|
|
icon={<Calendar size={18} />}
|
|
label="End Date"
|
|
value={project.contractedEndDate}
|
|
/>
|
|
|
|
<InfoItem
|
|
icon={<Clock size={18} />}
|
|
label="Duration"
|
|
value={duration ? duration : "-"}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-card border rounded-xl p-6 w-1/3">
|
|
<h2 className="text-lg font-semibold mb-4">Team Members</h2>
|
|
|
|
<div className="space-y-4">
|
|
{(teamNames || []).map((name: string, index: number) => {
|
|
const initials = name
|
|
.split(" ")
|
|
.map((n) => n[0])
|
|
.join("")
|
|
.slice(0, 2)
|
|
.toUpperCase();
|
|
|
|
return (
|
|
<div key={index} className="flex items-center gap-3">
|
|
<div className="h-9 w-9 rounded-full bg-muted flex items-center justify-center text-xs font-semibold uppercase">
|
|
{initials}
|
|
</div>
|
|
|
|
<div>
|
|
<div className="font-medium">{name}</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
|
|
{(!teamNames || teamNames.length === 0) && (
|
|
<div className="text-sm text-muted-foreground">
|
|
No team members assigned
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/* ================= SMALL COMPONENT ================= */
|
|
|
|
function InfoItem({
|
|
icon,
|
|
label,
|
|
value,
|
|
}: {
|
|
icon: React.ReactNode;
|
|
label: string;
|
|
value?: string | number | null;
|
|
}) {
|
|
return (
|
|
<div className="flex items-center gap-3">
|
|
<div className="text-muted-foreground">{icon}</div>
|
|
<div>
|
|
<div className="text-sm text-muted-foreground">{label}</div>
|
|
<div className="font-medium">{value || "—"}</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|