projects milestones
This commit is contained in:
@@ -8,6 +8,7 @@ import { Link, useParams } from "react-router-dom";
|
||||
import { FileAttachmentsPanel } from "../../components/FileAttachmentsPanel";
|
||||
import { useAuth } from "../../auth/AuthProvider";
|
||||
import { api, ApiError } from "../../lib/api";
|
||||
import { projectMilestoneStatusPalette } from "./config";
|
||||
import { ProjectPriorityBadge } from "./ProjectPriorityBadge";
|
||||
import { ProjectStatusBadge } from "./ProjectStatusBadge";
|
||||
|
||||
@@ -73,6 +74,27 @@ export function ProjectDetailPage() {
|
||||
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Due Date</p><div className="mt-2 text-base font-bold text-text">{project.dueDate ? new Date(project.dueDate).toLocaleDateString() : "Not set"}</div></article>
|
||||
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Created</p><div className="mt-2 text-base font-bold text-text">{new Date(project.createdAt).toLocaleDateString()}</div></article>
|
||||
</section>
|
||||
<section className="grid gap-3 xl:grid-cols-4">
|
||||
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Milestones</p>
|
||||
<div className="mt-2 text-base font-bold text-text">{project.rollups.completedMilestoneCount}/{project.rollups.milestoneCount}</div>
|
||||
<div className="mt-1 text-xs text-muted">{project.rollups.openMilestoneCount} open</div>
|
||||
</article>
|
||||
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Overdue Milestones</p>
|
||||
<div className="mt-2 text-base font-bold text-text">{project.rollups.overdueMilestoneCount}</div>
|
||||
</article>
|
||||
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Linked Work Orders</p>
|
||||
<div className="mt-2 text-base font-bold text-text">{project.rollups.workOrderCount}</div>
|
||||
<div className="mt-1 text-xs text-muted">{project.rollups.activeWorkOrderCount} active</div>
|
||||
</article>
|
||||
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Overdue Work Orders</p>
|
||||
<div className="mt-2 text-base font-bold text-text">{project.rollups.overdueWorkOrderCount}</div>
|
||||
<div className="mt-1 text-xs text-muted">{project.rollups.completedWorkOrderCount} complete</div>
|
||||
</article>
|
||||
</section>
|
||||
<div className="grid gap-3 xl:grid-cols-[minmax(0,1.05fr)_minmax(320px,0.95fr)]">
|
||||
<article className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Customer Linkage</p>
|
||||
@@ -104,6 +126,48 @@ export function ProjectDetailPage() {
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Milestones</p>
|
||||
<p className="mt-2 text-sm text-muted">Track project checkpoints, blockers, and completion progress.</p>
|
||||
</div>
|
||||
{canManage ? (
|
||||
<Link to={`/projects/${project.id}/edit`} className="inline-flex items-center justify-center rounded-2xl border border-line/70 px-2 py-2 text-sm font-semibold text-text">
|
||||
Edit milestones
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
{project.milestones.length === 0 ? (
|
||||
<div className="mt-6 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-4 py-8 text-center text-sm text-muted">
|
||||
No milestones are defined for this project yet.
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-6 space-y-3">
|
||||
{project.milestones.map((milestone) => (
|
||||
<div key={milestone.id} className="rounded-[18px] border border-line/70 bg-page/60 p-3">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="font-semibold text-text">{milestone.title}</div>
|
||||
<div className="mt-2 flex flex-wrap items-center gap-2">
|
||||
<span className={`inline-flex rounded-full px-2 py-1 text-xs font-semibold uppercase tracking-[0.16em] ${projectMilestoneStatusPalette[milestone.status]}`}>
|
||||
{milestone.status.replace("_", " ")}
|
||||
</span>
|
||||
<span className="text-xs text-muted">
|
||||
Due {milestone.dueDate ? new Date(milestone.dueDate).toLocaleDateString() : "not scheduled"}
|
||||
</span>
|
||||
{milestone.completedAt ? (
|
||||
<span className="text-xs text-muted">Completed {new Date(milestone.completedAt).toLocaleDateString()}</span>
|
||||
) : null}
|
||||
</div>
|
||||
{milestone.notes ? <div className="mt-3 whitespace-pre-line text-sm text-text">{milestone.notes}</div> : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
{planning ? (
|
||||
<section className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Material Readiness</p>
|
||||
|
||||
Reference in New Issue
Block a user