first commit
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { Button, Badge, RenderAppBreadcrumb } from "ikoncomponents";
|
||||
import { Pencil, CalendarDays, UserRound, Building2, Hash } from "lucide-react";
|
||||
|
||||
import { getProjectByIdentifierApi } from "@/app/utils/api/projectApi";
|
||||
import { useAppCache } from "@/app/utils/context/AppCacheContext";
|
||||
import { Project } from "../types/project";
|
||||
|
||||
export default function ProjectDetailsLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const { projectIdentifier } = useParams();
|
||||
|
||||
// Resolves the project manager UUID to a display name.
|
||||
const { userNameMap } = useAppCache();
|
||||
|
||||
const [project, setProject] = useState<Project | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!projectIdentifier) return;
|
||||
let active = true;
|
||||
getProjectByIdentifierApi(projectIdentifier as string)
|
||||
.then((data) => {
|
||||
if (active) setProject(data);
|
||||
})
|
||||
.catch((error) => console.error("Failed to load project", error));
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [projectIdentifier]);
|
||||
|
||||
const managerName = project?.projectManager
|
||||
? userNameMap.get(project.projectManager) ?? "—"
|
||||
: "—";
|
||||
|
||||
const startDate =
|
||||
project?.contractedStartDate || project?.projectStartDate || "—";
|
||||
|
||||
// Initials for the avatar tile (first letter of up to two name words).
|
||||
const initials =
|
||||
(project?.projectName || "")
|
||||
.split(" ")
|
||||
.filter(Boolean)
|
||||
.slice(0, 2)
|
||||
.map((word) => word[0])
|
||||
.join("")
|
||||
.toUpperCase() || "—";
|
||||
|
||||
// Map a status to a badge style so the state reads at a glance.
|
||||
const statusVariant = (() => {
|
||||
switch (project?.projectStatus?.toLowerCase()) {
|
||||
case "completed":
|
||||
return "verified" as const;
|
||||
case "active":
|
||||
case "ongoing":
|
||||
return "default" as const;
|
||||
default:
|
||||
return "secondary" as const;
|
||||
}
|
||||
})();
|
||||
|
||||
const handleEdit = () => {
|
||||
router.push(`/main/planning/projects/${projectIdentifier}?mode=edit`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background text-foreground mt-2 gap-4">
|
||||
{/* App-bar breadcrumb (same mechanism as the FX Rate page). The sidebar
|
||||
shell sets level 1 ("Projects"); this registers the project name at
|
||||
level 2 and updates once the project has loaded. */}
|
||||
|
||||
|
||||
<div className="relative flex flex-col gap-6 overflow-hidden rounded-xl border bg-card p-0 shadow-sm">
|
||||
{/* Top accent bar */}
|
||||
<div className="h-1 w-full bg-accent" />
|
||||
|
||||
<div className="p-5 sm:p-6">
|
||||
{/* Title row: avatar + name/subtitle + edit */}
|
||||
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Avatar tile */}
|
||||
<div className="flex h-14 w-14 shrink-0 items-center justify-center rounded-xl bg-foreground text-lg font-semibold text-background">
|
||||
{initials}
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-3">
|
||||
<h1 className="text-2xl font-bold leading-tight">
|
||||
{project?.projectName ?? "—"}
|
||||
</h1>
|
||||
<Badge variant={statusVariant}>
|
||||
{project?.projectStatus ?? "—"}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1.5 text-sm text-muted-foreground">
|
||||
<Hash className="h-3.5 w-3.5 shrink-0" />
|
||||
<span className="truncate">{projectIdentifier}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 items-center justify-end gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleEdit}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Pencil className="h-4 w-4" />
|
||||
Edit Project
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="my-5 border-t" />
|
||||
|
||||
{/* Meta row: icon-tiled fields */}
|
||||
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">
|
||||
<UserRound className="h-4 w-4" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground">
|
||||
Project Manager
|
||||
</p>
|
||||
<p className="truncate text-sm font-semibold text-foreground">
|
||||
{managerName}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">
|
||||
<Building2 className="h-4 w-4" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground">
|
||||
Client
|
||||
</p>
|
||||
<p className="truncate text-sm font-semibold text-foreground">
|
||||
{project?.accountName || "—"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">
|
||||
<CalendarDays className="h-4 w-4" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground">
|
||||
Start Date
|
||||
</p>
|
||||
<p className="truncate text-sm font-semibold text-foreground">
|
||||
{startDate}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ================= MAIN CONTENT ================= */}
|
||||
<main className=" py-6">{children}</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user