refactor: redesign table module
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription, CardFooter } from "@/components/ui/card";
|
||||
import {
|
||||
AlertCircle,
|
||||
Activity,
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Zap,
|
||||
PencilLine,
|
||||
CheckCircle2,
|
||||
Settings2,
|
||||
} from "lucide-react";
|
||||
import { StatsCard } from "@/components/dashboard/stats-card";
|
||||
import { CompactProjectSelector } from "@/components/dashboard/compact-project-selector";
|
||||
@@ -26,9 +27,9 @@ import {
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Filter } from "lucide-react";
|
||||
import { DashboardSkeleton } from "@/components/dashboard/dashboard-skeleton"
|
||||
import { PoweredBy } from "@/components/powered-by";
|
||||
import { toast } from "sonner";
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL;
|
||||
|
||||
@@ -207,14 +208,12 @@ export default function DashboardPage() {
|
||||
totalRoadDamage: 0,
|
||||
locationData: [],
|
||||
});
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Load projects on mount
|
||||
useEffect(() => {
|
||||
const loadProjects = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
const projectsData = await fetchProjects();
|
||||
setProjects(projectsData);
|
||||
|
||||
@@ -223,9 +222,9 @@ export default function DashboardPage() {
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to load projects:", err);
|
||||
setError(
|
||||
"Failed to load projects. Please check if the backend is running.",
|
||||
);
|
||||
toast.error("Failed to load projects", {
|
||||
description: "Please check if the backend is running."
|
||||
});
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
@@ -274,7 +273,6 @@ export default function DashboardPage() {
|
||||
const loadProjectSummary = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await fetch(
|
||||
`${API_URL}/summary/projects/${selectedProjectId}`,
|
||||
@@ -294,7 +292,7 @@ export default function DashboardPage() {
|
||||
setProjectSummary(summary);
|
||||
} catch (err) {
|
||||
console.error("Failed to load project summary:", err);
|
||||
setError("Failed to load project summary.");
|
||||
toast.error("Failed to load project summary");
|
||||
setProjectSummary(null);
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
@@ -422,10 +420,10 @@ export default function DashboardPage() {
|
||||
}, [projectSummary, selectedPackageId, selectedLocationId]);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen text-gray-900 dark:text-gray-100">
|
||||
<>
|
||||
{/* Main Content */}
|
||||
<main className="min-h-screen">
|
||||
<div className="p-4 px-8 max-w-340 mx-auto">
|
||||
<main>
|
||||
<div>
|
||||
{/* Header */}
|
||||
<div className="mb-8 flex items-center justify-between">
|
||||
<PageHeader
|
||||
@@ -437,14 +435,14 @@ export default function DashboardPage() {
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="outline" className="gap-2 font-semibold">
|
||||
<Filter className="h-4 w-4" />
|
||||
<Settings2 className="h-4 w-4" />
|
||||
<span>Filters</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[320px] p-0 overflow-hidden" align="end">
|
||||
<div className="p-4 border-b bg-muted/30">
|
||||
<h3 className="font-bold text-sm flex items-center gap-2 text-foreground">
|
||||
<Filter className="h-4 w-4 text-primary" />
|
||||
<Settings2 className="h-4 w-4 text-primary" />
|
||||
Filter Analysis
|
||||
</h3>
|
||||
<p className="text-xs mt-1 text-muted-foreground">Refine your view by project, package, or location</p>
|
||||
@@ -467,14 +465,6 @@ export default function DashboardPage() {
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
{/* Error Display */}
|
||||
{error && (
|
||||
<div className="mb-4 flex items-center gap-2 p-3 rounded-xl bg-red-50 dark:bg-red-950/30 border border-red-200 dark:border-red-800 text-red-600 dark:text-red-400 text-sm">
|
||||
<AlertCircle className="h-4 w-4 shrink-0" />
|
||||
<p>{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isLoading && !projectSummary ? (
|
||||
<DashboardSkeleton />
|
||||
) : (
|
||||
@@ -522,18 +512,12 @@ export default function DashboardPage() {
|
||||
{/* Charts Row - Side by Side */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-4">
|
||||
{/* Left Chart - Detection Distribution */}
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base font-bold flex items-center gap-2">
|
||||
<div className="p-2 rounded-md bg-secondary text-secondary-foreground">
|
||||
<BarChart3 className="h-5 w-5" />
|
||||
</div>
|
||||
<span className="text-xl">
|
||||
Detection Distribution
|
||||
</span>
|
||||
</CardTitle>
|
||||
<Card className="flex flex-col">
|
||||
<CardHeader className="items-center pb-0">
|
||||
<CardTitle>Detection Distribution</CardTitle>
|
||||
<CardDescription>Breakdown of all detected road conditions</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-4">
|
||||
<CardContent className="flex-1 pb-0">
|
||||
<DetectionDonutChart
|
||||
defectedSignboard={stats.totalDefectedSignboard}
|
||||
pothole={stats.totalPothole}
|
||||
@@ -542,19 +526,20 @@ export default function DashboardPage() {
|
||||
goodSignboard={stats.totalGoodSignboard}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter className="flex-col gap-2 text-sm">
|
||||
<div className="leading-none text-muted-foreground">
|
||||
Total detections: {stats.totalRoadDamage + stats.totalGoodSignboard}
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
{/* Right Chart - Location Bar Chart */}
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base font-bold flex items-center gap-2">
|
||||
<div className="p-2 rounded-md bg-secondary text-secondary-foreground">
|
||||
<MapPinIcon className="h-5 w-5" />
|
||||
</div>
|
||||
<span className="text-xl">Detections by Location</span>
|
||||
</CardTitle>
|
||||
{/* Right Chart - Detections by Location */}
|
||||
<Card className="flex flex-col">
|
||||
<CardHeader className="items-center pb-0">
|
||||
<CardTitle>Detections by Location</CardTitle>
|
||||
<CardDescription>Frequency of road issues across different map segments</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-4">
|
||||
<CardContent className="flex-1 pb-0">
|
||||
<LocationBarChart
|
||||
data={stats.locationData.map((loc) => ({
|
||||
name: loc.name,
|
||||
@@ -562,11 +547,15 @@ export default function DashboardPage() {
|
||||
pothole: loc.pothole,
|
||||
road_crack: loc.road_crack,
|
||||
damaged_road_marking: loc.damaged_road_marking,
|
||||
good_sign_board: loc.good_sign_board,
|
||||
total: loc.total,
|
||||
}))}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter className="flex-col gap-2 text-sm">
|
||||
<div className="leading-none text-muted-foreground">
|
||||
Analysis based on latest processed sequence
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@@ -585,6 +574,6 @@ export default function DashboardPage() {
|
||||
<PoweredBy />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user