Skip to content

Commit 00f6e32

Browse files
Merge branch 'ftr/update-ui' into test-deployment
2 parents 4532aed + 64fa512 commit 00f6e32

File tree

9 files changed

+352
-158
lines changed

9 files changed

+352
-158
lines changed

frontend/src/app/(app)/cases/[id]/layout.tsx

Lines changed: 22 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,11 @@
11
"use client";
22

33
import { useEffect, useState } from "react";
4-
import { useParams, useRouter, usePathname } from "next/navigation";
5-
import {
6-
ArrowLeft,
7-
Terminal,
8-
Network,
9-
Clock,
10-
FolderOpen,
11-
Globe,
12-
} from "lucide-react";
13-
import Link from "next/link";
4+
import { useParams, useRouter } from "next/navigation";
145
import { clsx } from "clsx";
156

167
import { api, ApiError } from "@/lib/api-client";
178
import type { Case, CaseStatus } from "@/types/case";
18-
import { ExpandableTabs } from "@/components/ui/expandable-tabs";
199
import { Chatbot } from "@/components/app/chatbot";
2010

2111
const statusConfig: Record<
@@ -41,22 +31,13 @@ const statusConfig: Record<
4131
},
4232
};
4333

44-
const navItems = [
45-
{ title: "Command Center", icon: Terminal, href: "/command-center" },
46-
{ title: "Knowledge Graph", icon: Network, href: "/knowledge-graph" },
47-
{ title: "Evidence Library", icon: FolderOpen, href: "/library" },
48-
{ title: "Timeline", icon: Clock, href: "/timeline" },
49-
{ title: "Geospatial", icon: Globe, href: "/geospatial" },
50-
];
51-
5234
export default function CaseLayout({
5335
children,
5436
}: {
5537
children: React.ReactNode;
5638
}) {
5739
const params = useParams();
5840
const router = useRouter();
59-
const pathname = usePathname();
6041
const [caseData, setCaseData] = useState<Case | null>(null);
6142
const [loading, setLoading] = useState(true);
6243

@@ -79,18 +60,14 @@ export default function CaseLayout({
7960
if (loading) {
8061
return (
8162
<div className="px-6 pt-4 pb-6 lg:px-8">
82-
<div className="flex items-center gap-3 mb-3">
63+
<div className="flex items-center gap-3 mb-1">
8364
<div
84-
className="h-4 w-4 rounded animate-pulse"
85-
style={{ backgroundColor: "var(--muted)" }}
86-
/>
87-
<div
88-
className="h-5 w-48 rounded animate-pulse"
65+
className="h-7 w-64 rounded animate-pulse"
8966
style={{ backgroundColor: "var(--muted)" }}
9067
/>
9168
</div>
9269
<div
93-
className="h-4 w-72 rounded animate-pulse mb-4 ml-7"
70+
className="h-5 w-80 rounded animate-pulse mb-4"
9471
style={{ backgroundColor: "var(--muted)" }}
9572
/>
9673
<div
@@ -106,75 +83,36 @@ export default function CaseLayout({
10683
}
10784

10885
const status = statusConfig[caseData.status];
109-
const basePath = `/cases/${params.id}`;
110-
111-
// Get current section from pathname
112-
const currentSection = pathname.split("/").pop() || "upload";
113-
114-
// Debug: log to verify matching
115-
console.log("CaseLayout - pathname:", pathname);
116-
console.log("CaseLayout - currentSection:", currentSection);
117-
console.log("CaseLayout - activeTab:", `/${currentSection}`);
118-
119-
const handleTabChange = (href: string) => {
120-
router.push(`${basePath}${href}`);
121-
};
12286

12387
return (
12488
<div
12589
className="min-h-screen"
12690
style={{ backgroundColor: "var(--background)" }}
12791
>
12892
<div className="px-6 pt-4 pb-6 lg:px-8">
129-
{/* Case header row: back arrow, title, status, and navigation tabs */}
130-
<div className="flex items-center justify-between mb-0.5">
131-
<div className="flex items-center gap-2.5">
132-
<Link
133-
href="/cases"
134-
className="inline-flex items-center justify-center rounded-md p-1.5 transition-colors"
135-
style={{ color: "var(--muted-foreground)" }}
136-
onMouseEnter={(e) => {
137-
e.currentTarget.style.color = "var(--foreground)";
138-
}}
139-
onMouseLeave={(e) => {
140-
e.currentTarget.style.color = "var(--muted-foreground)";
141-
}}
142-
aria-label="Back to cases"
143-
>
144-
<ArrowLeft className="w-4 h-4" />
145-
</Link>
146-
<h1
147-
className="text-base font-medium"
148-
style={{ color: "var(--foreground)" }}
149-
>
150-
{caseData.name}
151-
</h1>
152-
<span
153-
className={clsx(
154-
"px-2 py-0.5 rounded-full text-xs font-medium",
155-
status.className,
156-
)}
157-
style={status.style}
158-
>
159-
{status.label}
160-
</span>
161-
</div>
162-
163-
{/* Floating Navigation Tabs - Top Right */}
164-
<div className="shrink-0">
165-
<ExpandableTabs
166-
tabs={navItems}
167-
activeTab={`/${currentSection}`}
168-
onTabChange={handleTabChange}
169-
className="shadow-[0_8px_32px_0_rgba(0,0,0,0.12)] dark:shadow-[0_8px_32px_0_rgba(0,0,0,0.4)]"
170-
/>
171-
</div>
93+
{/* Case header: title and status */}
94+
<div className="flex items-center gap-2.5 mb-1">
95+
<h1
96+
className="text-xl font-semibold"
97+
style={{ color: "var(--foreground)" }}
98+
>
99+
{caseData.name}
100+
</h1>
101+
<span
102+
className={clsx(
103+
"px-2.5 py-1 rounded-full text-xs font-medium",
104+
status.className,
105+
)}
106+
style={status.style}
107+
>
108+
{status.label}
109+
</span>
172110
</div>
173111

174112
{/* Case description */}
175113
{caseData.description && (
176114
<p
177-
className="max-w-2xl text-xs mb-4 ml-9"
115+
className="max-w-2xl text-sm mb-4"
178116
style={{ color: "var(--muted-foreground)" }}
179117
>
180118
{caseData.description}

frontend/src/app/(app)/cases/[id]/library/page.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ export default function LibraryPage() {
88
const caseId = params.id as string;
99

1010
return (
11-
<div className="h-screen flex flex-col">
12-
<CaseLibrary caseId={caseId} caseName="Offshore Holdings Investigation" />
13-
</div>
11+
<CaseLibrary caseId={caseId} caseName="Offshore Holdings Investigation" />
1412
);
1513
}

frontend/src/app/(app)/layout.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// ABOUTME: App layout for authenticated users
2-
// ABOUTME: Validates session server-side and renders topbar shell
2+
// ABOUTME: Validates session server-side and renders sidebar navigation
33

44
export const dynamic = "force-dynamic";
55

@@ -9,7 +9,7 @@ import { redirect } from "next/navigation";
99
import { Toaster } from "sonner";
1010

1111
import { auth } from "@/lib/auth";
12-
import { Topbar } from "@/components/app/topbar";
12+
import { Sidebar } from "@/components/app/sidebar";
1313
import { AuthListener } from "@/components/app/auth-listener";
1414
import { ClearInvalidSession } from "@/app/clear-invalid-session";
1515

@@ -56,10 +56,10 @@ export default async function AppLayout({
5656

5757
return (
5858
<div
59-
className="theme-scope flex flex-col min-h-screen"
59+
className="theme-scope flex min-h-screen"
6060
style={{ backgroundColor: "var(--background)" }}
6161
>
62-
<Topbar user={user} />
62+
<Sidebar user={user} />
6363
<main
6464
className="flex-1 bg-canvas text-foreground"
6565
style={{
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// ABOUTME: Case-aware navigation section for sidebar
2+
// ABOUTME: Self-detects case context and renders case-specific navigation
3+
4+
"use client";
5+
6+
import { usePathname, useParams } from "next/navigation";
7+
import { Terminal, Network, FolderOpen, Clock, Globe } from "lucide-react";
8+
import { SidebarTabs } from "@/components/ui/sidebar-tabs";
9+
10+
interface CaseNavSectionProps {
11+
collapsed: boolean;
12+
}
13+
14+
const caseTabs = [
15+
{ title: "Command Center", icon: Terminal, href: "/command-center" },
16+
{ title: "Knowledge Graph", icon: Network, href: "/knowledge-graph" },
17+
{ title: "Evidence Library", icon: FolderOpen, href: "/library" },
18+
{ title: "Timeline", icon: Clock, href: "/timeline" },
19+
{ title: "Geospatial", icon: Globe, href: "/geospatial" },
20+
];
21+
22+
export function CaseNavSection({ collapsed }: CaseNavSectionProps) {
23+
const pathname = usePathname();
24+
const params = useParams();
25+
26+
// Detect if we're on a case route
27+
const caseId = params?.id as string | undefined;
28+
const isOnCaseRoute = pathname?.startsWith("/cases/") && caseId;
29+
30+
// Return null if not on a case route
31+
if (!isOnCaseRoute) {
32+
return null;
33+
}
34+
35+
// Get active tab from pathname
36+
const pathSegments = pathname.split("/");
37+
const currentSection =
38+
pathSegments[pathSegments.length - 1] || "command-center";
39+
const activeTab = `/${currentSection}`;
40+
const basePath = `/cases/${caseId}`;
41+
42+
return (
43+
<>
44+
{/* Section divider */}
45+
<div
46+
className="my-2 mx-2"
47+
style={{ borderTop: "1px solid var(--border)" }}
48+
/>
49+
50+
{/* Case tabs */}
51+
<div className="px-2">
52+
<SidebarTabs
53+
tabs={caseTabs}
54+
activeTab={activeTab}
55+
basePath={basePath}
56+
collapsed={collapsed}
57+
/>
58+
</div>
59+
</>
60+
);
61+
}

0 commit comments

Comments
 (0)