feat(summary): add status distribution and priority breakdown breakdown analytical cards
This commit is contained in:
parent
4d8cef8605
commit
f18427c4ab
3 changed files with 195 additions and 0 deletions
|
|
@ -9,6 +9,8 @@ import { TaskService } from "@/services/task.service";
|
||||||
import { COLUMNS } from "@/types/task";
|
import { COLUMNS } from "@/types/task";
|
||||||
import SummaryHeader from "./sections/SummaryHeader";
|
import SummaryHeader from "./sections/SummaryHeader";
|
||||||
import SummaryKpiGrid from "./sections/SummaryKpiGrid";
|
import SummaryKpiGrid from "./sections/SummaryKpiGrid";
|
||||||
|
import SummaryStatusCard from "./sections/SummaryStatusCard";
|
||||||
|
import SummaryPriorityCard from "./sections/SummaryPriorityCard";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates comprehensive analytical metrics from the user's active task collection.
|
* Calculates comprehensive analytical metrics from the user's active task collection.
|
||||||
|
|
@ -93,6 +95,17 @@ export default async function SummaryPage() {
|
||||||
overdueTasks={analytics.overdueTasks}
|
overdueTasks={analytics.overdueTasks}
|
||||||
upcomingDueTasks={analytics.upcomingDueTasks}
|
upcomingDueTasks={analytics.upcomingDueTasks}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||||
|
<SummaryStatusCard
|
||||||
|
totalTasks={analytics.totalTasks}
|
||||||
|
statusCounts={analytics.statusCounts}
|
||||||
|
/>
|
||||||
|
<SummaryPriorityCard
|
||||||
|
totalTasks={analytics.totalTasks}
|
||||||
|
priorityCounts={analytics.priorityCounts}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
94
app/(app)/summary/sections/SummaryPriorityCard.tsx
Normal file
94
app/(app)/summary/sections/SummaryPriorityCard.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
/**
|
||||||
|
* @file app/summary/sections/SummaryPriorityCard.tsx
|
||||||
|
* @description Client component rendering the priority severity analysis breakdown.
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Flame } from "lucide-react";
|
||||||
|
import { PRIORITY_CONFIG } from "@/types/task";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties for the SummaryPriorityCard component.
|
||||||
|
*
|
||||||
|
* @interface SummaryPriorityCardProps
|
||||||
|
* @property {number} totalTasks - The total number of tasks to calculate percentages against.
|
||||||
|
* @property {Record<string, number>} priorityCounts - An object containing counts for each priority level.
|
||||||
|
*/
|
||||||
|
interface SummaryPriorityCardProps {
|
||||||
|
totalTasks: number;
|
||||||
|
priorityCounts: Record<string, number>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a priority breakdown card featuring progress bars, count badges,
|
||||||
|
* percentage statistics, and severity configuration tags for high, medium, and low tasks.
|
||||||
|
*
|
||||||
|
* @param {SummaryPriorityCardProps} props - The component props.
|
||||||
|
* @returns {JSX.Element} The rendered summary priority card component.
|
||||||
|
*/
|
||||||
|
export default function SummaryPriorityCard({
|
||||||
|
totalTasks,
|
||||||
|
priorityCounts,
|
||||||
|
}: SummaryPriorityCardProps) {
|
||||||
|
return (
|
||||||
|
<div className="bg-card/40 border border-border/80 backdrop-blur-xl p-4 sm:p-6 rounded-2xl shadow-sm flex flex-col justify-between space-y-6">
|
||||||
|
<div className="flex items-start justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-bold tracking-tight">
|
||||||
|
Priority Breakdown
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-foreground-muted mt-0.5">
|
||||||
|
Severity and importance weights assigned to active workloads.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="bg-accent/10 text-accent p-2 rounded-xl shrink-0">
|
||||||
|
<Flame size={18} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-6 my-auto">
|
||||||
|
{(["high", "medium", "low"] as const).map((pKey) => {
|
||||||
|
const count = priorityCounts[pKey] || 0;
|
||||||
|
const percentage =
|
||||||
|
totalTasks > 0 ? Math.round((count / totalTasks) * 100) : 0;
|
||||||
|
const config = PRIORITY_CONFIG[pKey];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={pKey} className="space-y-3">
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span
|
||||||
|
className={`px-2.5 py-0.5 rounded-lg text-xs font-semibold border ${config.className}`}
|
||||||
|
>
|
||||||
|
{config.label}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-xs text-foreground-muted font-semibold w-10 text-right">
|
||||||
|
{percentage}%
|
||||||
|
</span>
|
||||||
|
<span className="bg-background/80 py-0.5 rounded-lg border border-border/60 text-xs font-bold w-14 text-right inline-block">
|
||||||
|
{count} {count === 1 ? "item" : "items"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full bg-background/80 h-2.5 rounded-full overflow-hidden border border-border/40">
|
||||||
|
<div
|
||||||
|
className={`h-full transition-all duration-500 ease-out ${
|
||||||
|
pKey === "high"
|
||||||
|
? "bg-destructive"
|
||||||
|
: pKey === "medium"
|
||||||
|
? "bg-amber-500"
|
||||||
|
: "bg-primary"
|
||||||
|
}`}
|
||||||
|
style={{ width: `${percentage}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
88
app/(app)/summary/sections/SummaryStatusCard.tsx
Normal file
88
app/(app)/summary/sections/SummaryStatusCard.tsx
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
/**
|
||||||
|
* @file app/summary/sections/SummaryStatusCard.tsx
|
||||||
|
* @description Client component rendering the workflow status distribution progress breakdown.
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { LayoutDashboard } from "lucide-react";
|
||||||
|
import { COLUMNS } from "@/types/task";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties for the SummaryStatusCard component.
|
||||||
|
*
|
||||||
|
* @interface SummaryStatusCardProps
|
||||||
|
* @property {number} totalTasks - The total number of tasks across all statuses.
|
||||||
|
* @property {Record<string, number>} statusCounts - A record mapping each status identifier to its respective task count.
|
||||||
|
*/
|
||||||
|
interface SummaryStatusCardProps {
|
||||||
|
totalTasks: number;
|
||||||
|
statusCounts: Record<string, number>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the workflow status distribution card showing progress bars, percentages,
|
||||||
|
* and task counts for each individual pipeline stage.
|
||||||
|
*
|
||||||
|
* @param {SummaryStatusCardProps} props - The component props.
|
||||||
|
* @returns {JSX.Element} The rendered summary status card component.
|
||||||
|
*/
|
||||||
|
export default function SummaryStatusCard({
|
||||||
|
totalTasks,
|
||||||
|
statusCounts,
|
||||||
|
}: SummaryStatusCardProps) {
|
||||||
|
return (
|
||||||
|
<div className="lg:col-span-2 bg-card/40 border border-border/80 backdrop-blur-xl p-4 sm:p-6 rounded-2xl shadow-sm space-y-6">
|
||||||
|
<div className="flex items-start justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-bold tracking-tight">
|
||||||
|
Workflow Status Distribution
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-foreground-muted mt-0.5">
|
||||||
|
Task concentration across individual pipeline stages.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="bg-primary/10 text-primary p-2 rounded-xl shrink-0">
|
||||||
|
<LayoutDashboard size={18} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-5">
|
||||||
|
{COLUMNS.map((col) => {
|
||||||
|
const count = statusCounts[col.id] || 0;
|
||||||
|
const percentage =
|
||||||
|
totalTasks > 0 ? Math.round((count / totalTasks) * 100) : 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={col.id} className="space-y-2">
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<div className="flex items-center gap-2.5 font-medium">
|
||||||
|
<span
|
||||||
|
className={`w-3 h-3 rounded-full shadow-sm ${col.color}`}
|
||||||
|
/>
|
||||||
|
<span>{col.title}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-xs text-foreground-muted font-semibold w-10 text-right">
|
||||||
|
{percentage}%
|
||||||
|
</span>
|
||||||
|
<span className="bg-background/80 py-0.5 rounded-lg border border-border/60 text-xs font-bold w-14 text-right inline-block">
|
||||||
|
{count} {count === 1 ? "task" : "tasks"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full bg-background/80 h-2.5 rounded-full overflow-hidden border border-border/40">
|
||||||
|
<div
|
||||||
|
className={`h-full transition-all duration-500 ease-out ${col.color}`}
|
||||||
|
style={{ width: `${percentage}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue