Skip to content

Commit 2822614

Browse files
author
raineer
committed
add two-level breadcrumb navigation
1 parent c52d9d8 commit 2822614

1 file changed

Lines changed: 55 additions & 13 deletions

File tree

  • apps/codebility/app/home/kanban/[projectId]

‎apps/codebility/app/home/kanban/[projectId]/page.tsx‎

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
TableRow,
1414
} from "@/components/ui/table";
1515
import pathsConfig from "@/config/paths.config";
16-
import { IconKanban } from "@/public/assets/svgs";
16+
import { ArrowRightIcon, IconKanban } from "@/public/assets/svgs";
1717
import { KanbanBoardType, KanbanSprintType } from "@/types/home/codev";
1818
import { createClientServerComponent } from "@/utils/supabase/server";
1919
import { format } from "date-fns";
@@ -37,6 +37,10 @@ interface PageProps {
3737
params: Promise<{ projectId: string }>;
3838
}
3939

40+
/**
41+
* Maps raw sprint data to typed sprint structure
42+
* Ensures type safety and handles null values gracefully
43+
*/
4044
const mapSprint = (sprint: any): KanbanSprintData => {
4145
return {
4246
id: String(sprint.id),
@@ -51,6 +55,10 @@ const mapSprint = (sprint: any): KanbanSprintData => {
5155
};
5256
};
5357

58+
/**
59+
* Formats date range for display in timeline column
60+
* Handles edge cases and provides fallback text
61+
*/
5462
function formatDateRange(
5563
startDate: string | null,
5664
endDate: string | null,
@@ -65,10 +73,16 @@ function formatDateRange(
6573
}
6674
}
6775

76+
/**
77+
* Enhanced Kanban Sprint Page with two-level breadcrumb navigation
78+
* Shows project sprints with timeline and board access
79+
* Follows KISS, YAGNI, DRY, and SOLID principles
80+
*/
6881
export default async function KanbanSprintPage(props: PageProps) {
6982
const params = await props.params;
7083
const supabase = await createClientServerComponent();
7184

85+
// Early validation for required parameters
7286
if (!params?.projectId) {
7387
return (
7488
<div className="mx-auto flex max-w-7xl flex-col gap-4 p-4">
@@ -78,7 +92,7 @@ export default async function KanbanSprintPage(props: PageProps) {
7892
);
7993
}
8094

81-
// start: see if the user is a member of the project
95+
// Authentication and authorization checks
8296
const {
8397
data: { user: sessionUser },
8498
} = await supabase.auth.getUser();
@@ -87,6 +101,7 @@ export default async function KanbanSprintPage(props: PageProps) {
87101
redirect("/auth/sign-in");
88102
}
89103

104+
// Verify user is member of the project
90105
const { data: member, error } = await supabase
91106
.from("project_members")
92107
.select("id")
@@ -99,10 +114,9 @@ export default async function KanbanSprintPage(props: PageProps) {
99114
if (!userIsPartOfProject) {
100115
redirect("/home/kanban");
101116
}
102-
// end: see if the user is a member of the project
103117

104118
try {
105-
// Construct the sprint query
119+
// Fetch project data with nested sprints and boards
106120
let projectWithSprintsQuery = supabase
107121
.from("projects")
108122
.select(
@@ -126,11 +140,13 @@ export default async function KanbanSprintPage(props: PageProps) {
126140
.eq("id", params.projectId)
127141
.single();
128142

129-
// Execute the query
130143
const { data, error } = await projectWithSprintsQuery;
131144
const project = data as KanbanProjectWithSprintsData | null;
132145

133-
// Render table content
146+
/**
147+
* Renders table content based on data state
148+
* Handles loading, error, and empty states appropriately
149+
*/
134150
const renderTableContent = () => {
135151
// Error handling
136152
if (error) {
@@ -193,7 +209,7 @@ export default async function KanbanSprintPage(props: PageProps) {
193209
);
194210
}
195211

196-
return sprints.map((sprint, index) => (
212+
return sprints.map((sprint) => (
197213
<TableRow
198214
key={sprint.id}
199215
className="border-customBlue-500 grid grid-cols-1 border-l-4 transition-all duration-200 hover:bg-gray-50 dark:hover:bg-gray-800/50 md:table-row"
@@ -232,17 +248,43 @@ export default async function KanbanSprintPage(props: PageProps) {
232248
));
233249
};
234250

235-
// Render the full page
251+
// Main page render
236252
return (
237253
<div className="mx-auto flex max-w-7xl flex-col gap-4 p-4">
238-
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
254+
255+
{/* Two-Level Breadcrumb Navigation */}
256+
<nav className="flex flex-row items-center gap-3 text-sm pb-2" aria-label="Breadcrumb">
257+
{/* Level 1: Kanban Board Overview */}
258+
<Link
259+
href={pathsConfig.app.kanban}
260+
className="group transition-colors duration-200"
261+
>
262+
<span className="group-hover:text-customBlue-600 dark:group-hover:text-customBlue-400 text-gray-500 dark:text-gray-400">
263+
📋 Kanban Board
264+
</span>
265+
</Link>
266+
267+
<ArrowRightIcon className="text-gray-400 dark:text-gray-500" aria-hidden="true" />
268+
269+
{/* Level 2: Current Project Name */}
270+
<span
271+
className="to-customBlue-600 bg-gradient-to-r from-purple-600 bg-clip-text font-semibold text-gray-900 text-transparent dark:text-white"
272+
aria-current="page"
273+
>
274+
{project?.name || "Loading..."}
275+
</span>
276+
</nav>
277+
278+
{/* Page Header */}
279+
<header className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
239280
<H1>Kanban Sprints - {project?.name || "Loading..."}</H1>
240281
<div className="flex flex-col items-end gap-4 md:flex-row md:items-center">
241282
<AddSprintButton projectId={params.projectId} />
242283
</div>
243-
</div>
284+
</header>
244285

245-
<div className="overflow-hidden rounded-lg border border-gray-200 shadow-sm dark:border-gray-700">
286+
{/* Sprints Table */}
287+
<main className="overflow-hidden rounded-lg border border-gray-200 shadow-sm dark:border-gray-700">
246288
<Table>
247289
<TableHeader className="hidden md:table-header-group">
248290
<TableRow className="border-0 bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900">
@@ -262,7 +304,7 @@ export default async function KanbanSprintPage(props: PageProps) {
262304
{renderTableContent()}
263305
</TableBody>
264306
</Table>
265-
</div>
307+
</main>
266308
</div>
267309
);
268310
} catch (err) {
@@ -273,4 +315,4 @@ export default async function KanbanSprintPage(props: PageProps) {
273315
</div>
274316
);
275317
}
276-
}
318+
}

0 commit comments

Comments
 (0)