docs & style: upgrade README to production-grade detail and polish mobile responsive UX layouts
Browse files- README.md +117 -81
- frontend/app.js +1 -3
- frontend/style.css +61 -17
README.md
CHANGED
|
@@ -9,81 +9,99 @@ pinned: false
|
|
| 9 |
---
|
| 10 |
|
| 11 |
# 🚛 Aeterna AI: Next-Gen Waste Intelligence Platform
|
| 12 |
-
**Platform Sistem Peringatan Dini & Peramalan Sampah Real-Time DKI Jakarta**
|
| 13 |
|
| 14 |
-
|
| 15 |
|
| 16 |
-
|
| 17 |
|
| 18 |
-
--
|
|
|
|
|
|
|
| 19 |
|
| 20 |
> [!NOTE]
|
| 21 |
-
> **📖 DOKUMENTASI SISTEM BACKEND**:
|
| 22 |
-
> Untuk rincian mendalam mengenai
|
| 23 |
|
| 24 |
---
|
| 25 |
|
| 26 |
## 🌟 Fitur Unggulan (Key Features)
|
| 27 |
|
| 28 |
-
1.
|
| 29 |
-
2.
|
| 30 |
-
3.
|
| 31 |
-
4.
|
| 32 |
-
5.
|
| 33 |
|
| 34 |
---
|
| 35 |
|
| 36 |
-
##
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
-
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
| Metrik Evaluasi | Model Baseline | Model Upgraded (Aeterna AI) | Status Performa |
|
| 43 |
| :--- | :---: | :---: | :--- |
|
| 44 |
-
| **Mean Absolute Error (MAE)** | `149.13 Ton` | **`132.29 Ton`** |
|
| 45 |
-
| **Root Mean Squared Error (RMSE)** | `188.46 Ton` | **`165.46 Ton`** |
|
| 46 |
-
| **R-Squared ($R^2$ Score)** | `76.02%` | **`81.51%`** |
|
| 47 |
| **Mean Absolute Percentage Error (MAPE)** | `1.78%` | **`1.59%`** | **Sangat Akurat (< 10%) (⬇️)** |
|
| 48 |
|
| 49 |
-
### Hyperparameter Terbaik (GBR):
|
| 50 |
-
* `n_estimators` (Pohon keputusan): **100**
|
| 51 |
-
* `learning_rate`: **0.03**
|
| 52 |
-
* `max_depth`: **3**
|
| 53 |
-
* `subsample`: **0.9**
|
| 54 |
-
|
| 55 |
---
|
| 56 |
|
| 57 |
-
##
|
| 58 |
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
│
|
| 67 |
-
├── vercel.json (Konfigurasi Vercel root level)
|
| 68 |
-
├── app.py (Python FastAPI Backend)
|
| 69 |
-
├── train.py (Skrip training GBR & GridSearchCV)
|
| 70 |
-
├── Dockerfile (Hugging Face Docker deployment)
|
| 71 |
-
├── requirements.txt (Python library dependencies)
|
| 72 |
-
├── model_sampah_advanced.pkl (Binary Model GBR)
|
| 73 |
-
├── latest_waste_news.json (Database Berita Rill)
|
| 74 |
-
└── event_jakarta_2026.txt (Jadwal Event Jakarta 2026)
|
| 75 |
-
```
|
| 76 |
|
| 77 |
---
|
| 78 |
|
| 79 |
-
## 📡
|
| 80 |
|
| 81 |
Semua endpoint didukung dengan dokumentasi interaktif Swagger UI di `/docs`.
|
| 82 |
|
| 83 |
### 1. Predict Waste Volume (Forecasting)
|
| 84 |
* **Method**: `POST`
|
| 85 |
* **Endpoint**: `/api/v1/predict`
|
| 86 |
-
* **Request
|
| 87 |
```json
|
| 88 |
{
|
| 89 |
"forecast_days": 7,
|
|
@@ -94,72 +112,90 @@ Semua endpoint didukung dengan dokumentasi interaktif Swagger UI di `/docs`.
|
|
| 94 |
"granularity": "daily"
|
| 95 |
}
|
| 96 |
```
|
| 97 |
-
* **Response
|
| 98 |
```json
|
| 99 |
{
|
| 100 |
"status": "success",
|
| 101 |
-
"confidence_score": 0.
|
| 102 |
"message": "Normal conditions.",
|
| 103 |
"data": {
|
| 104 |
"prediction_results": [
|
| 105 |
{
|
| 106 |
-
"date": "2026-07-
|
| 107 |
-
"
|
|
|
|
| 108 |
"organic_waste_ton": 60.64,
|
| 109 |
"plastic_waste_ton": 27.54,
|
| 110 |
"paper_waste_ton": 13.89,
|
| 111 |
-
"
|
| 112 |
-
"
|
| 113 |
-
"textile_waste_ton": 5.07,
|
| 114 |
-
"other_waste_ton": 0.00,
|
| 115 |
-
"risk_status": "SAFE",
|
| 116 |
-
"event_info": null,
|
| 117 |
-
"recommended_trucks": 25
|
| 118 |
}
|
| 119 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
}
|
| 121 |
}
|
| 122 |
```
|
| 123 |
|
| 124 |
-
### 2.
|
| 125 |
* **Method**: `GET`
|
| 126 |
* **Endpoint**: `/api/v1/autopilot`
|
| 127 |
-
* **Description**: Mengembalikan kalkulasi prediksi otonom hari ini untuk seluruh 44 kecamatan DKI secara paralel.
|
| 128 |
|
| 129 |
-
### 3. News Feed
|
| 130 |
* **Method**: `GET`
|
| 131 |
* **Endpoint**: `/api/v1/news`
|
| 132 |
-
* **Description**: Mengembalikan
|
| 133 |
|
| 134 |
---
|
| 135 |
|
| 136 |
-
##
|
| 137 |
|
| 138 |
-
### 1.
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
### 2. Deploy ke Vercel (Frontend Dashboard)
|
| 145 |
-
Frontend dirancang agar terpisah dan di-host di Vercel:
|
| 146 |
-
1. Hubungkan repositori GitHub Anda ke akun Vercel.
|
| 147 |
-
2. Buat proyek baru dan pilih repositori `Aeterna-Ai`.
|
| 148 |
-
3. Di bagian pengaturan Vercel, Anda dapat membiarkannya default (Vercel akan mendeteksi `vercel.json` di root) atau mengatur **Root Directory** langsung ke folder `frontend/`.
|
| 149 |
-
4. Vercel akan menyajikan frontend statis dan secara otomatis mem-proxy pemanggilan API `/api` langsung ke backend Hugging Face Space Anda tanpa kendala CORS.
|
| 150 |
-
|
| 151 |
-
---
|
| 152 |
|
| 153 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
|
| 155 |
-
###
|
|
|
|
| 156 |
```bash
|
| 157 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
```
|
| 159 |
|
| 160 |
-
###
|
| 161 |
```bash
|
| 162 |
-
|
|
|
|
|
|
|
| 163 |
```
|
| 164 |
-
|
| 165 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
---
|
| 10 |
|
| 11 |
# 🚛 Aeterna AI: Next-Gen Waste Intelligence Platform
|
| 12 |
+
**Platform Sistem Peringatan Dini & Peramalan Sampah Real-Time DKI Jakarta (Case 2 - AI Open Innovation Challenge 2026)**
|
| 13 |
|
| 14 |
+
---
|
| 15 |
|
| 16 |
+
## 📖 Overview
|
| 17 |
|
| 18 |
+
**Aeterna AI** (sebelumnya Eco-Twin AI) adalah platform analitik cerdas berbasis *Machine Learning* yang dirancang untuk memantau, memprediksi, dan mengoptimalkan manajemen logistik sampah DKI Jakarta secara spasial-temporal harian.
|
| 19 |
+
|
| 20 |
+
Platform ini mengubah paradigma pengelolaan sampah dari **reaktif** (menangani setelah terjadi penumpukan) menjadi **prediktif** (memprediksi surge sebelum terjadi) guna mengoptimalkan penyebaran truk pengangkut ke 44 kecamatan DKI Jakarta.
|
| 21 |
|
| 22 |
> [!NOTE]
|
| 23 |
+
> **📖 DOKUMENTASI SISTEM BACKEND MENDALAM**:
|
| 24 |
+
> Untuk rincian mendalam mengenai formula rekayasa fitur cuaca/event, detail hyperparameter GridSearchCV, dan arsitektur asinkron FastAPI, silakan merujuk ke **[BACKEND_DOC.md](file:///c:/khusus project IT/Fine tuning ulang AI jakarta/waste-prediction-api/BACKEND_DOC.md)**.
|
| 25 |
|
| 26 |
---
|
| 27 |
|
| 28 |
## 🌟 Fitur Unggulan (Key Features)
|
| 29 |
|
| 30 |
+
1. **AI Autopilot Forecaster**: Sistem asinkron otonom yang mengevaluasi seluruh **44 Kecamatan DKI Jakarta** secara paralel berdasarkan curah hujan tingkat koordinat (Open-Meteo) dan kalender event aktif.
|
| 31 |
+
2. **6-Kategori Komposisi Sampah**: Memprediksi rincian tonase sampah secara proporsional sesuai statistik riil DLH DKI Jakarta: *Sisa Makanan (~50.2%), Plastik (~22.8%), Kertas (~11.5%), Tekstil (~4.2%), Kaca (~3.2%), dan Logam/Lainnya (~8.1%)*.
|
| 32 |
+
3. **Dynamic Weather Multiplier**: Mengintegrasikan curah hujan real-time per kecamatan berdasarkan titik koordinat geografis asli untuk mengukur penambahan berat sampah basah akibat resapan air hujan (2% s.d. 5% multiplier).
|
| 33 |
+
4. **Event Calendar Crowd Engine**: Mengidentifikasi jadwal acara besar Jakarta (seperti PRJ, BTN Marathon, HUT RI, dll.) untuk menghitung lonjakan kapasitas sampah kerumunan (10% s.d. 35% multiplier).
|
| 34 |
+
5. **Interactive Cyber HUD UI**: Antarmuka bertema *Dark Glassmorphism* dengan kursor delay kustom (lerp), visualisasi progress bar kategori neon glow, rincian logistik armada truk (15-Ton Heavy Compactor), dan rute logistik ke TPST Bantargebang.
|
| 35 |
|
| 36 |
---
|
| 37 |
|
| 38 |
+
## 🏗️ Arsitektur Sistem (Clean Architecture)
|
| 39 |
+
|
| 40 |
+
Sistem ini didesain menggunakan arsitektur modular multi-platform yang terpisah (*decoupled*):
|
| 41 |
+
|
| 42 |
+
```
|
| 43 |
+
+-----------------------------------+
|
| 44 |
+
| NEXT.JS FRONTEND |
|
| 45 |
+
| (Hosted on Vercel Global CDN) |
|
| 46 |
+
+-----------------------------------+
|
| 47 |
+
|
|
| 48 |
+
| HTTPS Requests
|
| 49 |
+
v
|
| 50 |
+
+-----------------------------------+
|
| 51 |
+
| LARAVEL BACKEND |
|
| 52 |
+
| (API Controller & Gateway) |
|
| 53 |
+
+-----------------------------------+
|
| 54 |
+
|
|
| 55 |
+
| REST Proxy API
|
| 56 |
+
v
|
| 57 |
+
+---------------------------------------------------------------------------------------------------+
|
| 58 |
+
| PYTHON ML MICROSERVICE CONTAINER |
|
| 59 |
+
| (Docker - Hosted on Hugging Face Spaces) |
|
| 60 |
+
| |
|
| 61 |
+
| [ FastAPI ] --> [ GBR Model (GridSearchCV) ] & [ Amazon Chronos-T5 ] --> [ Open-Meteo API Sync ] |
|
| 62 |
+
+---------------------------------------------------------------------------------------------------+
|
| 63 |
+
```
|
| 64 |
|
| 65 |
+
### Penjelasan Lapisan (Layers):
|
| 66 |
+
* **Front-End Layer (Next.js)**: Menyajikan antarmuka pengguna interaktif. Memanfaatkan *Next.js Dynamic Imports* dengan SSR dinonaktifkan khusus untuk modul **Leaflet.js** agar peta interaktif dapat dirender secara asinkron di client-side tanpa memicu error server-node.
|
| 67 |
+
* **API Gateway Layer (Laravel)**: Berfungsi sebagai backend pengendali utama. Menangani middleware CORS, validasi skema request, manajemen kalender event, parsing berita, dan mengamankan pemanggilan proxy ke microservice AI.
|
| 68 |
+
* **ML Microservice Layer (Python)**: Kontainer Docker yang memuat model AI (GBR & Chronos) dan menyajikan API prediksi berkecepatan tinggi menggunakan FastAPI dengan threadpool non-blocking.
|
| 69 |
|
| 70 |
+
---
|
| 71 |
+
|
| 72 |
+
## 📊 Hasil Evaluasi & Akurasi Model GBR
|
| 73 |
+
|
| 74 |
+
Model GBR dilatih dengan **GridSearchCV** di atas dataset historis teraugmentasi 2 tahun dengan baseline rata-rata kota **8.020 Ton/hari**.
|
| 75 |
|
| 76 |
| Metrik Evaluasi | Model Baseline | Model Upgraded (Aeterna AI) | Status Performa |
|
| 77 |
| :--- | :---: | :---: | :--- |
|
| 78 |
+
| **Mean Absolute Error (MAE)** | `149.13 Ton` | **`132.29 Ton`** | Lebih Baik (Turun ⬇️) |
|
| 79 |
+
| **Root Mean Squared Error (RMSE)** | `188.46 Ton` | **`165.46 Ton`** | Lebih Baik (Turun ⬇️) |
|
| 80 |
+
| **R-Squared ($R^2$ Score)** | `76.02%` | **`81.51%`** | Lebih Baik (Naik ⬆️) |
|
| 81 |
| **Mean Absolute Percentage Error (MAPE)** | `1.78%` | **`1.59%`** | **Sangat Akurat (< 10%) (⬇️)** |
|
| 82 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
---
|
| 84 |
|
| 85 |
+
## 🌦️ Model Rekayasa Fitur Matematika
|
| 86 |
|
| 87 |
+
### A. Rainfall weight multiplier:
|
| 88 |
+
$$Volume_{calibrated} = Volume_{pred} \times \left(1.0 + \frac{Precipitation_{mm}}{1000} \right)$$
|
| 89 |
+
|
| 90 |
+
### B. Event Crowd Multiplier:
|
| 91 |
+
* Skala 1 s.d. 2 (Lokal): **+10% s.d. +15%** volume sampah.
|
| 92 |
+
* Skala 3 s.d. 4 (Regional): **+20% s.d. +25%** volume sampah.
|
| 93 |
+
* Skala 5 (Nasional / Hari Raya): **+30% s.d. +35%** volume sampah.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
|
| 95 |
---
|
| 96 |
|
| 97 |
+
## 📡 Referensi Endpoint API Utama
|
| 98 |
|
| 99 |
Semua endpoint didukung dengan dokumentasi interaktif Swagger UI di `/docs`.
|
| 100 |
|
| 101 |
### 1. Predict Waste Volume (Forecasting)
|
| 102 |
* **Method**: `POST`
|
| 103 |
* **Endpoint**: `/api/v1/predict`
|
| 104 |
+
* **Request Payload**:
|
| 105 |
```json
|
| 106 |
{
|
| 107 |
"forecast_days": 7,
|
|
|
|
| 112 |
"granularity": "daily"
|
| 113 |
}
|
| 114 |
```
|
| 115 |
+
* **Response Payload**:
|
| 116 |
```json
|
| 117 |
{
|
| 118 |
"status": "success",
|
| 119 |
+
"confidence_score": 0.9325,
|
| 120 |
"message": "Normal conditions.",
|
| 121 |
"data": {
|
| 122 |
"prediction_results": [
|
| 123 |
{
|
| 124 |
+
"date": "2026-07-15",
|
| 125 |
+
"location": "Menteng",
|
| 126 |
+
"total_volume_ton": 120.8,
|
| 127 |
"organic_waste_ton": 60.64,
|
| 128 |
"plastic_waste_ton": 27.54,
|
| 129 |
"paper_waste_ton": 13.89,
|
| 130 |
+
"recommended_trucks": 8,
|
| 131 |
+
"risk_status": "SAFE"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
}
|
| 133 |
+
],
|
| 134 |
+
"logistics_plan": {
|
| 135 |
+
"trucks_needed": 8,
|
| 136 |
+
"manpower": 24,
|
| 137 |
+
"estimated_duration_hours": 8.1,
|
| 138 |
+
"efficiency_rate": "85% (Optimal)"
|
| 139 |
+
}
|
| 140 |
}
|
| 141 |
}
|
| 142 |
```
|
| 143 |
|
| 144 |
+
### 2. Autopilot Live DKI (Today)
|
| 145 |
* **Method**: `GET`
|
| 146 |
* **Endpoint**: `/api/v1/autopilot`
|
| 147 |
+
* **Description**: Mengembalikan kalkulasi prediksi otonom hari ini untuk seluruh 44 kecamatan DKI secara paralel lengkap dengan data koordinat (latitude, longitude) untuk plotting peta instan.
|
| 148 |
|
| 149 |
+
### 3. News Feed API
|
| 150 |
* **Method**: `GET`
|
| 151 |
* **Endpoint**: `/api/v1/news`
|
| 152 |
+
* **Description**: Mengembalikan 10 berita persampahan DKI Jakarta terbaru yang diperbarui dinamis menggunakan Conduit AI API (dengan generator fallback lokal).
|
| 153 |
|
| 154 |
---
|
| 155 |
|
| 156 |
+
## 🛠️ Panduan Instalasi & Pengembangan Lokal
|
| 157 |
|
| 158 |
+
### 1. Prasyarat & Backend Setup (Python ML)
|
| 159 |
+
Masuk ke root folder proyek:
|
| 160 |
+
```bash
|
| 161 |
+
# Install dependencies
|
| 162 |
+
pip install -r requirements.txt
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
|
| 164 |
+
# Jalankan server FastAPI lokal
|
| 165 |
+
python -m uvicorn app:app --port 8001 --host 127.0.0.1
|
| 166 |
+
```
|
| 167 |
+
* Akses UI di: `http://localhost:8001`
|
| 168 |
+
* Akses Swagger di: `http://localhost:8001/docs`
|
| 169 |
|
| 170 |
+
### 2. Setup Backend Controller (Laravel)
|
| 171 |
+
Jika menggunakan controller Laravel untuk mengamankan route API:
|
| 172 |
```bash
|
| 173 |
+
# Clone atau masuk ke direktori Laravel Anda
|
| 174 |
+
composer install
|
| 175 |
+
cp .env.example .env
|
| 176 |
+
php artisan key:generate
|
| 177 |
+
```
|
| 178 |
+
Di dalam berkas `.env` Laravel, arahkan URL microservice AI Anda:
|
| 179 |
+
```env
|
| 180 |
+
AETERNA_ML_SERVICE_URL=http://localhost:8001
|
| 181 |
```
|
| 182 |
|
| 183 |
+
### 3. Setup Frontend Dashboard (Next.js)
|
| 184 |
```bash
|
| 185 |
+
# Masuk ke folder Next.js
|
| 186 |
+
npm install
|
| 187 |
+
npm run dev
|
| 188 |
```
|
| 189 |
+
* Pastikan Leaflet di-import dinamis:
|
| 190 |
+
```javascript
|
| 191 |
+
import dynamic from 'next/dynamic';
|
| 192 |
+
const MapComponent = dynamic(() => import('../components/Map'), { ssr: false });
|
| 193 |
+
```
|
| 194 |
+
|
| 195 |
+
---
|
| 196 |
+
|
| 197 |
+
## 👥 Kontributor Tim Pengembang (Aeterna Team)
|
| 198 |
+
|
| 199 |
+
* **FARIL PUTRA PRATAMA** (AI Engineer) — *SMK Taruna Bangsa*
|
| 200 |
+
* **ARGA KURNIAWAN** (Front End Developer)
|
| 201 |
+
* **BAGAS TRESNA MUSTIDA SAKLI** (System Architecture)
|
frontend/app.js
CHANGED
|
@@ -729,9 +729,7 @@ async function loadAutopilotFeed() {
|
|
| 729 |
autoRiskList.innerHTML = "";
|
| 730 |
data.top_kecamatan.forEach((item, index) => {
|
| 731 |
const card = document.createElement("div");
|
| 732 |
-
card.className = "alert-row";
|
| 733 |
-
card.style.gridTemplateColumns = "60px 180px 100px 1fr";
|
| 734 |
-
card.style.padding = "0.6rem 1.2rem";
|
| 735 |
card.innerHTML = `
|
| 736 |
<span class="alert-date" style="font-weight:bold; color:var(--cyan);">#0${index+1}</span>
|
| 737 |
<span class="alert-location">${item.location}</span>
|
|
|
|
| 729 |
autoRiskList.innerHTML = "";
|
| 730 |
data.top_kecamatan.forEach((item, index) => {
|
| 731 |
const card = document.createElement("div");
|
| 732 |
+
card.className = "alert-row autopilot-row";
|
|
|
|
|
|
|
| 733 |
card.innerHTML = `
|
| 734 |
<span class="alert-date" style="font-weight:bold; color:var(--cyan);">#0${index+1}</span>
|
| 735 |
<span class="alert-location">${item.location}</span>
|
frontend/style.css
CHANGED
|
@@ -1120,10 +1120,8 @@ footer {
|
|
| 1120 |
cursor: pointer;
|
| 1121 |
}
|
| 1122 |
|
| 1123 |
-
|
| 1124 |
-
|
| 1125 |
-
grid-template-columns: 1fr 1fr;
|
| 1126 |
-
}
|
| 1127 |
}
|
| 1128 |
|
| 1129 |
.alert-row:hover {
|
|
@@ -1388,9 +1386,21 @@ body::after {
|
|
| 1388 |
========================================== */
|
| 1389 |
@media (max-width: 1200px) {
|
| 1390 |
.dashboard-grid {
|
| 1391 |
-
|
|
|
|
| 1392 |
height: auto !important;
|
| 1393 |
overflow: visible !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1394 |
}
|
| 1395 |
.page-container {
|
| 1396 |
height: auto !important;
|
|
@@ -1424,24 +1434,32 @@ body::after {
|
|
| 1424 |
justify-content: center;
|
| 1425 |
flex-wrap: wrap;
|
| 1426 |
}
|
|
|
|
| 1427 |
.nav-links {
|
| 1428 |
-
|
| 1429 |
-
|
| 1430 |
-
gap: 0.
|
| 1431 |
-
width: 100%;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1432 |
}
|
| 1433 |
.nav-btn {
|
| 1434 |
-
|
| 1435 |
-
|
| 1436 |
-
|
| 1437 |
-
text-align: center;
|
|
|
|
|
|
|
| 1438 |
}
|
| 1439 |
.system-status {
|
| 1440 |
justify-content: center;
|
| 1441 |
width: 100%;
|
|
|
|
| 1442 |
}
|
| 1443 |
.page-container {
|
| 1444 |
-
padding: 1rem 1.
|
| 1445 |
}
|
| 1446 |
.stats-row {
|
| 1447 |
grid-template-columns: 1fr !important;
|
|
@@ -1457,11 +1475,37 @@ body::after {
|
|
| 1457 |
font-size: 0.95rem !important;
|
| 1458 |
}
|
| 1459 |
.map-panel {
|
| 1460 |
-
height:
|
| 1461 |
}
|
| 1462 |
.logistics-grid {
|
| 1463 |
grid-template-columns: 1fr !important;
|
| 1464 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1465 |
}
|
| 1466 |
|
| 1467 |
@media (max-width: 480px) {
|
|
@@ -1469,8 +1513,8 @@ body::after {
|
|
| 1469 |
font-size: 1.5rem !important;
|
| 1470 |
}
|
| 1471 |
.nav-btn {
|
| 1472 |
-
font-size: 0.
|
| 1473 |
-
padding:
|
| 1474 |
}
|
| 1475 |
.section-title {
|
| 1476 |
font-size: 1.2rem !important;
|
|
|
|
| 1120 |
cursor: pointer;
|
| 1121 |
}
|
| 1122 |
|
| 1123 |
+
.autopilot-row {
|
| 1124 |
+
grid-template-columns: 60px 180px 100px 1fr !important;
|
|
|
|
|
|
|
| 1125 |
}
|
| 1126 |
|
| 1127 |
.alert-row:hover {
|
|
|
|
| 1386 |
========================================== */
|
| 1387 |
@media (max-width: 1200px) {
|
| 1388 |
.dashboard-grid {
|
| 1389 |
+
display: flex !important;
|
| 1390 |
+
flex-direction: column !important;
|
| 1391 |
height: auto !important;
|
| 1392 |
overflow: visible !important;
|
| 1393 |
+
gap: 1.5rem !important;
|
| 1394 |
+
}
|
| 1395 |
+
/* Smart re-ordering for mobile: Show Map & Stats first, then Config Panel, then Charts */
|
| 1396 |
+
.map-and-stats {
|
| 1397 |
+
order: -1 !important;
|
| 1398 |
+
}
|
| 1399 |
+
.control-panel {
|
| 1400 |
+
order: 0 !important;
|
| 1401 |
+
}
|
| 1402 |
+
.analysis-panel {
|
| 1403 |
+
order: 1 !important;
|
| 1404 |
}
|
| 1405 |
.page-container {
|
| 1406 |
height: auto !important;
|
|
|
|
| 1434 |
justify-content: center;
|
| 1435 |
flex-wrap: wrap;
|
| 1436 |
}
|
| 1437 |
+
/* Sleek Equal-width Segmented Tab-bar on Mobile */
|
| 1438 |
.nav-links {
|
| 1439 |
+
display: grid !important;
|
| 1440 |
+
grid-template-columns: repeat(3, 1fr) !important;
|
| 1441 |
+
gap: 0.5rem !important;
|
| 1442 |
+
width: 100% !important;
|
| 1443 |
+
background: rgba(0, 0, 0, 0.4);
|
| 1444 |
+
padding: 4px;
|
| 1445 |
+
border-radius: 8px;
|
| 1446 |
+
border: 1px solid rgba(255, 255, 255, 0.05);
|
| 1447 |
}
|
| 1448 |
.nav-btn {
|
| 1449 |
+
width: 100% !important;
|
| 1450 |
+
padding: 8px 4px !important;
|
| 1451 |
+
font-size: 0.72rem !important;
|
| 1452 |
+
text-align: center !important;
|
| 1453 |
+
justify-content: center !important;
|
| 1454 |
+
margin: 0 !important;
|
| 1455 |
}
|
| 1456 |
.system-status {
|
| 1457 |
justify-content: center;
|
| 1458 |
width: 100%;
|
| 1459 |
+
margin-top: 0.2rem;
|
| 1460 |
}
|
| 1461 |
.page-container {
|
| 1462 |
+
padding: 1rem 1.2rem !important;
|
| 1463 |
}
|
| 1464 |
.stats-row {
|
| 1465 |
grid-template-columns: 1fr !important;
|
|
|
|
| 1475 |
font-size: 0.95rem !important;
|
| 1476 |
}
|
| 1477 |
.map-panel {
|
| 1478 |
+
height: 350px !important;
|
| 1479 |
}
|
| 1480 |
.logistics-grid {
|
| 1481 |
grid-template-columns: 1fr !important;
|
| 1482 |
}
|
| 1483 |
+
/* Stack progress bars in 1 column to prevent text clipping */
|
| 1484 |
+
.progress-container {
|
| 1485 |
+
grid-template-columns: 1fr !important;
|
| 1486 |
+
gap: 1rem !important;
|
| 1487 |
+
}
|
| 1488 |
+
/* Flex stack alerts to fit narrow viewports beautifully */
|
| 1489 |
+
.alert-row, .autopilot-row {
|
| 1490 |
+
display: flex !important;
|
| 1491 |
+
flex-direction: column !important;
|
| 1492 |
+
align-items: flex-start !important;
|
| 1493 |
+
gap: 0.5rem !important;
|
| 1494 |
+
padding: 1rem 1.2rem !important;
|
| 1495 |
+
}
|
| 1496 |
+
.alert-row > *, .autopilot-row > * {
|
| 1497 |
+
width: auto !important;
|
| 1498 |
+
margin: 0 !important;
|
| 1499 |
+
text-align: left !important;
|
| 1500 |
+
}
|
| 1501 |
+
.alert-desc, .alert-location {
|
| 1502 |
+
font-size: 0.8rem !important;
|
| 1503 |
+
word-break: break-word !important;
|
| 1504 |
+
line-height: 1.4 !important;
|
| 1505 |
+
}
|
| 1506 |
+
.alert-badge {
|
| 1507 |
+
align-self: flex-start !important;
|
| 1508 |
+
}
|
| 1509 |
}
|
| 1510 |
|
| 1511 |
@media (max-width: 480px) {
|
|
|
|
| 1513 |
font-size: 1.5rem !important;
|
| 1514 |
}
|
| 1515 |
.nav-btn {
|
| 1516 |
+
font-size: 0.68rem !important;
|
| 1517 |
+
padding: 6px 2px !important;
|
| 1518 |
}
|
| 1519 |
.section-title {
|
| 1520 |
font-size: 1.2rem !important;
|