diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a8df07c..15eeb4c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,9 +2,9 @@ name: CI Testing on: push: - branches: [ main ] + branches: [ main, UTS ] pull_request: - branches: [ main ] + branches: [ main, UTS ] jobs: test: diff --git a/package-lock.json b/package-lock.json index fc98b1e..f69eb56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1904,25 +1904,24 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", "license": "BSD-3-Clause", "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "@protobufjs/aspromise": "^1.1.1" } }, "node_modules/@protobufjs/float": { @@ -1932,9 +1931,9 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", + "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/path": { @@ -1950,9 +1949,9 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", "license": "BSD-3-Clause" }, "node_modules/@rtsao/scc": { @@ -2502,9 +2501,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, "license": "MIT", "dependencies": { @@ -5793,9 +5792,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "funding": [ { "type": "github", @@ -6290,9 +6289,9 @@ } }, "node_modules/postcss": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", - "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "dev": true, "funding": [ { @@ -6310,7 +6309,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -6369,24 +6368,24 @@ } }, "node_modules/protobufjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", - "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.2.tgz", + "integrity": "sha512-N9EiLovGEQOJSPF26Ij7qUGvahfEnq0eeYZ02aigIedkmz1qZSwjnP9SBITHJuF/6MYbIW4HDN8zdYjsjqJKXQ==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", + "@protobufjs/inquire": "^1.1.2", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", - "long": "^5.0.0" + "long": "^5.3.2" }, "engines": { "node": ">=12.0.0" diff --git a/src/components/dashboard/BinCard.tsx b/src/components/dashboard/BinCard.tsx index 707ebb0..93d7438 100644 --- a/src/components/dashboard/BinCard.tsx +++ b/src/components/dashboard/BinCard.tsx @@ -40,21 +40,21 @@ export default function BinCard({ id, gedung, lantai, ruang, level, capacity, st return ( -
+
{/* Header: ID & Status Wifi */} -
-
-

+
+
+

{id}

-
+
- {gedung} - {lantai} {ruang ? `• ${ruang}` : ''} + {gedung} - {lantai} {ruang ? `• ${ruang}` : ''} +
-
-
+
{isOnline ? : } - + {theme.label}
@@ -63,27 +63,27 @@ export default function BinCard({ id, gedung, lantai, ruang, level, capacity, st {/* Progress Bar Level Isi */}
- Level Isi - + Level Isi + {isOnline ? `${level}%` : '--'}
-
+
100 ? 100 : level < 0 ? 0 : level) : 0}%` }} />
-
+
0L {capacity}L
{/* Footer Card */} -
+
Update: {getLastUpdateLabel(lastUpdate)} diff --git a/src/components/dashboard/StatCard.tsx b/src/components/dashboard/StatCard.tsx index 3209294..be2ac78 100644 --- a/src/components/dashboard/StatCard.tsx +++ b/src/components/dashboard/StatCard.tsx @@ -7,15 +7,15 @@ interface StatCardProps { export default function StatCard({ title, value, icon, textColor }: StatCardProps) { return ( -
+
- {title} + {title}
{icon}
-
- +
+ {value}
diff --git a/src/components/layout/PageContainer.tsx b/src/components/layout/PageContainer.tsx index 6d69d4f..baf034b 100644 --- a/src/components/layout/PageContainer.tsx +++ b/src/components/layout/PageContainer.tsx @@ -7,7 +7,7 @@ interface PageContainerProps { export default function PageContainer({ children, className = '' }: PageContainerProps) { return ( -
+
{children}
); diff --git a/src/components/layout/PageHeader.tsx b/src/components/layout/PageHeader.tsx index 8f4e3fd..7fc5766 100644 --- a/src/components/layout/PageHeader.tsx +++ b/src/components/layout/PageHeader.tsx @@ -8,12 +8,12 @@ interface PageHeaderProps { export default function PageHeader({ title, subtitle, className = '' }: PageHeaderProps) { return ( -
-

+
+

{title}

{subtitle && ( -

+

{subtitle}

)} diff --git a/src/components/layout/navbar/index.tsx b/src/components/layout/navbar/index.tsx index b56b8e5..6e9c36b 100644 --- a/src/components/layout/navbar/index.tsx +++ b/src/components/layout/navbar/index.tsx @@ -34,27 +34,33 @@ export default function Navbar({ onMenuClick }: NavbarProps) { }, []); return ( -
-
+
+
-
-

{userName}

-

{userRole}

+
+
{/* Search Bar Group */} -
+
{/* 2. Gunakan Conditional Rendering */} {!shouldHideSearch ? ( <> - + setSearchQuery(e.target.value)} - className="w-full rounded-full border-none bg-white py-2.5 pl-12 pr-4 text-sm shadow-soft outline-none transition-all focus:ring-2 focus:ring-emerald-500" + className="w-full rounded-full border-none bg-white py-1.5 pl-9 pr-2 text-[11px] shadow-soft outline-none transition-all focus:ring-2 focus:ring-emerald-500 sm:py-2.5 sm:pl-12 sm:pr-4 sm:text-sm" /> ) : ( /* Bisa dikosongkan atau ganti jadi Breadcrumb/Judul Kecil */ -
+
)}
{/* Sisi Kanan (Bell, Profile, dll) tetap muncul di semua page */} {/* Right Side Icons & Profile */} -
-
- -
- +
{/* User Profile */} -
+
-
-
-
+
+
- Logout + Logout
diff --git a/src/components/notifications/NotificationItem.tsx b/src/components/notifications/NotificationItem.tsx index b157c7b..c74ce72 100644 --- a/src/components/notifications/NotificationItem.tsx +++ b/src/components/notifications/NotificationItem.tsx @@ -53,19 +53,19 @@ export default function NotificationItem({ const IconComponent = styles.icon; return ( -
+
{/* Icon */} -
+
{/* Content */}
-

+

{notification.gedung} - {notification.lantai} - {notification.ruang}

-

{notification.description}

-

+

{notification.description}

+

⏱ {formatTime(notification.timestamp)}

@@ -74,12 +74,12 @@ export default function NotificationItem({ {notification.status === 'baru' ? ( ) : ( -
+
Dibaca
)} diff --git a/src/components/notifications/NotificationList.tsx b/src/components/notifications/NotificationList.tsx index a1d1ff2..45634fd 100644 --- a/src/components/notifications/NotificationList.tsx +++ b/src/components/notifications/NotificationList.tsx @@ -34,9 +34,9 @@ export default function NotificationList({ ]; return ( -
+
{/* Bagian Header yang diubah menggunakan PageHeader */} -
+
✓ Tandai Semua Dibaca @@ -53,12 +53,12 @@ export default function NotificationList({
{/* Filter Tabs - Tetap sesuai desain asli Anda */} -
+
{filterOptions.map((option) => ( -
diff --git a/src/views/dashboard/index.tsx b/src/views/dashboard/index.tsx index a2a0caa..118f119 100644 --- a/src/views/dashboard/index.tsx +++ b/src/views/dashboard/index.tsx @@ -54,7 +54,7 @@ export default function DashboardView() { /> {/* 1. StatCard */} -
+
} /> } /> } /> @@ -64,12 +64,12 @@ export default function DashboardView() { {/* 2. Banner Perhatian */} {(stats.full > 0 || stats.nearlyFull > 0) && ( -
-
+
+
Perhatian! {stats.full} tempat sampah penuh, {stats.nearlyFull} hampir penuh
-

Segera bersihkan untuk menjaga kebersihan kampus.

+

Segera bersihkan untuk menjaga kebersihan kampus.

)} @@ -105,24 +105,24 @@ export default function DashboardView() {
*/} {/* 4. Prioritas Pengambilan - Leaderboard */} -
-
-

Prioritas Pengambilan

+
+
+

Prioritas Pengambilan

Berdasarkan lamanya sampah penuh dan belum diambil

-
+
{(() => { const ranking = getPickupPriorityRanking(bins, historyRecords).slice(0, 10); if (!ranking || ranking.length === 0) return ( -
Tidak ada prioritas saat ini
+
Tidak ada prioritas saat ini
); return ranking.map((r, idx) => ( -
+
{/* Rank Badge */} -
ID: {r.binId}
{/* Capacity Bar */} -
+
Level Isi {r.capacity}% @@ -167,7 +167,7 @@ export default function DashboardView() { {/* Priority Label */}
-
Memuat log riwayat sampah...
; return ( -
-

Analitik Bin

+
+

Analitik Bin

-
+
-
+
setStartDate(e.target.value)} className="bg-transparent outline-none" /> sampai setEndDate(e.target.value)} className="bg-transparent outline-none" />
-
-
-
+
+
+

Rata-rata Kapasitas

-

{chartData.length > 0 ? chartData[chartData.length - 1].avg : 0}%

-

Berdasarkan data filter terakhir

+

{chartData.length > 0 ? chartData[chartData.length - 1].avg : 0}%

+

Berdasarkan data filter terakhir

-
-
+
+

Total Aktivitas

-

{filteredRecords.length}

-

Jumlah log yang tercatat

+

{filteredRecords.length}

+

Jumlah log yang tercatat

-
-
+
+

Log Terbaru

-

{latestRecord ? latestRecord.location : '-'}

-

{latestRecord ? `BIN: ${shortenId(latestRecord.binId)}` : 'BIN: -'}

+

{latestRecord ? latestRecord.location : '-'}

+

{latestRecord ? `BIN: ${shortenId(latestRecord.binId)}` : 'BIN: -'}

-
-
+
+

Jam Sibuk

-

{peakHour ? peakHour.label : '-'}

-

{peakHour ? `${peakHour.count} log pada jam ini` : 'Belum ada data log'}

+

{peakHour ? peakHour.label : '-'}

+

{peakHour ? `${peakHour.count} log pada jam ini` : 'Belum ada data log'}

-
+

Top Lokasi Sering Penuh

{topFullLocations.length > 0 ? (
{topFullLocations.map((item, index) => ( -
-

+

+

{index + 1}. {item.location}

-

{item.count}x

+

{item.count}x

))}
) : ( -

Belum ada lokasi dengan kapasitas {FULL_THRESHOLD}% ke atas.

+

Belum ada lokasi dengan kapasitas {FULL_THRESHOLD}% ke atas.

)}
-
-

Tren Kapasitas Sampah

+
+

Tren Kapasitas Sampah

-
-

Tentang Dashboard Analitik

-

+

+

Tentang Dashboard Analitik

+

Dashboard ini menampilkan analisis real-time pengisian sampah di seluruh lokasi. Gunakan filter lokasi dan tanggal untuk melihat metrik spesifik. Pantau Jam Sibuk untuk mengetahui periode puncak pengumpulan, dan Top Lokasi Sering Penuh untuk prioritas pengangkutan. Grafik tren membantu merencanakan jadwal maintenance yang lebih efisien.

diff --git a/src/views/manage/index.tsx b/src/views/manage/index.tsx index fd71a92..66ebcf8 100644 --- a/src/views/manage/index.tsx +++ b/src/views/manage/index.tsx @@ -49,14 +49,14 @@ export default function ManageView() { return ( {/* Header Section menggunakan PageHeader */} -
+
@@ -130,7 +130,7 @@ export default function ManageView() {
-
+
{filteredBins.length === 0 ? (
Data bin tidak ditemukan. @@ -141,33 +141,33 @@ export default function ManageView() { const isOnline = bin.status === 'on'; return ( -
-
+
+

{bin.id}

-

{bin.gedung}

-

{bin.lantai} · {bin.ruang}

+

{bin.gedung}

+

{bin.lantai} · {bin.ruang}

- {bin.status || 'OFF'}
-
-
-

Kapasitas

+
+
+

Kapasitas

{bin.capacity}L

-
-

Level

+
+

Level

{level}%

-
- + = 90 ? 'bg-[#FF3B30]' : level >= 70 ? 'bg-[#FFCC00]' : level > 0 ? 'bg-[#00D26A]' : 'bg-gray-400' @@ -176,10 +176,10 @@ export default function ManageView() {
- -
diff --git a/src/views/monitoring/ActionView.tsx b/src/views/monitoring/ActionView.tsx index 8b152d9..f47ce93 100644 --- a/src/views/monitoring/ActionView.tsx +++ b/src/views/monitoring/ActionView.tsx @@ -137,7 +137,7 @@ export default function ActionView({ id }: { id: string }) { @@ -147,63 +147,63 @@ export default function ActionView({ id }: { id: string }) { subtitle="Verifikasi dilakukan otomatis melalui pencocokan data sensor IoT Trashware." /> -
-
+
+
{/* Section: Petugas */} -
-
+
+
-

Petugas Penjemputan

-

{session?.user?.name || 'Loading...'}

+

Petugas Penjemputan

+

{session?.user?.name || 'Loading...'}

{/* Section: Level Comparison */} -
-
-

+

+
+

Level Awal

-

+

{initialLevel !== null ? `${initialLevel}%` : '--%'}

-
-

+

+

Real-time

-

+

{binData ? `${binData.level}%` : '--%'}

{/* Section: Lokasi */} -
+
-

Titik Unit

+

Titik Unit

{binData ? `${binData.gedung}, ${binData.lantai}` : 'Memuat lokasi...'}

-

+

{binData?.ruang || 'Mencari detail ruang...'}

{/* Feedback Messages */} {message && ( -
{message.type === 'success' ? : } -

{message.text}

+

{message.text}

)} @@ -211,7 +211,7 @@ export default function ActionView({ id }: { id: string }) {
diff --git a/src/views/monitoring/index.tsx b/src/views/monitoring/index.tsx index 95f1dee..30e5d0d 100644 --- a/src/views/monitoring/index.tsx +++ b/src/views/monitoring/index.tsx @@ -63,16 +63,16 @@ export default function MonitoringView() { /> {/* Filter Bar */} -
+
- Filter Tampilan: + Filter Tampilan: