Skip to content

Commit 70fda0a

Browse files
committed
Change page structure
1 parent 930ffc6 commit 70fda0a

9 files changed

Lines changed: 1035 additions & 544 deletions

File tree

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
"use client";
2+
3+
import { useCallback, useEffect, useState } from "react";
4+
import { useParams, useRouter } from "next/navigation";
5+
import Image from "next/image";
6+
import InspectionDetailsPanel from "@/components/InspectionDetailsPanel";
7+
import LoadingScreen from "@/components/LoadingScreen";
8+
import ThemeToggle from "@/components/ThemeToggle";
9+
import Logo from "@/components/Logo";
10+
import { useInspections } from "@/context/InspectionsContext";
11+
import { Inspection } from "@/types/inspection";
12+
13+
const InspectionDetailPage = () => {
14+
const params = useParams<{ id: string }>();
15+
const router = useRouter();
16+
const rawId = Array.isArray(params?.id) ? params.id[0] : params?.id;
17+
18+
const { inspections, fetchInspectionById } = useInspections();
19+
20+
const [inspection, setInspection] = useState<Inspection | null>(null);
21+
const [username, setUsername] = useState<string | null>(null);
22+
const [profileSrc, setProfileSrc] = useState<string>("/avatar.png");
23+
const [isLoading, setIsLoading] = useState(true);
24+
const [loadingMessage, setLoadingMessage] = useState<string>("Loading inspection...");
25+
const [panelOverlay, setPanelOverlay] = useState<{ show: boolean; message?: string }>({ show: false });
26+
const [notFound, setNotFound] = useState(false);
27+
28+
const handlePanelLoadingChange = useCallback((state: { show: boolean; message?: string }) => {
29+
setPanelOverlay((prev) => {
30+
if (prev.show === state.show && prev.message === state.message) return prev;
31+
return { show: state.show, message: state.message };
32+
});
33+
}, []);
34+
35+
useEffect(() => {
36+
try {
37+
const loggedIn =
38+
typeof window !== "undefined" &&
39+
localStorage.getItem("isLoggedIn") === "true";
40+
if (!loggedIn) {
41+
router.replace("/");
42+
}
43+
if (typeof window !== "undefined") {
44+
setUsername(localStorage.getItem("username"));
45+
const stored = localStorage.getItem("userImage");
46+
if (stored && stored.length > 0) {
47+
setProfileSrc(stored);
48+
}
49+
}
50+
} catch {
51+
router.replace("/");
52+
}
53+
}, [router]);
54+
55+
useEffect(() => {
56+
let active = true;
57+
const load = async () => {
58+
if (!rawId) {
59+
if (active) {
60+
setInspection(null);
61+
setNotFound(true);
62+
setIsLoading(false);
63+
}
64+
return;
65+
}
66+
setIsLoading(true);
67+
setLoadingMessage("Loading inspection...");
68+
setNotFound(false);
69+
const existing = inspections.find((candidate) => {
70+
if (candidate.id && candidate.id === rawId) return true;
71+
return candidate.inspectionNumber === rawId;
72+
});
73+
let resolved: Inspection | null = existing ?? null;
74+
const targetId = existing?.id ?? rawId;
75+
try {
76+
const fetched = await fetchInspectionById(targetId);
77+
if (fetched) {
78+
resolved = fetched;
79+
}
80+
} catch {
81+
// ignore fetch failures
82+
}
83+
if (!resolved) {
84+
if (active) {
85+
setInspection(null);
86+
setNotFound(true);
87+
}
88+
} else if (active) {
89+
setInspection(resolved);
90+
}
91+
if (active) {
92+
setIsLoading(false);
93+
}
94+
};
95+
void load();
96+
return () => {
97+
active = false;
98+
};
99+
}, [rawId, inspections, fetchInspectionById]);
100+
101+
102+
if (notFound && !isLoading) {
103+
return (
104+
<div className="p-6 flex flex-col gap-4">
105+
<div className="flex items-center gap-3">
106+
<Logo width={36} height={36} />
107+
<span
108+
className="font-semibold text-2xl tracking-wider"
109+
style={{ fontFamily: "var(--font-orbitron)" }}
110+
>
111+
APEX-GRID
112+
</span>
113+
</div>
114+
<p className="text-lg font-semibold text-gray-700">Inspection not found.</p>
115+
<button
116+
onClick={() => router.push("/inspections")}
117+
className="custombutton inline-flex items-center px-4 py-2 rounded"
118+
>
119+
Back to inspections
120+
</button>
121+
</div>
122+
);
123+
}
124+
125+
return (
126+
<>
127+
<div className="p-4 pb-24">
128+
<div className="flex items-start justify-between mb-4">
129+
<div className="flex flex-col items-start gap-2">
130+
<div className="flex items-center gap-3">
131+
<Logo width={36} height={36} />
132+
<span
133+
className="font-semibold text-2xl tracking-wider"
134+
style={{ fontFamily: "var(--font-orbitron)" }}
135+
>
136+
APEX-GRID
137+
</span>
138+
</div>
139+
<div className="flex items-center gap-2">
140+
<button
141+
onClick={() => router.back()}
142+
className="text-sm text-gray-500 hover:text-gray-700"
143+
>
144+
← Back
145+
</button>
146+
<button
147+
onClick={() => router.push("/inspections")}
148+
className="text-sm text-gray-500 hover:text-gray-700"
149+
>
150+
View all inspections
151+
</button>
152+
</div>
153+
{inspection && (
154+
<h1 className="text-2xl font-bold">
155+
Inspection {inspection.inspectionNumber}
156+
</h1>
157+
)}
158+
</div>
159+
<div className="flex flex-col items-end gap-2">
160+
<div className="flex items-center gap-3">
161+
<ThemeToggle />
162+
<span className="text-sm text-gray-700">
163+
Logged in as:{" "}
164+
<span className="font-medium">{username || "unknown"}</span>
165+
</span>
166+
<button
167+
onClick={() => router.push("/profile")}
168+
className="w-8 h-8 rounded-full overflow-hidden bg-gray-200 border hover:ring-2 hover:ring-gray-300"
169+
title="Profile Settings"
170+
>
171+
<Image
172+
src={profileSrc}
173+
alt="Profile"
174+
width={32}
175+
height={32}
176+
loading="lazy"
177+
unoptimized={profileSrc.startsWith("data:")}
178+
className="w-full h-full object-cover"
179+
/>
180+
</button>
181+
<button
182+
onClick={() => {
183+
try {
184+
localStorage.removeItem("isLoggedIn");
185+
localStorage.removeItem("username");
186+
localStorage.removeItem("userImage");
187+
} catch {}
188+
router.replace("/");
189+
}}
190+
className="inline-flex items-center rounded-md px-4 py-2 custombutton"
191+
>
192+
Log out
193+
</button>
194+
</div>
195+
</div>
196+
</div>
197+
198+
{inspection && (
199+
<InspectionDetailsPanel
200+
inspection={inspection}
201+
onClose={() => router.back()}
202+
onLoadingChange={handlePanelLoadingChange}
203+
/>
204+
)}
205+
</div>
206+
<LoadingScreen
207+
show={isLoading || panelOverlay.show}
208+
message={
209+
panelOverlay.show
210+
? panelOverlay.message || "Working..."
211+
: loadingMessage || "Loading inspection..."
212+
}
213+
/>
214+
</>
215+
);
216+
};
217+
218+
export default InspectionDetailPage;

0 commit comments

Comments
 (0)