Skip to content

Commit 1e15b23

Browse files
committed
Merge branch 'develop'
2 parents 89784b4 + a5ba053 commit 1e15b23

File tree

106 files changed

+12707
-5240
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+12707
-5240
lines changed

aods_dev_v3.sql

Lines changed: 1177 additions & 0 deletions
Large diffs are not rendered by default.

client/my-app/src/app/(plain)/login/page.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { useState } from 'react';
3+
import { useEffect, useState } from 'react';
44
import { Button } from "@/components/ui/button";
55
import {
66
Card, CardContent, CardFooter, CardHeader,
@@ -31,6 +31,8 @@ export default function LoginPage() {
3131
const data = await res.json().catch(() => ({}));
3232
if (!res.ok) throw new Error(data?.message || 'Login failed');
3333

34+
localStorage.setItem("access_token", data.token);
35+
3436
window.location.href = '/cameras';
3537
} catch (e: any) {
3638
setErr(e.message ?? 'Login failed');
@@ -39,6 +41,8 @@ export default function LoginPage() {
3941
}
4042
}
4143

44+
console.log('Token: ', localStorage.getItem("access_token"));
45+
4246
return (
4347
<main
4448
className="
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Alert } from "@/app/models/alerts.model";
2+
import AlertDetails from '@/app/components/Alerts/Details/AlertDetails'
3+
4+
const base = process.env.NEXT_PUBLIC_APP_URL!;
5+
6+
export default async function Page({ params }: { params: Promise<{ alr_id: string }> }) {
7+
const { alr_id } = await params
8+
9+
const res = await fetch(`${base}/api/alerts/${alr_id}`, { cache: "no-store" });
10+
if (!res.ok) {
11+
throw new Error("Failed to load alert");
12+
}
13+
14+
const json = await res.json();
15+
const alert: Alert = json.data;
16+
17+
return (
18+
<div className="rounded-lg bg-[var(--color-white)] shadow-md p-6">
19+
<div className="grid grid-cols-[repeat(auto-fit,minmax(320px,1fr))] gap-6">
20+
<AlertDetails alert={alert} />
21+
</div>
22+
</div>
23+
)
24+
}
Lines changed: 29 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,50 @@
1-
import AlertTable, { type Alert } from "../../components/Alerts/AlertTable";
2-
import * as StatusCard from "../../components/Utilities/StatusCard";
1+
// app/(dashboard)/alerts/page.tsx
32
import { Separator } from "@/components/ui/separator";
4-
import AlertsClient from "@/app/components/Alerts/AlertsClient";
5-
import DistributionChart from "@/app/components/Alerts/Chart/Distribution";
6-
import Trend from "@/app/components/Alerts/Chart/Trends";
7-
8-
const base = process.env.NEXT_PUBLIC_APP_URL!;
9-
10-
export default async function AlertsPage() {
11-
const res = await fetch(`${base}/api/alerts`, {
12-
cache: "no-store",
13-
credentials: "include",
14-
method: "GET",
15-
});
16-
if (!res.ok) throw new Error(`Failed to load alerts (${res.status})`);
17-
const alerts: Alert[] = await res.json();
3+
import AlertView from "@/app/components/Alerts/AlertsView";
4+
import SearchAlertsInput from "@/app/components/Alerts/SearchAlertsInput";
5+
import AlertFilters from "@/app/components/Alerts/AlertFilters";
6+
import RecentAlerts from "@/app/components/Alerts/RecentAlerts";
187

8+
export default function AlertsPage() {
199
return (
2010
<div className="space-y-6">
21-
<StatusCard.DashboardSummaryAlertSection />
11+
{/* === Search & Filter (บนสุด) === */}
2212
<div className="rounded-lg bg-[var(--color-white)] shadow-md p-6">
23-
<div className="flex flex-col sm:flex-row sm:flex-wrap sm:items-start gap-3 justify-center mb-3">
24-
<label
25-
htmlFor="AlertManagement"
26-
className="min-w-0 flex-1 font-bold text-lg text-[var(--color-primary)]"
27-
>
28-
Alert Management
29-
</label>
30-
</div>
31-
<Separator className="bg-[var(--color-primary-bg)] my-3" />
32-
33-
<AlertsClient alerts={alerts} />
34-
</div>
35-
36-
{/* ส่วนอื่นๆ ในหน้า เดิม */}
37-
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
38-
<div className="rounded-lg bg-[var(--color-white)] shadow-md p-6 ">
39-
<div className="flex flex-wrap items-start gap-3 justify-center mb-3">
40-
<label
41-
htmlFor="AlertTrends"
42-
className="min-w-0 flex-1 font-bold text-lg text-[var(--color-primary)]"
43-
>
44-
Alert Trends
45-
</label>
13+
<label
14+
htmlFor="alertSearchFilter"
15+
className="min-w-0 flex-1 font-bold text-lg text-[var(--color-primary)]"
16+
>
17+
Search & Filter
18+
</label>
19+
<div className="grid gap-2 items-start sm:gap-3 mt-3">
20+
<div className="w-full">
21+
<SearchAlertsInput />
4622
</div>
47-
<Separator className="bg-[var(--color-primary-bg)] mb-3" />
48-
49-
<Trend />
50-
</div>
51-
<div className="rounded-lg bg-[var(--color-white)] shadow-md p-6 ">
52-
<div className="flex flex-wrap items-start gap-3 justify-center mb-3">
53-
<label
54-
htmlFor="AlertDistribution"
55-
className="min-w-0 flex-1 font-bold text-lg text-[var(--color-primary)]"
56-
>
57-
Alert Distribution by Event Type
58-
</label>
23+
<div className="w-full">
24+
{/* ✅ ไม่ต้อง Loader แล้ว */}
25+
<AlertFilters />
5926
</div>
60-
<Separator className="bg-[var(--color-primary-bg)] mb-3" />
61-
62-
<DistributionChart />
6327
</div>
6428
</div>
6529

30+
{/* === Summary + Table + Refresh === */}
31+
<AlertView />
32+
6633
<div className="rounded-lg bg-[var(--color-white)] shadow-md p-6 ">
6734
<div className="flex flex-wrap items-start gap-3 justify-center mb-3">
6835
<label
69-
htmlFor="cameraName"
36+
htmlFor="recentCameraActivity"
7037
className="min-w-0 flex-1 font-bold text-lg text-[var(--color-primary)]"
7138
>
72-
Recent Camera Activity
39+
Recent Camera Alert
7340
</label>
7441
</div>
7542
<Separator className="bg-[var(--color-primary-bg)]" />
76-
43+
{/* TODO: ใส่คอมโพเนนต์ Recent ของคุณต่อท้ายได้เลย */}
44+
<div className="mt-3">
45+
<RecentAlerts />
46+
</div>
7747
</div>
7848
</div>
7949
);
80-
}
50+
}

client/my-app/src/app/(with-layout)/cameras/[cam_id]/details/page.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Camera } from "@/app/Models/cameras.model";
1+
import { Camera } from "@/app/models/cameras.model";
22
import CameraDetails from '@/app/components/Cameras/Details/CameraDetails'
33

44
const base = process.env.NEXT_PUBLIC_APP_URL!;
@@ -11,7 +11,8 @@ export default async function Page({ params }: { params: Promise<{ cam_id: strin
1111
throw new Error("Failed to load cameras");
1212
}
1313

14-
const camera: Camera = await res.json();
14+
const json = await res.json();
15+
const camera: Camera = json.data[0];
1516

1617
return (
1718
<div className="rounded-lg bg-[var(--color-white)] shadow-md p-6">

client/my-app/src/app/(with-layout)/cameras/[cam_id]/page.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
11
import FullScreenView from "@/app/components/Cameras/FullScreenView";
2-
import { Camera } from "@/app/Models/cameras.model";
2+
import { Camera } from "@/app/models/cameras.model";
33

44
const base = process.env.NEXT_PUBLIC_APP_URL!;
55

66
export default async function Page({ params }: { params: Promise<{ cam_id: string }> }) {
77
const { cam_id } = await params
88

9-
const res = await fetch(`${base}/api/cameras/${cam_id}`, { cache: "no-store" });
9+
const res = await fetch(`${base}/api/cameras/${cam_id}`, {
10+
method: "GET",
11+
headers: {
12+
Authorization: `Bearer ${process.env.NEXT_PUBLIC_TOKEN}`,
13+
"Content-Type": "application/json",
14+
},
15+
cache: "no-store",
16+
});
1017
if (!res.ok) {
1118
throw new Error("Failed to load cameras");
1219
}
1320

14-
const camera: Camera = await res.json();
21+
const json = await res.json();
22+
const camera: Camera = json.data;
23+
24+
console.log(camera);
1525

1626
return (
1727
<div className="rounded-lg bg-[var(--color-white)] shadow-md p-6">

client/my-app/src/app/(with-layout)/cameras/page.tsx

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
// app/(with-layout)/cameras/page.tsx
2-
import * as StatusCard from "../../components/Utilities/StatusCard";
32
import CreateEventForm from "@/app/components/Forms/CreateEventForm";
43
import { Separator } from "@/components/ui/separator";
5-
import ToggleViewButton from "@/app/components/Cameras/ToggleViewButton";
64
import CameraView from "@/app/components/Cameras/CameraView";
75
import SearchCamerasInput from "@/app/components/Cameras/SearchCamerasInput";
86
import CameraFilters from "@/app/components/Cameras/CameraFilters";
9-
import CreateCameraForm from "@/app/components/Forms/CreateCameraForm";
107
import EventGrid from "@/app/components/Events/EventCardGrid";
118

129
type ViewMode = "grid" | "list";
@@ -15,13 +12,10 @@ type SP = Record<string, string | string[] | undefined>;
1512
export default async function CamerasPage({
1613
searchParams,
1714
}: {
18-
/** Next.js 15: searchParams เป็น Promise แล้ว */
1915
searchParams: Promise<SP>;
2016
}) {
21-
// ✅ ต้อง await ก่อนใช้งาน
2217
const sp = await searchParams;
2318

24-
// helper ดึงค่าแบบ string เดี่ยว (กันกรณีเป็น array)
2519
const pick = (key: keyof SP) =>
2620
typeof sp?.[key] === "string" ? (sp[key] as string) : undefined;
2721

@@ -35,25 +29,14 @@ export default async function CamerasPage({
3529

3630
return (
3731
<div className="space-y-6">
38-
<StatusCard.DashboardSummaryCameraSection />
39-
32+
{/* Search & Filter */}
4033
<div className="rounded-lg bg-[var(--color-white)] shadow-md p-6">
41-
<div className="flex flex-col sm:flex-row sm:flex-wrap sm:items-start gap-3 justify-center mb-3">
42-
<label
43-
htmlFor="cameraName"
44-
className="min-w-0 flex-1 font-bold text-lg text-[var(--color-primary)]"
45-
>
46-
Camera Management
47-
</label>
48-
49-
<div className="flex gap-3">
50-
<ToggleViewButton />
51-
<CreateCameraForm />
52-
</div>
53-
</div>
54-
55-
<Separator className="bg-[var(--color-primary-bg)] my-3" />
56-
34+
<label
35+
htmlFor="cameraName"
36+
className="min-w-0 flex-1 font-bold text-lg text-[var(--color-primary)]"
37+
>
38+
Search & Filter
39+
</label>
5740
<div className="grid gap-2 items-start sm:gap-3 mt-3">
5841
<div className="w-full">
5942
<SearchCamerasInput />
@@ -62,16 +45,18 @@ export default async function CamerasPage({
6245
<CameraFilters />
6346
</div>
6447
</div>
65-
66-
<CameraView
67-
viewMode={viewMode}
68-
search={q}
69-
status={status}
70-
location={location}
71-
type={type}
72-
/>
7348
</div>
7449

50+
{/* Summary + Camera Management + List/Grid (ทั้งหมดอยู่ใน CameraView แล้ว) */}
51+
<CameraView
52+
viewMode={viewMode}
53+
search={q}
54+
status={status}
55+
location={location}
56+
type={type}
57+
/>
58+
59+
{/* Event Detection Configuration */}
7560
<div className="rounded-lg bg-[var(--color-white)] shadow-md p-6">
7661
<div className="flex flex-wrap items-start gap-3 justify-center mb-3">
7762
<label
@@ -83,7 +68,6 @@ export default async function CamerasPage({
8368
<CreateEventForm />
8469
</div>
8570
<Separator className="bg-[var(--color-primary-bg)] mb-3" />
86-
8771
<EventGrid />
8872
</div>
8973
</div>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import HistoryMenu from "@/app/components/History/HistoryMenu";
2+
3+
export default function History() {
4+
return (
5+
<div className="rounded-lg bg-[var(--color-white)] shadow-md p-6">
6+
<HistoryMenu />
7+
</div>
8+
);
9+
}

client/my-app/src/app/(with-layout)/layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ export default function RootLayout({ children }: { children: React.ReactNode })
1616
<div className="flex min-h-[calc(100vh-3.5rem)] md:min-h-[calc(100vh-4rem)]">
1717
<Sidebar />
1818
<main className="flex-1 min-w-0 p-4 md:p-6 space-y-6">
19-
<Title/>
20-
{children}
19+
<Title />
20+
{children}
2121
</main>
2222
</div>
2323
</UIProvider>
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
import SettingsMenu from "@/app/components/Settings/SettingsMenu";
2+
13
export default function Settings() {
24
return (
35
<div className="rounded-lg bg-[var(--color-white)] shadow-md p-6">
4-
<div className="grid grid-cols-[repeat(auto-fit,minmax(320px,1fr))] gap-6">
5-
<h1 className="text-2xl font-bold">Settings</h1>
6-
</div>
6+
<SettingsMenu />
77
</div>
88
);
99
}

0 commit comments

Comments
 (0)