diff --git a/DASHBOARD_API_DOCUMENTATION.md b/DASHBOARD_API_DOCUMENTATION.md
deleted file mode 100644
index af063ad..0000000
--- a/DASHBOARD_API_DOCUMENTATION.md
+++ /dev/null
@@ -1,653 +0,0 @@
-# مستندات API داشبورد Farm (Farm Dashboard)
-
-این سند شامل توضیحات کل داشبورد، APIهای تنظیمات (disable/enable/move کارتها) و ساختار پیشنهادی ریسپانس برای محتوای کارتها است.
-
----
-
-## ۱. نمای کلی داشبورد
-
-داشبورد Farm از کامپوننت `FarmDashboardWrapper` استفاده میکند و شامل ردیفها (rows) و کارتهای (cards) زیر است:
-
-| Row ID | Row Label | کارتها |
-|--------|-----------|---------|
-| `overviewKpis` | Overview KPIs | `farmOverviewKpis` |
-| `weatherAlerts` | Weather & Alerts | `farmWeatherCard`, `farmAlertsTracker` |
-| `sensorMonitoring` | Sensor Monitoring | `sensorValuesList`, `sensorRadarChart` |
-| `sensorCharts` | Sensor Charts | `sensorComparisonChart`, `anomalyDetectionCard` |
-| `alertsWater` | Alerts & Water Prediction | `farmAlertsTimeline`, `waterNeedPrediction` |
-| `predictions` | Predictions | `harvestPredictionCard`, `yieldPredictionChart` |
-| `soilHeatmap` | Soil Moisture Heatmap | `soilMoistureHeatmap` |
-| `ndviRecommendations` | NDVI & Recommendations | `ndviHealthCard`, `recommendationsList` |
-| `economic` | Economic Overview | `economicOverview` |
-
----
-
-## ۲. APIهای تنظیمات داشبورد
-
-### ۲.۱ دریافت تنظیمات داشبورد (Get Config)
-
-```
-GET /api/farm-dashboard-config
-```
-
-**توضیح:** تنظیمات شخصیسازی داشبورد کاربر لاگینشده را برمیگرداند.
-
-**Response:**
-```json
-{
- "code": 200,
- "msg": "OK",
- "data": {
- "disabled_card_ids": ["farmWeatherCard", "sensorRadarChart"],
- "row_order": [
- "overviewKpis",
- "weatherAlerts",
- "sensorMonitoring",
- "sensorCharts",
- "alertsWater",
- "predictions",
- "soilHeatmap",
- "ndviRecommendations",
- "economic"
- ],
- "enable_drag_reorder": true
- }
-}
-```
-
-**فیلدها:**
-| فیلد | نوع | توضیح |
-|------|-----|-------|
-| `disabled_card_ids` | `string[]` | لیست شناسه کارتهای غیرفعال (hidden) |
-| `row_order` | `string[]` | ترتیب نمایش ردیفها |
-| `enable_drag_reorder` | `boolean` | امکان جابجایی ردیفها با drag |
-
----
-
-### ۲.۲ غیرفعال کردن کارت (Disable Card)
-
-```
-PATCH /api/farm-dashboard-config
-```
-
-**Request Body:**
-```json
-{
- "disabled_card_ids": ["farmWeatherCard", "sensorRadarChart"]
-}
-```
-
-کارت با شناسه `cardId` به لیست `disabled_card_ids` اضافه میشود و در داشبورد نمایش داده نمیشود.
-
----
-
-### ۲.۳ فعال کردن کارت (Enable Card)
-
-```
-PATCH /api/farm-dashboard-config
-```
-
-**Request Body:**
-```json
-{
- "disabled_card_ids": ["farmWeatherCard"]
-}
-```
-
-شناسه کارت از لیست `disabled_card_ids` حذف میشود و کارت دوباره نمایش داده میشود.
-
-**نکته:** کل لیست `disabled_card_ids` جدید ارسال میشود؛ برای enable باید آرایه بدون آن کارت فرستاده شود.
-
----
-
-### ۲.۴ جابجایی ردیفها (Move Rows)
-
-```
-PATCH /api/farm-dashboard-config
-```
-
-**Request Body:**
-```json
-{
- "row_order": [
- "overviewKpis",
- "weatherAlerts",
- "sensorMonitoring",
- "predictions",
- "sensorCharts",
- "alertsWater",
- "soilHeatmap",
- "ndviRecommendations",
- "economic"
- ]
-}
-```
-
-ترتیب ردیفها طبق آرایه `row_order` ذخیره میشود.
-
----
-
-### ۲.۵ تغییر وضعیت Drag Reorder
-
-```
-PATCH /api/farm-dashboard-config
-```
-
-**Request Body:**
-```json
-{
- "enable_drag_reorder": false
-}
-```
-
----
-
-## ۳. API دریافت همه دیتای کارتها
-
-### Endpoint پیشنهادی
-
-```
-GET /api/farm-dashboard
-```
-
-یا به تفکیک کارت:
-```
-GET /api/farm-dashboard/cards
-```
-
----
-
-## ۴. لیست کامل ریسپانس هر کارت
-
-ساختار پیشنهادی response برای محتوای هر کارت (بر اساس دادههای mock فعلی در فرانت):
-
-### ۴.۱ farmOverviewKpis
-
-```json
-{
- "kpis": [
- {
- "id": "farm_health_score",
- "title": "Farm Health Score",
- "subtitle": "AI Analysis",
- "stats": "87%",
- "avatarColor": "success",
- "avatarIcon": "tabler-heartbeat",
- "chipText": "Good",
- "chipColor": "success"
- },
- {
- "id": "water_stress_index",
- "title": "Water Stress Index",
- "subtitle": "Current",
- "stats": "12%",
- "avatarColor": "info",
- "avatarIcon": "tabler-droplet",
- "chipText": "Low",
- "chipColor": "success"
- },
- {
- "id": "disease_risk",
- "title": "Disease Risk",
- "subtitle": "Last 7 Days",
- "stats": "Low",
- "avatarColor": "success",
- "avatarIcon": "tabler-bug",
- "chipText": "5%",
- "chipColor": "success"
- },
- {
- "id": "avg_soil_moisture",
- "title": "Avg Soil Moisture",
- "subtitle": "Field-wide",
- "stats": "65%",
- "avatarColor": "primary",
- "avatarIcon": "tabler-plant-2",
- "chipText": "Optimal",
- "chipColor": "success"
- },
- {
- "id": "yield_prediction",
- "title": "Yield Prediction",
- "subtitle": "This Season",
- "stats": "42 ton",
- "avatarColor": "secondary",
- "avatarIcon": "tabler-chart-bar",
- "chipText": "+8%",
- "chipColor": "success"
- },
- {
- "id": "pest_risk",
- "title": "Pest Risk",
- "subtitle": "AI Forecast",
- "stats": "15%",
- "avatarColor": "warning",
- "avatarIcon": "tabler-bug-off",
- "chipText": "Monitor",
- "chipColor": "warning"
- }
- ]
-}
-```
-
----
-
-### ۴.۲ farmWeatherCard
-
-```json
-{
- "condition": "Clear",
- "temperature": 24,
- "unit": "°C",
- "humidity": 45,
- "windSpeed": 12,
- "windUnit": "km/h",
- "chartData": {
- "labels": ["6am", "9am", "12pm", "3pm", "6pm", "9pm", "12am"],
- "series": [[18, 22, 26, 28, 25, 20, 18]]
- }
-}
-```
-
----
-
-### ۴.۳ farmAlertsTracker
-
-```json
-{
- "totalAlerts": 3,
- "radialBarValue": 30,
- "alertStats": [
- {
- "title": "Water Shortage",
- "count": "2",
- "avatarColor": "error",
- "avatarIcon": "tabler-droplet-half-2"
- },
- {
- "title": "Fungal Risk",
- "count": "1",
- "avatarColor": "warning",
- "avatarIcon": "tabler-mushroom"
- },
- {
- "title": "Frost Alert",
- "count": "0",
- "avatarColor": "info",
- "avatarIcon": "tabler-snowflake"
- }
- ]
-}
-```
-
----
-
-### ۴.۴ sensorValuesList
-
-```json
-{
- "sensors": [
- {
- "title": "28°C",
- "subtitle": "Air Temperature",
- "trendNumber": 2.1,
- "trend": "positive",
- "unit": "°C"
- },
- {
- "title": "24°C",
- "subtitle": "Soil Temperature",
- "trendNumber": -0.5,
- "trend": "negative",
- "unit": "°C"
- },
- {
- "title": "65%",
- "subtitle": "Air Humidity",
- "trendNumber": 3.2,
- "trend": "positive",
- "unit": "%"
- },
- {
- "title": "42%",
- "subtitle": "Soil Moisture (10cm)",
- "trendNumber": -1.8,
- "trend": "negative",
- "unit": "%"
- },
- {
- "title": "6.8",
- "subtitle": "Soil pH",
- "trendNumber": 0.2,
- "trend": "positive",
- "unit": "pH"
- },
- {
- "title": "1.2",
- "subtitle": "EC (dS/m)",
- "trendNumber": 0.1,
- "trend": "positive",
- "unit": "dS/m"
- },
- {
- "title": "850",
- "subtitle": "Light Intensity (lux)",
- "trendNumber": 15.3,
- "trend": "positive",
- "unit": "lux"
- },
- {
- "title": "12",
- "subtitle": "Wind Speed (km/h)",
- "trendNumber": -2.4,
- "trend": "negative",
- "unit": "km/h"
- }
- ]
-}
-```
-
----
-
-### ۴.۵ sensorRadarChart
-
-```json
-{
- "labels": ["Temp", "Humidity", "pH", "EC", "Light", "Wind"],
- "series": [
- { "name": "Today", "data": [75, 65, 80, 70, 85, 60] },
- { "name": "Ideal", "data": [80, 70, 75, 75, 90, 50] }
- ]
-}
-```
-
----
-
-### ۴.۶ sensorComparisonChart
-
-```json
-{
- "currentValue": 48,
- "vsLastWeek": "+5%",
- "vsLastWeekValue": 5,
- "categories": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
- "series": [
- { "name": "Today", "data": [42, 45, 48, 52, 50, 48, 46] },
- { "name": "Last Week", "data": [38, 40, 42, 45, 43, 40, 38] }
- ]
-}
-```
-
----
-
-### ۴.۷ anomalyDetectionCard
-
-```json
-{
- "anomalies": [
- {
- "sensor": "Soil Moisture Z3",
- "value": "38%",
- "expected": "45-65%",
- "deviation": "-12%",
- "severity": "warning"
- },
- {
- "sensor": "pH Sector 2",
- "value": "5.2",
- "expected": "6.0-7.0",
- "deviation": "-0.8",
- "severity": "error"
- }
- ]
-}
-```
-
----
-
-### ۴.۸ farmAlertsTimeline
-
-```json
-{
- "alerts": [
- {
- "title": "Water Shortage Risk",
- "description": "Soil moisture at 10cm depth (42%) is below optimal. AI predicts stress in 2-3 days if no irrigation. Recommended: irrigate within 24h.",
- "time": "15 min ago",
- "color": "warning"
- },
- {
- "title": "Fungal Disease Risk",
- "description": "High humidity (65%) + temp 24°C creates favorable conditions for fungal growth. Consider preventive fungicide or reduce irrigation.",
- "time": "1 hour ago",
- "color": "error"
- },
- {
- "title": "Irrigation Suggestion",
- "description": "Optimal watering window: 6:00-8:00 AM. Suggested amount: 450 m³ for Zone A. Expected efficiency gain: 12%.",
- "time": "2 hours ago",
- "color": "info"
- },
- {
- "title": "Soil Salinity Check",
- "description": "EC reading 1.2 dS/m is within range. No action needed. Next check recommended in 5 days.",
- "time": "4 hours ago",
- "color": "success"
- }
- ]
-}
-```
-
----
-
-### ۴.۹ waterNeedPrediction
-
-```json
-{
- "totalNext7Days": 3290,
- "unit": "m³",
- "categories": ["Day 1", "Day 2", "Day 3", "Day 4", "Day 5", "Day 6", "Day 7"],
- "series": [{ "name": "Water Need", "data": [420, 450, 480, 460, 490, 510, 480] }]
-}
-```
-
----
-
-### ۴.۱۰ harvestPredictionCard
-
-```json
-{
- "date": "2025-10-15",
- "dateFormatted": "Oct 15, 2025",
- "daysUntil": 58,
- "description": "Based on current GDD accumulation and weather forecast. Optimal harvest window: Oct 12-18.",
- "optimalWindowStart": "2025-10-12",
- "optimalWindowEnd": "2025-10-18"
-}
-```
-
----
-
-### ۴.۱۱ yieldPredictionChart
-
-```json
-{
- "categories": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
- "series": [
- { "name": "This Year", "data": [35, 38, 40, 42, 45, 48, 50, 48, 46, 44, 42, 42] },
- { "name": "Last Year", "data": [32, 34, 36, 38, 40, 42, 44, 42, 40, 38, 36, 38] }
- ],
- "summary": [
- { "title": "Predicted Yield", "subtitle": "This Season", "amount": "42 ton", "avatarColor": "primary", "avatarIcon": "tabler-chart-bar" },
- { "title": "Harvest Date", "subtitle": "Est. Oct 15", "amount": "+8%", "avatarColor": "success", "avatarIcon": "tabler-calendar" }
- ]
-}
-```
-
----
-
-### ۴.۱۲ soilMoistureHeatmap
-
-```json
-{
- "zones": ["Z1", "Z2", "Z3", "Z4", "Z5", "Z6", "Z7"],
- "hours": ["6h", "8h", "10h", "12h", "14h", "16h", "18h"],
- "series": [
- { "name": "Z1", "data": [{"x": "6h", "y": 52}, {"x": "8h", "y": 48}, {"x": "10h", "y": 55}, {"x": "12h", "y": 60}, {"x": "14h", "y": 58}, {"x": "16h", "y": 54}, {"x": "18h", "y": 50}] },
- { "name": "Z2", "data": [{"x": "6h", "y": 45}, {"x": "8h", "y": 42}, {"x": "10h", "y": 48}, {"x": "12h", "y": 52}, {"x": "14h", "y": 50}, {"x": "16h", "y": 47}, {"x": "18h", "y": 44}] }
- ]
-}
-```
-
----
-
-### ۴.۱۳ ndviHealthCard
-
-```json
-{
- "ndviIndex": 0.78,
- "healthData": [
- { "title": "Nitrogen Stress", "value": "Low", "color": "success", "icon": "tabler-leaf" },
- { "title": "Crop Health", "value": "Good", "color": "success", "icon": "tabler-plant" }
- ]
-}
-```
-
----
-
-### ۴.۱۴ recommendationsList
-
-```json
-{
- "recommendations": [
- {
- "title": "Irrigation: 6:00-8:00 AM",
- "subtitle": "450 m³ for Zone A. Without irrigation, yield may drop ~8%.",
- "avatarIcon": "tabler-droplet",
- "avatarColor": "primary"
- },
- {
- "title": "Fertilizer: NPK 20-20-20",
- "subtitle": "Apply 25 kg/ha in 7 days. Current N deficiency in sector 2.",
- "avatarIcon": "tabler-leaf",
- "avatarColor": "success"
- },
- {
- "title": "Fungicide: Preventive",
- "subtitle": "Humidity + temp favor fungi. Consider copper-based spray.",
- "avatarIcon": "tabler-mushroom",
- "avatarColor": "warning"
- },
- {
- "title": "Harvest Window: Oct 12-18",
- "subtitle": "Peak ripeness expected Oct 15. Plan labor accordingly.",
- "avatarIcon": "tabler-calendar-event",
- "avatarColor": "info"
- }
- ]
-}
-```
-
----
-
-### ۴.۱۵ economicOverview
-
-```json
-{
- "economicData": [
- { "title": "Water Cost", "value": "€720", "subtitle": "This month", "avatarIcon": "tabler-droplet", "avatarColor": "primary" },
- { "title": "AI Water Savings", "value": "€156", "subtitle": "18% saved", "avatarIcon": "tabler-bulb", "avatarColor": "success" },
- { "title": "Platform ROI", "value": "127%", "subtitle": "vs last year", "avatarIcon": "tabler-chart-line", "avatarColor": "info" },
- { "title": "Income Forecast", "value": "€42k", "subtitle": "This season", "avatarIcon": "tabler-currency-euro", "avatarColor": "success" }
- ],
- "chartSeries": [
- { "name": "Water Cost", "data": [120, 115, 110, 125, 118, 122] },
- { "name": "Fertilizer", "data": [80, 85, 90, 75, 82, 78] }
- ],
- "chartCategories": ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
-}
-```
-
----
-
-## ۵. Response یکپارچه همه کارتها
-
-اگر یک endpoint برای کل دیتای داشبورد داشته باشید:
-
-```
-GET /api/farm-dashboard
-```
-
-**Response پیشنهادی:**
-```json
-{
- "code": 200,
- "msg": "OK",
- "data": {
- "farmOverviewKpis": { ... },
- "farmWeatherCard": { ... },
- "farmAlertsTracker": { ... },
- "sensorValuesList": { ... },
- "sensorRadarChart": { ... },
- "sensorComparisonChart": { ... },
- "anomalyDetectionCard": { ... },
- "farmAlertsTimeline": { ... },
- "waterNeedPrediction": { ... },
- "harvestPredictionCard": { ... },
- "yieldPredictionChart": { ... },
- "soilMoistureHeatmap": { ... },
- "ndviHealthCard": { ... },
- "recommendationsList": { ... },
- "economicOverview": { ... }
- }
-}
-```
-
----
-
-## ۶. خلاصه Endpoints
-
-| عملیات | Method | Endpoint | Body |
-|--------|--------|----------|------|
-| دریافت تنظیمات | GET | `/api/farm-dashboard-config` | - |
-| غیرفعال کردن کارت | PATCH | `/api/farm-dashboard-config` | `{ "disabled_card_ids": [...] }` |
-| فعال کردن کارت | PATCH | `/api/farm-dashboard-config` | `{ "disabled_card_ids": [...] }` |
-| جابجایی ردیفها | PATCH | `/api/farm-dashboard-config` | `{ "row_order": [...] }` |
-| Enable/Disable Drag | PATCH | `/api/farm-dashboard-config` | `{ "enable_drag_reorder": boolean }` |
-| دیتای همه کارتها | GET | `/api/farm-dashboard` یا `/api/farm-dashboard/cards` | - |
-
----
-
-## ۷. Card IDs معتبر
-
-```
-farmOverviewKpis
-farmWeatherCard
-farmAlertsTracker
-sensorValuesList
-sensorRadarChart
-sensorComparisonChart
-anomalyDetectionCard
-farmAlertsTimeline
-waterNeedPrediction
-harvestPredictionCard
-yieldPredictionChart
-soilMoistureHeatmap
-ndviHealthCard
-recommendationsList
-economicOverview
-```
-
-## ۸. Row IDs معتبر
-
-```
-overviewKpis
-weatherAlerts
-sensorMonitoring
-sensorCharts
-alertsWater
-predictions
-soilHeatmap
-ndviRecommendations
-economic
-```
diff --git a/messages/fa.json b/messages/fa.json
index 86cdd45..4808070 100644
--- a/messages/fa.json
+++ b/messages/fa.json
@@ -142,7 +142,18 @@
"menuLevels": "سطحهای منو",
"menuLevel2": "سطح منو 2",
"menuLevel3": "سطح منو 3",
- "disabledMenu": "منوی غیرفعال"
+ "disabledMenu": "منوی غیرفعال",
+ "farmDomain": "مدیریت مزرعه",
+ "farmDashboard": "داشبورد مزرعه",
+ "cropHealth": "سلامت محصول",
+ "waterWeather": "آب و هوا",
+ "soilAnalytics": "تحلیل خاک",
+ "yieldHarvest": "عملکرد و برداشت",
+ "farmAlerts": "هشدارهای مزرعه",
+ "pestDiseaseRisk": "ریسک آفات و بیماری",
+ "economicOverview": "نمای اقتصادی",
+ "sensorSection": "سنسورها",
+ "sensor7In1": "سنسور خاک 7 در 1"
},
"farmDashboard": {
"settings": {
diff --git a/src/app/(dashboard)/(private)/crop-health/page.tsx b/src/app/(dashboard)/(private)/crop-health/page.tsx
new file mode 100644
index 0000000..72e4fae
--- /dev/null
+++ b/src/app/(dashboard)/(private)/crop-health/page.tsx
@@ -0,0 +1,7 @@
+import CropHealthPageWrapper from '@views/dashboards/farm/CropHealthPageWrapper'
+
+const CropHealthPage = async () => {
+ return
+}
+
+export default CropHealthPage
diff --git a/src/app/(dashboard)/(private)/economic-overview/page.tsx b/src/app/(dashboard)/(private)/economic-overview/page.tsx
new file mode 100644
index 0000000..226d183
--- /dev/null
+++ b/src/app/(dashboard)/(private)/economic-overview/page.tsx
@@ -0,0 +1,7 @@
+import EconomicOverviewPageWrapper from '@views/dashboards/farm/EconomicOverviewPageWrapper'
+
+const EconomicOverviewPage = async () => {
+ return
+}
+
+export default EconomicOverviewPage
diff --git a/src/app/(dashboard)/(private)/farm-alerts/page.tsx b/src/app/(dashboard)/(private)/farm-alerts/page.tsx
new file mode 100644
index 0000000..274eefc
--- /dev/null
+++ b/src/app/(dashboard)/(private)/farm-alerts/page.tsx
@@ -0,0 +1,7 @@
+import AlertsPageWrapper from '@views/dashboards/farm/AlertsPageWrapper'
+
+const FarmAlertsPage = async () => {
+ return
+}
+
+export default FarmAlertsPage
diff --git a/src/app/(dashboard)/(private)/pest-risk/page.tsx b/src/app/(dashboard)/(private)/pest-risk/page.tsx
new file mode 100644
index 0000000..61db928
--- /dev/null
+++ b/src/app/(dashboard)/(private)/pest-risk/page.tsx
@@ -0,0 +1,7 @@
+import PestRiskPageWrapper from '@views/dashboards/farm/PestRiskPageWrapper'
+
+const PestRiskPage = async () => {
+ return
+}
+
+export default PestRiskPage
diff --git a/src/app/(dashboard)/(private)/yield-harvest/page.tsx b/src/app/(dashboard)/(private)/yield-harvest/page.tsx
new file mode 100644
index 0000000..5aac408
--- /dev/null
+++ b/src/app/(dashboard)/(private)/yield-harvest/page.tsx
@@ -0,0 +1,7 @@
+import YieldHarvestPageWrapper from '@views/dashboards/farm/YieldHarvestPageWrapper'
+
+const YieldHarvestPage = async () => {
+ return
+}
+
+export default YieldHarvestPage
diff --git a/src/components/layout/vertical/VerticalMenu.tsx b/src/components/layout/vertical/VerticalMenu.tsx
index 75e7dfd..3762ec5 100644
--- a/src/components/layout/vertical/VerticalMenu.tsx
+++ b/src/components/layout/vertical/VerticalMenu.tsx
@@ -94,6 +94,23 @@ const VerticalMenu = ({ scrollMenu }: Props) => {
>
{t('dashboards')}
+
+ }>
+ {t('cropHealth')}
+
+ }>
+ {t('yieldHarvest')}
+
+ }>
+ {t('farmAlerts')}
+
+ }>
+ {t('pestDiseaseRisk')}
+
+ }>
+ {t('economicOverview')}
+
+
}>
{t('waterData')}
@@ -105,9 +122,9 @@ const VerticalMenu = ({ scrollMenu }: Props) => {
{t('cropZoning')}
-
+
}>
- Sensor 7
+ {t('sensor7In1')}
diff --git a/src/constants/navigation.ts b/src/constants/navigation.ts
index 4290a74..7a49129 100644
--- a/src/constants/navigation.ts
+++ b/src/constants/navigation.ts
@@ -4,6 +4,8 @@ export const navigationLabels = {
farm: 'داشبورد مزرعه',
waterData: 'دیتاهای آب',
soilData: 'اطلاعات خاک',
+ sensorSection: 'سنسورها',
+ sensor7In1: 'سنسور خاک 7 در 1',
dataSection: 'بخش دادهها',
crm: 'مدیریت ارتباط با مشتری',
analytics: 'تحلیلها',
diff --git a/src/data/dictionaries/ar.json b/src/data/dictionaries/ar.json
index 6b18001..1f2c788 100644
--- a/src/data/dictionaries/ar.json
+++ b/src/data/dictionaries/ar.json
@@ -100,6 +100,17 @@
"menuLevels": "مستويات القائمة",
"menuLevel2": "مستوى القائمة 2",
"menuLevel3": "مستوى القائمة 3",
- "disabledMenu": "قائمة المعوقين"
+ "disabledMenu": "قائمة المعوقين",
+ "farmDomain": "إدارة المزرعة",
+ "farmDashboard": "لوحة المزرعة",
+ "cropHealth": "صحة المحصول",
+ "waterWeather": "المياه والطقس",
+ "soilAnalytics": "تحليل التربة",
+ "yieldHarvest": "الإنتاج والحصاد",
+ "farmAlerts": "تنبيهات المزرعة",
+ "pestDiseaseRisk": "مخاطر الآفات والأمراض",
+ "economicOverview": "النظرة الاقتصادية",
+ "sensorSection": "المستشعرات",
+ "sensor7In1": "مستشعر التربة 7 في 1"
}
}
diff --git a/src/data/dictionaries/en.json b/src/data/dictionaries/en.json
index d32e680..e76778d 100644
--- a/src/data/dictionaries/en.json
+++ b/src/data/dictionaries/en.json
@@ -100,6 +100,17 @@
"menuLevels": "Menu Levels",
"menuLevel2": "Menu Level 2",
"menuLevel3": "Menu Level 3",
- "disabledMenu": "Disabled Menu"
+ "disabledMenu": "Disabled Menu",
+ "farmDomain": "Farm Management",
+ "farmDashboard": "Farm Dashboard",
+ "cropHealth": "Crop Health",
+ "waterWeather": "Water & Weather",
+ "soilAnalytics": "Soil Analytics",
+ "yieldHarvest": "Yield & Harvest",
+ "farmAlerts": "Farm Alerts",
+ "pestDiseaseRisk": "Pest & Disease Risk",
+ "economicOverview": "Economic Overview",
+ "sensorSection": "Sensors",
+ "sensor7In1": "Soil Sensor 7-in-1"
}
}
diff --git a/src/data/dictionaries/fa.json b/src/data/dictionaries/fa.json
index e962eed..bfdb9de 100644
--- a/src/data/dictionaries/fa.json
+++ b/src/data/dictionaries/fa.json
@@ -100,8 +100,17 @@
"menuLevels": "سطحهای منو",
"menuLevel2": "سطح منو 2",
"menuLevel3": "سطح منو 3",
- "disabledMenu": "منوی غیرفعال"
+ "disabledMenu": "منوی غیرفعال",
+ "farmDomain": "مدیریت مزرعه",
+ "farmDashboard": "داشبورد مزرعه",
+ "cropHealth": "سلامت محصول",
+ "waterWeather": "آب و هوا",
+ "soilAnalytics": "تحلیل خاک",
+ "yieldHarvest": "عملکرد و برداشت",
+ "farmAlerts": "هشدارهای مزرعه",
+ "pestDiseaseRisk": "ریسک آفات و بیماری",
+ "economicOverview": "نمای اقتصادی",
+ "sensorSection": "سنسورها",
+ "sensor7In1": "سنسور خاک 7 در 1"
}
}
-
-
diff --git a/src/data/dictionaries/fr.json b/src/data/dictionaries/fr.json
index 17bef06..e584f0b 100644
--- a/src/data/dictionaries/fr.json
+++ b/src/data/dictionaries/fr.json
@@ -100,6 +100,17 @@
"menuLevels": "Niveaux de menus",
"menuLevel2": "Niveau menu 2",
"menuLevel3": "Niveau menu 3",
- "disabledMenu": "Menu désactivé"
+ "disabledMenu": "Menu désactivé",
+ "farmDomain": "Gestion de la ferme",
+ "farmDashboard": "Tableau de bord ferme",
+ "cropHealth": "Santé des cultures",
+ "waterWeather": "Eau et météo",
+ "soilAnalytics": "Analyse du sol",
+ "yieldHarvest": "Rendement et récolte",
+ "farmAlerts": "Alertes ferme",
+ "pestDiseaseRisk": "Risque ravageurs et maladies",
+ "economicOverview": "Aperçu économique",
+ "sensorSection": "Capteurs",
+ "sensor7In1": "Capteur de sol 7-en-1"
}
}
diff --git a/src/data/navigation/horizontalMenuData.tsx b/src/data/navigation/horizontalMenuData.tsx
index c50a8ab..5c28d9c 100644
--- a/src/data/navigation/horizontalMenuData.tsx
+++ b/src/data/navigation/horizontalMenuData.tsx
@@ -36,6 +36,128 @@ const horizontalMenuData = (): HorizontalMenuDataType[] => [
}
]
},
+ {
+ label: 'farmDomain',
+ icon: 'tabler-plant-2',
+ children: [
+ {
+ label: 'farmDashboard',
+ icon: 'tabler-dashboard',
+ href: '/dashboard'
+ },
+ {
+ label: 'cropHealth',
+ icon: 'tabler-plant',
+ href: '/crop-health'
+ },
+ {
+ label: 'waterWeather',
+ icon: 'tabler-droplet',
+ href: '/water-data'
+ },
+ {
+ label: 'soilAnalytics',
+ icon: 'tabler-grain',
+ href: '/soil-data'
+ },
+ {
+ label: 'yieldHarvest',
+ icon: 'tabler-chart-line',
+ href: '/yield-harvest'
+ },
+ {
+ label: 'plantSimulator',
+ icon: 'tabler-seeding',
+ href: '/plant-simulator'
+ },
+ {
+ label: 'farmAlerts',
+ icon: 'tabler-alert-triangle',
+ href: '/farm-alerts'
+ },
+ {
+ label: 'pestDiseaseRisk',
+ icon: 'tabler-bug',
+ href: '/pest-risk'
+ },
+ {
+ label: 'economicOverview',
+ icon: 'tabler-currency-dollar',
+ href: '/economic-overview'
+ },
+ {
+ label: 'cropZoning',
+ icon: 'tabler-map-pin',
+ href: '/crop-zoning'
+ },
+ {
+ label: 'farmAiAssistant',
+ icon: 'tabler-robot',
+ href: '/farm-ai-assistant'
+ }
+ ]
+ },
+ {
+ label: 'farmDomain',
+ icon: 'tabler-plant-2',
+ children: [
+ {
+ label: 'farmDashboard',
+ icon: 'tabler-dashboard',
+ href: '/dashboard'
+ },
+ {
+ label: 'cropHealth',
+ icon: 'tabler-plant',
+ href: '/crop-health'
+ },
+ {
+ label: 'waterWeather',
+ icon: 'tabler-droplet',
+ href: '/water-data'
+ },
+ {
+ label: 'soilAnalytics',
+ icon: 'tabler-grain',
+ href: '/soil-data'
+ },
+ {
+ label: 'yieldHarvest',
+ icon: 'tabler-chart-line',
+ href: '/yield-harvest'
+ },
+ {
+ label: 'plantSimulator',
+ icon: 'tabler-seeding',
+ href: '/plant-simulator'
+ },
+ {
+ label: 'farmAlerts',
+ icon: 'tabler-alert-triangle',
+ href: '/farm-alerts'
+ },
+ {
+ label: 'pestDiseaseRisk',
+ icon: 'tabler-bug',
+ href: '/pest-risk'
+ },
+ {
+ label: 'economicOverview',
+ icon: 'tabler-currency-dollar',
+ href: '/economic-overview'
+ },
+ {
+ label: 'cropZoning',
+ icon: 'tabler-map-pin',
+ href: '/crop-zoning'
+ },
+ {
+ label: 'farmAiAssistant',
+ icon: 'tabler-robot',
+ href: '/farm-ai-assistant'
+ }
+ ]
+ },
{
label: 'apps',
icon: 'tabler-mail',
diff --git a/src/data/navigation/verticalMenuData.tsx b/src/data/navigation/verticalMenuData.tsx
index 64981ff..1ba196b 100644
--- a/src/data/navigation/verticalMenuData.tsx
+++ b/src/data/navigation/verticalMenuData.tsx
@@ -40,6 +40,67 @@ const verticalMenuData = (): VerticalMenuDataType[] => [
}
]
},
+ {
+ label: 'farmDomain',
+ isSection: true,
+ children: [
+ {
+ label: 'farmDashboard',
+ icon: 'tabler-dashboard',
+ href: '/dashboard'
+ },
+ {
+ label: 'cropHealth',
+ icon: 'tabler-plant',
+ href: '/crop-health'
+ },
+ {
+ label: 'waterWeather',
+ icon: 'tabler-droplet',
+ href: '/water-data'
+ },
+ {
+ label: 'soilAnalytics',
+ icon: 'tabler-grain',
+ href: '/soil-data'
+ },
+ {
+ label: 'yieldHarvest',
+ icon: 'tabler-chart-line',
+ href: '/yield-harvest'
+ },
+ {
+ label: 'plantSimulator',
+ icon: 'tabler-seeding',
+ href: '/plant-simulator'
+ },
+ {
+ label: 'farmAlerts',
+ icon: 'tabler-alert-triangle',
+ href: '/farm-alerts'
+ },
+ {
+ label: 'pestDiseaseRisk',
+ icon: 'tabler-bug',
+ href: '/pest-risk'
+ },
+ {
+ label: 'economicOverview',
+ icon: 'tabler-currency-dollar',
+ href: '/economic-overview'
+ },
+ {
+ label: 'cropZoning',
+ icon: 'tabler-map-pin',
+ href: '/crop-zoning'
+ },
+ {
+ label: 'farmAiAssistant',
+ icon: 'tabler-robot',
+ href: '/farm-ai-assistant'
+ }
+ ]
+ },
{
label: 'frontPages',
icon: 'tabler-files',
diff --git a/src/hooks/useFarmHub.ts b/src/hooks/useFarmHub.ts
index c93e872..df0e936 100644
--- a/src/hooks/useFarmHub.ts
+++ b/src/hooks/useFarmHub.ts
@@ -4,6 +4,7 @@ import { useCallback, useEffect, useState } from "react";
export const FARM_HUB_STORAGE_KEY = "farm_hub";
const LEGACY_SENSOR_HUB_STORAGE_KEY = "sensor_hub";
+const FARM_HUB_UPDATED_EVENT = "farm-hub-updated";
export interface FarmHubInfo {
farm_uuid: string;
@@ -70,7 +71,28 @@ export const useFarmHub = (): UseFarmHubReturn => {
const [farmHub, setFarmHubState] = useState(null);
useEffect(() => {
- setFarmHubState(getStoredFarmHub());
+ const syncFarmHub = () => {
+ setFarmHubState(getStoredFarmHub());
+ };
+
+ const handleStorage = (event: StorageEvent) => {
+ if (
+ event.key === null ||
+ event.key === FARM_HUB_STORAGE_KEY ||
+ event.key === LEGACY_SENSOR_HUB_STORAGE_KEY
+ ) {
+ syncFarmHub();
+ }
+ };
+
+ syncFarmHub();
+ window.addEventListener("storage", handleStorage);
+ window.addEventListener(FARM_HUB_UPDATED_EVENT, syncFarmHub);
+
+ return () => {
+ window.removeEventListener("storage", handleStorage);
+ window.removeEventListener(FARM_HUB_UPDATED_EVENT, syncFarmHub);
+ };
}, []);
const setFarmHub = useCallback((data: FarmHubInfo | null) => {
@@ -79,6 +101,7 @@ export const useFarmHub = (): UseFarmHubReturn => {
if (data === null) {
localStorage.removeItem(FARM_HUB_STORAGE_KEY);
setFarmHubState(null);
+ window.dispatchEvent(new Event(FARM_HUB_UPDATED_EVENT));
return;
}
@@ -90,6 +113,7 @@ export const useFarmHub = (): UseFarmHubReturn => {
localStorage.setItem(FARM_HUB_STORAGE_KEY, JSON.stringify(normalized));
setFarmHubState(normalized);
+ window.dispatchEvent(new Event(FARM_HUB_UPDATED_EVENT));
}, []);
const getFarmUuid = useCallback(() => {
diff --git a/src/libs/api/services/cropHealthService.ts b/src/libs/api/services/cropHealthService.ts
new file mode 100644
index 0000000..e698393
--- /dev/null
+++ b/src/libs/api/services/cropHealthService.ts
@@ -0,0 +1,27 @@
+import { apiClient } from '../client'
+
+const PREFIX = '/api/crop-health'
+
+export interface CropHealthSummary {
+ farm_health_score?: Record
+ ndviHealthCard?: Record
+}
+
+interface ApiResponse {
+ code: number
+ msg: string
+ data: T
+}
+
+function extract(res: ApiResponse | T): T {
+ return res && typeof res === 'object' && 'data' in res ? (res as ApiResponse).data : (res as T)
+}
+
+export const cropHealthService = {
+ async getSummary(farmUuid: string): Promise {
+ const res = await apiClient.get | CropHealthSummary>(
+ `${PREFIX}/summary/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ return extract(res)
+ },
+}
diff --git a/src/libs/api/services/economicOverviewService.ts b/src/libs/api/services/economicOverviewService.ts
new file mode 100644
index 0000000..4b04b47
--- /dev/null
+++ b/src/libs/api/services/economicOverviewService.ts
@@ -0,0 +1,34 @@
+import { apiClient } from '../client'
+
+const PREFIX = '/api/economic-overview'
+
+export interface EconomicOverviewSummary {
+ economicOverview?: Record
+}
+
+interface ApiResponse {
+ code: number
+ msg: string
+ data: T
+ result?: T
+}
+
+function extract(res: ApiResponse | T): T {
+ if (res && typeof res === 'object') {
+ if ('data' in res) return (res as ApiResponse).data
+ if ('result' in res) return (res as ApiResponse).result as T
+ }
+
+ return res as T
+}
+
+export const economicOverviewService = {
+ async getSummary(farmUuid: string): Promise {
+ const res = await apiClient.get | EconomicOverviewSummary>(
+ `${PREFIX}/summary/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ const data = extract(res) as Record
+
+ return 'economicOverview' in data ? (data as EconomicOverviewSummary) : { economicOverview: data }
+ },
+}
diff --git a/src/libs/api/services/farmAlertsService.ts b/src/libs/api/services/farmAlertsService.ts
new file mode 100644
index 0000000..0fb6b3e
--- /dev/null
+++ b/src/libs/api/services/farmAlertsService.ts
@@ -0,0 +1,48 @@
+import { apiClient } from '../client'
+
+const PREFIX = '/api/farm-alerts'
+
+export interface FarmAlertsSummary {
+ tracker?: Record
+ timeline?: Record
+ recommendations?: Record
+}
+
+interface ApiResponse {
+ code: number
+ msg: string
+ data: T
+ result?: T
+}
+
+function extract(res: ApiResponse | T): T {
+ if (res && typeof res === 'object') {
+ if ('data' in res) return (res as ApiResponse).data
+ if ('result' in res) return (res as ApiResponse).result as T
+ }
+
+ return res as T
+}
+
+export const farmAlertsService = {
+ async getTracker(farmUuid: string): Promise> {
+ const res = await apiClient.get> | Record>(
+ `${PREFIX}/tracker/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ return extract(res)
+ },
+
+ async getTimeline(farmUuid: string): Promise> {
+ const res = await apiClient.get> | Record>(
+ `${PREFIX}/timeline/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ return extract(res)
+ },
+
+ async getRecommendations(farmUuid: string): Promise> {
+ const res = await apiClient.get> | Record>(
+ `${PREFIX}/recommendations/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ return extract(res)
+ },
+}
diff --git a/src/libs/api/services/pestDetectionDomainService.ts b/src/libs/api/services/pestDetectionDomainService.ts
new file mode 100644
index 0000000..e2318ee
--- /dev/null
+++ b/src/libs/api/services/pestDetectionDomainService.ts
@@ -0,0 +1,44 @@
+import { apiClient } from '../client'
+
+const PREFIX = '/api/pest-detection'
+
+export interface PestRiskSummary {
+ disease_risk?: Record
+ pest_risk?: Record
+}
+
+interface ApiResponse {
+ code: number
+ msg: string
+ data: T
+ result?: T
+}
+
+function extract(res: ApiResponse | T): T {
+ if (res && typeof res === 'object') {
+ if ('data' in res) return (res as ApiResponse).data
+ if ('result' in res) return (res as ApiResponse).result as T
+ }
+
+ return res as T
+}
+
+function toKpiCard(card?: Record): Record {
+ if (!card || typeof card !== 'object') return {}
+
+ return { kpis: [card] }
+}
+
+export const pestDetectionDomainService = {
+ async getRiskSummary(farmUuid: string): Promise {
+ const res = await apiClient.get | PestRiskSummary>(
+ `${PREFIX}/risk-summary/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ const data = extract(res)
+
+ return {
+ disease_risk: toKpiCard(data?.disease_risk),
+ pest_risk: toKpiCard(data?.pest_risk)
+ }
+ },
+}
diff --git a/src/libs/api/services/soilService.ts b/src/libs/api/services/soilService.ts
new file mode 100644
index 0000000..134e052
--- /dev/null
+++ b/src/libs/api/services/soilService.ts
@@ -0,0 +1,65 @@
+import { apiClient } from '../client'
+
+const PREFIX = '/api/soil'
+
+export interface SoilSummary {
+ avg_soil_moisture?: Record
+ sensorRadarChart?: Record
+ sensorComparisonChart?: Record
+ anomalyDetectionCard?: Record
+ soilMoistureHeatmap?: Record
+}
+
+interface ApiResponse {
+ code: number
+ msg: string
+ data: T
+}
+
+function extract(res: ApiResponse | T): T {
+ return res && typeof res === 'object' && 'data' in res ? (res as ApiResponse).data : (res as T)
+}
+
+export const soilService = {
+ async getSummary(farmUuid: string): Promise {
+ const res = await apiClient.get | SoilSummary>(
+ `${PREFIX}/summary/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ return extract(res)
+ },
+
+ async getAvgMoisture(farmUuid: string): Promise> {
+ const res = await apiClient.get> | Record>(
+ `${PREFIX}/avg-moisture/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ return extract(res)
+ },
+
+ async getSensorRadarChart(farmUuid: string): Promise> {
+ const res = await apiClient.get> | Record>(
+ `${PREFIX}/sensor-radar-chart/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ return extract(res)
+ },
+
+ async getSensorComparisonChart(farmUuid: string): Promise> {
+ const res = await apiClient.get> | Record>(
+ `${PREFIX}/sensor-comparison-chart/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ return extract(res)
+ },
+
+ async getAnomalies(farmUuid: string): Promise> {
+ const res = await apiClient.get> | Record>(
+ `${PREFIX}/anomalies/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ return extract(res)
+ },
+
+ async getMoistureHeatmap(farmUuid: string): Promise> {
+ const res = await apiClient.get> | Record>(
+ `${PREFIX}/moisture-heatmap/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ return extract(res)
+ },
+}
diff --git a/src/libs/api/services/waterService.ts b/src/libs/api/services/waterService.ts
new file mode 100644
index 0000000..64610dc
--- /dev/null
+++ b/src/libs/api/services/waterService.ts
@@ -0,0 +1,49 @@
+import { apiClient } from '../client'
+
+const PREFIX = '/api/water'
+
+export interface WaterSummary {
+ farmWeatherCard?: Record
+ waterNeedPrediction?: Record
+ water_stress_index?: Record
+}
+
+interface ApiResponse {
+ code: number
+ msg: string
+ data: T
+}
+
+function extract(res: ApiResponse | T): T {
+ return res && typeof res === 'object' && 'data' in res ? (res as ApiResponse).data : (res as T)
+}
+
+export const waterService = {
+ async getSummary(farmUuid: string): Promise {
+ const res = await apiClient.get | WaterSummary>(
+ `${PREFIX}/summary/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ return extract(res)
+ },
+
+ async getCard(farmUuid: string): Promise> {
+ const res = await apiClient.get> | Record>(
+ `${PREFIX}/card/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ return extract(res)
+ },
+
+ async getNeedPrediction(farmUuid: string): Promise> {
+ const res = await apiClient.get> | Record>(
+ `${PREFIX}/need-prediction/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ return extract(res)
+ },
+
+ async getStressIndex(farmUuid: string): Promise> {
+ const res = await apiClient.get> | Record>(
+ `${PREFIX}/stress-index/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ return extract(res)
+ },
+}
diff --git a/src/libs/api/services/yieldHarvestService.ts b/src/libs/api/services/yieldHarvestService.ts
new file mode 100644
index 0000000..4ac8f42
--- /dev/null
+++ b/src/libs/api/services/yieldHarvestService.ts
@@ -0,0 +1,46 @@
+import { apiClient } from '../client'
+
+const PREFIX = '/api/yield-harvest'
+
+export interface YieldHarvestSummary {
+ yield_prediction?: Record
+ yieldPredictionChart?: Record
+ harvestPredictionCard?: Record
+}
+
+interface ApiResponse {
+ code: number
+ msg: string
+ data: T
+ result?: T
+}
+
+function extract(res: ApiResponse | T): T {
+ if (res && typeof res === 'object') {
+ if ('data' in res) return (res as ApiResponse).data
+ if ('result' in res) return (res as ApiResponse).result as T
+ }
+
+ return res as T
+}
+
+function toKpiCard(card?: Record): Record {
+ if (!card || typeof card !== 'object') return {}
+
+ return { kpis: [card] }
+}
+
+export const yieldHarvestService = {
+ async getSummary(farmUuid: string): Promise {
+ const res = await apiClient.get> | Record>(
+ `${PREFIX}/summary/?farm_uuid=${encodeURIComponent(farmUuid)}`
+ )
+ const data = extract(res)
+
+ return {
+ yield_prediction: toKpiCard(data?.yield_prediction ?? data?.yield_prediction_card),
+ yieldPredictionChart: (data?.yieldPredictionChart ?? data?.yield_prediction_chart ?? {}) as Record,
+ harvestPredictionCard: (data?.harvestPredictionCard ?? data?.harvest_prediction_card ?? {}) as Record
+ }
+ },
+}
diff --git a/src/views/dashboards/farm/AlertsPageWrapper.tsx b/src/views/dashboards/farm/AlertsPageWrapper.tsx
new file mode 100644
index 0000000..b4360a6
--- /dev/null
+++ b/src/views/dashboards/farm/AlertsPageWrapper.tsx
@@ -0,0 +1,81 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { useFarmHub } from '@/hooks/useFarmHub'
+
+import Grid from '@mui/material/Grid2'
+import Box from '@mui/material/Box'
+import CircularProgress from '@mui/material/CircularProgress'
+
+import FarmAlertsTracker from '@views/dashboards/farm/FarmAlertsTracker'
+import FarmAlertsTimeline from '@views/dashboards/farm/FarmAlertsTimeline'
+import RecommendationsList from '@views/dashboards/farm/RecommendationsList'
+
+import { farmAlertsService } from '@/libs/api/services/farmAlertsService'
+
+const cardRowSx = {
+ display: 'flex',
+ flexDirection: 'column',
+ minHeight: 380,
+ '& > *': { flex: 1, minHeight: 0 }
+}
+
+const AlertsPageWrapper = () => {
+ const { farmHub } = useFarmHub()
+ const farmUuid = farmHub?.farm_uuid
+ const [tracker, setTracker] = useState>({})
+ const [timeline, setTimeline] = useState>({})
+ const [recommendations, setRecommendations] = useState>({})
+ const [loading, setLoading] = useState(true)
+
+ useEffect(() => {
+ if (!farmUuid) {
+ setTracker({})
+ setTimeline({})
+ setRecommendations({})
+ setLoading(false)
+ return
+ }
+
+ setLoading(true)
+ Promise.all([
+ farmAlertsService.getTracker(farmUuid).catch(() => ({})),
+ farmAlertsService.getTimeline(farmUuid).catch(() => ({})),
+ farmAlertsService.getRecommendations(farmUuid).catch(() => ({}))
+ ])
+ .then(([t, tl, r]) => {
+ setTracker(t)
+ setTimeline(tl)
+ setRecommendations(r)
+ })
+ .finally(() => setLoading(false))
+ }, [farmUuid])
+
+ if (loading) {
+ return (
+
+
+
+ )
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default AlertsPageWrapper
diff --git a/src/views/dashboards/farm/CropHealthPageWrapper.tsx b/src/views/dashboards/farm/CropHealthPageWrapper.tsx
new file mode 100644
index 0000000..73fe6f6
--- /dev/null
+++ b/src/views/dashboards/farm/CropHealthPageWrapper.tsx
@@ -0,0 +1,65 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { useFarmHub } from '@/hooks/useFarmHub'
+
+import Grid from '@mui/material/Grid2'
+import Box from '@mui/material/Box'
+import CircularProgress from '@mui/material/CircularProgress'
+
+import FarmOverviewKPIs from '@views/dashboards/farm/FarmOverviewKPIs'
+import NDVIHealthCard from '@views/dashboards/farm/NDVIHealthCard'
+
+import { cropHealthService } from '@/libs/api/services/cropHealthService'
+
+const cardRowSx = {
+ display: 'flex',
+ flexDirection: 'column',
+ minHeight: 380,
+ '& > *': { flex: 1, minHeight: 0 }
+}
+
+const CropHealthPageWrapper = () => {
+ const { farmHub } = useFarmHub()
+ const farmUuid = farmHub?.farm_uuid
+ const [data, setData] = useState>({})
+ const [loading, setLoading] = useState(true)
+
+ useEffect(() => {
+ if (!farmUuid) {
+ setData({})
+ setLoading(false)
+ return
+ }
+
+ setLoading(true)
+ cropHealthService
+ .getSummary(farmUuid)
+ .then(summary => setData((summary as Record) ?? {}))
+ .catch(() => setData({}))
+ .finally(() => setLoading(false))
+ }, [farmUuid])
+
+ if (loading) {
+ return (
+
+
+
+ )
+ }
+
+ return (
+
+
+
+ } />
+
+
+ } />
+
+
+
+ )
+}
+
+export default CropHealthPageWrapper
diff --git a/src/views/dashboards/farm/EconomicOverviewPageWrapper.tsx b/src/views/dashboards/farm/EconomicOverviewPageWrapper.tsx
new file mode 100644
index 0000000..6c0d233
--- /dev/null
+++ b/src/views/dashboards/farm/EconomicOverviewPageWrapper.tsx
@@ -0,0 +1,62 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { useFarmHub } from '@/hooks/useFarmHub'
+
+import Grid from '@mui/material/Grid2'
+import Box from '@mui/material/Box'
+import CircularProgress from '@mui/material/CircularProgress'
+
+import EconomicOverview from '@views/dashboards/farm/EconomicOverview'
+
+import { economicOverviewService } from '@/libs/api/services/economicOverviewService'
+import type { EconomicOverviewSummary } from '@/libs/api/services/economicOverviewService'
+
+const cardRowSx = {
+ display: 'flex',
+ flexDirection: 'column',
+ minHeight: 380,
+ '& > *': { flex: 1, minHeight: 0 }
+}
+
+const EconomicOverviewPageWrapper = () => {
+ const { farmHub } = useFarmHub()
+ const farmUuid = farmHub?.farm_uuid
+ const [data, setData] = useState({})
+ const [loading, setLoading] = useState(true)
+
+ useEffect(() => {
+ if (!farmUuid) {
+ setData({})
+ setLoading(false)
+ return
+ }
+
+ setLoading(true)
+ economicOverviewService
+ .getSummary(farmUuid)
+ .then(summary => setData(summary ?? {}))
+ .catch(() => setData({}))
+ .finally(() => setLoading(false))
+ }, [farmUuid])
+
+ if (loading) {
+ return (
+
+
+
+ )
+ }
+
+ return (
+
+
+
+ } />
+
+
+
+ )
+}
+
+export default EconomicOverviewPageWrapper
diff --git a/src/views/dashboards/farm/PestRiskPageWrapper.tsx b/src/views/dashboards/farm/PestRiskPageWrapper.tsx
new file mode 100644
index 0000000..3d41210
--- /dev/null
+++ b/src/views/dashboards/farm/PestRiskPageWrapper.tsx
@@ -0,0 +1,62 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { useFarmHub } from '@/hooks/useFarmHub'
+
+import Grid from '@mui/material/Grid2'
+import Box from '@mui/material/Box'
+import CircularProgress from '@mui/material/CircularProgress'
+
+import FarmOverviewKPIs from '@views/dashboards/farm/FarmOverviewKPIs'
+
+import { pestDetectionDomainService } from '@/libs/api/services/pestDetectionDomainService'
+import type { PestRiskSummary } from '@/libs/api/services/pestDetectionDomainService'
+
+const PestRiskPageWrapper = () => {
+ const { farmHub } = useFarmHub()
+ const farmUuid = farmHub?.farm_uuid
+ const [data, setData] = useState({})
+ const [loading, setLoading] = useState(true)
+
+ useEffect(() => {
+ if (!farmUuid) {
+ setData({})
+ setLoading(false)
+ return
+ }
+
+ setLoading(true)
+ pestDetectionDomainService
+ .getRiskSummary(farmUuid)
+ .then(summary => setData(summary ?? {}))
+ .catch(() => setData({}))
+ .finally(() => setLoading(false))
+ }, [farmUuid])
+
+ if (loading) {
+ return (
+
+
+
+ )
+ }
+
+ return (
+
+
+ {data.disease_risk && (
+
+ } />
+
+ )}
+ {data.pest_risk && (
+
+ } />
+
+ )}
+
+
+ )
+}
+
+export default PestRiskPageWrapper
diff --git a/src/views/dashboards/farm/SoilDataDashboardWrapper.tsx b/src/views/dashboards/farm/SoilDataDashboardWrapper.tsx
index 3a1d21a..b8d7315 100644
--- a/src/views/dashboards/farm/SoilDataDashboardWrapper.tsx
+++ b/src/views/dashboards/farm/SoilDataDashboardWrapper.tsx
@@ -1,31 +1,19 @@
'use client'
-// React Imports
import { useEffect, useState } from 'react'
import { useFarmHub } from '@/hooks/useFarmHub'
-// MUI Imports
import Grid from '@mui/material/Grid2'
import Box from '@mui/material/Box'
import CircularProgress from '@mui/material/CircularProgress'
-// Component Imports
import SoilMoistureHeatmap from '@views/dashboards/farm/SoilMoistureHeatmap'
-import SensorValuesList from '@views/dashboards/farm/SensorValuesList'
import SensorComparisonChart from '@views/dashboards/farm/SensorComparisonChart'
import SensorRadarChart from '@views/dashboards/farm/SensorRadarChart'
import AnomalyDetectionCard from '@views/dashboards/farm/AnomalyDetectionCard'
-// Service
-import { farmDashboardService } from '@/libs/api/services/farmDashboardService'
-import type { CardId } from '@views/dashboards/farm/farmDashboardConfig'
-
-/** هر ردیف: آرایهٔ کارتها؛ در هر ردیف فضا مساوی بین گریدها تقسیم میشود (جمع = ۱۲) */
-const SOIL_ROWS: CardId[][] = [
- ['soilMoistureHeatmap'],
- ['sensorValuesList', 'sensorRadarChart'],
- ['sensorComparisonChart', 'anomalyDetectionCard']
-]
+import { soilService } from '@/libs/api/services/soilService'
+import type { SoilSummary } from '@/libs/api/services/soilService'
const cardRowSx = {
display: 'flex',
@@ -34,32 +22,24 @@ const cardRowSx = {
'& > *': { flex: 1, minHeight: 0 }
}
-const CARD_COMPONENTS: Partial }>>> = {
- soilMoistureHeatmap: SoilMoistureHeatmap,
- sensorValuesList: SensorValuesList,
- sensorComparisonChart: SensorComparisonChart,
- sensorRadarChart: SensorRadarChart,
- anomalyDetectionCard: AnomalyDetectionCard
-}
-
const SoilDataDashboardWrapper = () => {
const { farmHub } = useFarmHub()
const farmUuid = farmHub?.farm_uuid
- const [cardsData, setCardsData] = useState>>>({})
+ const [data, setData] = useState({})
const [loading, setLoading] = useState(true)
useEffect(() => {
if (!farmUuid) {
- setCardsData({})
+ setData({})
setLoading(false)
return
}
setLoading(true)
- farmDashboardService
- .getAllCards(farmUuid)
- .then(cards => setCardsData(cards ?? {}))
- .catch(() => setCardsData({}))
+ soilService
+ .getSummary(farmUuid)
+ .then(summary => setData(summary ?? {}))
+ .catch(() => setData({}))
.finally(() => setLoading(false))
}, [farmUuid])
@@ -74,28 +54,20 @@ const SoilDataDashboardWrapper = () => {
return (
- {SOIL_ROWS.map((rowCards, rowIndex) => {
- const sizePerCard = 12 / rowCards.length
- return (
-
- {rowCards.map(cardId => {
- const Component = CARD_COMPONENTS[cardId]
- if (!Component) return null
- return (
-
-
-
- )
- })}
-
- )
- })}
+
+ } />
+
+
+
+ } />
+
+
+ } />
+
+
+
+ } />
+
)
diff --git a/src/views/dashboards/farm/WaterDataDashboardWrapper.tsx b/src/views/dashboards/farm/WaterDataDashboardWrapper.tsx
index 2d328b9..443e2b2 100644
--- a/src/views/dashboards/farm/WaterDataDashboardWrapper.tsx
+++ b/src/views/dashboards/farm/WaterDataDashboardWrapper.tsx
@@ -11,19 +11,11 @@ import CircularProgress from '@mui/material/CircularProgress'
// Component Imports
import FarmWeatherCard from '@views/dashboards/farm/FarmWeatherCard'
-import FarmAlertsTimeline from '@views/dashboards/farm/FarmAlertsTimeline'
import WaterNeedPrediction from '@views/dashboards/farm/WaterNeedPrediction'
-import SensorValuesList from '@views/dashboards/farm/SensorValuesList'
+import FarmOverviewKPIs from '@views/dashboards/farm/FarmOverviewKPIs'
// Service
-import { farmDashboardService } from '@/libs/api/services/farmDashboardService'
-import type { CardId } from '@views/dashboards/farm/farmDashboardConfig'
-
-/** هر ردیف: آرایهٔ کارتها؛ در هر ردیف فضا مساوی بین گریدها تقسیم میشود (جمع = ۱۲) */
-const WATER_ROWS: CardId[][] = [
- ['farmWeatherCard', 'farmAlertsTimeline', 'waterNeedPrediction'],
- ['sensorValuesList']
-]
+import { waterService } from '@/libs/api/services/waterService'
const cardRowSx = {
display: 'flex',
@@ -32,31 +24,24 @@ const cardRowSx = {
'& > *': { flex: 1, minHeight: 0 }
}
-const CARD_COMPONENTS: Partial }>>> = {
- farmWeatherCard: FarmWeatherCard,
- farmAlertsTimeline: FarmAlertsTimeline,
- waterNeedPrediction: WaterNeedPrediction,
- sensorValuesList: SensorValuesList
-}
-
const WaterDataDashboardWrapper = () => {
const { farmHub } = useFarmHub()
const farmUuid = farmHub?.farm_uuid
- const [cardsData, setCardsData] = useState>>>({})
+ const [data, setData] = useState>({})
const [loading, setLoading] = useState(true)
useEffect(() => {
if (!farmUuid) {
- setCardsData({})
+ setData({})
setLoading(false)
return
}
setLoading(true)
- farmDashboardService
- .getAllCards(farmUuid)
- .then(cards => setCardsData(cards ?? {}))
- .catch(() => setCardsData({}))
+ waterService
+ .getSummary(farmUuid)
+ .then(summary => setData((summary as Record) ?? {}))
+ .catch(() => setData({}))
.finally(() => setLoading(false))
}, [farmUuid])
@@ -71,28 +56,19 @@ const WaterDataDashboardWrapper = () => {
return (
- {WATER_ROWS.map((rowCards, rowIndex) => {
- const sizePerCard = 12 / rowCards.length
- return (
-
- {rowCards.map(cardId => {
- const Component = CARD_COMPONENTS[cardId]
- if (!Component) return null
- return (
-
-
-
- )
- })}
-
- )
- })}
+ {data.water_stress_index != null && (
+
+ } />
+
+ )}
+
+
+ } />
+
+
+ } />
+
+
)
diff --git a/src/views/dashboards/farm/YieldHarvestPageWrapper.tsx b/src/views/dashboards/farm/YieldHarvestPageWrapper.tsx
new file mode 100644
index 0000000..2ba74cb
--- /dev/null
+++ b/src/views/dashboards/farm/YieldHarvestPageWrapper.tsx
@@ -0,0 +1,74 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { useFarmHub } from '@/hooks/useFarmHub'
+
+import Grid from '@mui/material/Grid2'
+import Box from '@mui/material/Box'
+import CircularProgress from '@mui/material/CircularProgress'
+
+import HarvestPredictionCard from '@views/dashboards/farm/HarvestPredictionCard'
+import YieldPredictionChart from '@views/dashboards/farm/YieldPredictionChart'
+import FarmOverviewKPIs from '@views/dashboards/farm/FarmOverviewKPIs'
+
+import { yieldHarvestService } from '@/libs/api/services/yieldHarvestService'
+import type { YieldHarvestSummary } from '@/libs/api/services/yieldHarvestService'
+
+const cardRowSx = {
+ display: 'flex',
+ flexDirection: 'column',
+ minHeight: 380,
+ '& > *': { flex: 1, minHeight: 0 }
+}
+
+const YieldHarvestPageWrapper = () => {
+ const { farmHub } = useFarmHub()
+ const farmUuid = farmHub?.farm_uuid
+ const [data, setData] = useState({})
+ const [loading, setLoading] = useState(true)
+
+ useEffect(() => {
+ if (!farmUuid) {
+ setData({})
+ setLoading(false)
+ return
+ }
+
+ setLoading(true)
+ yieldHarvestService
+ .getSummary(farmUuid)
+ .then(summary => setData(summary ?? {}))
+ .catch(() => setData({}))
+ .finally(() => setLoading(false))
+ }, [farmUuid])
+
+ if (loading) {
+ return (
+
+
+
+ )
+ }
+
+ return (
+
+
+ {data.yield_prediction && (
+
+ } />
+
+ )}
+
+
+ } />
+
+
+ } />
+
+
+
+
+ )
+}
+
+export default YieldHarvestPageWrapper