From 51175ffac2e1d590d3bd1f73d9b2c6e72cd99078 Mon Sep 17 00:00:00 2001 From: Mohammad Sajad Pourajam Date: Thu, 19 Feb 2026 16:58:30 +0330 Subject: [PATCH] Enhance farm dashboard components by refactoring to accept data props, improving API integration for dynamic data rendering. Updated FarmDashboardWrapper to fetch and manage card data, ensuring components like EconomicOverview, AnomalyDetectionCard, and others utilize the new data structure. Removed hardcoded values and added error handling for better resilience. --- farm_dashboard.json | 1 + src/libs/api/services/farmDashboardService.ts | 44 ++++++- .../dashboards/farm/AnomalyDetectionCard.tsx | 11 +- .../dashboards/farm/EconomicOverview.tsx | 66 +++++----- .../dashboards/farm/FarmAlertsTimeline.tsx | 35 +---- .../dashboards/farm/FarmAlertsTracker.tsx | 31 +++-- .../dashboards/farm/FarmDashboardWrapper.tsx | 28 ++-- .../dashboards/farm/FarmOverviewKPIs.tsx | 120 +++++------------- src/views/dashboards/farm/FarmWeatherCard.tsx | 25 +++- .../dashboards/farm/HarvestPredictionCard.tsx | 25 +++- src/views/dashboards/farm/NDVIHealthCard.tsx | 50 +++++--- .../dashboards/farm/RecommendationsList.tsx | 36 ++---- .../dashboards/farm/SensorComparisonChart.tsx | 21 +-- .../dashboards/farm/SensorRadarChart.tsx | 17 ++- .../dashboards/farm/SensorValuesList.tsx | 20 ++- .../dashboards/farm/SoilMoistureHeatmap.tsx | 26 ++-- .../dashboards/farm/WaterNeedPrediction.tsx | 23 +++- .../dashboards/farm/YieldPredictionChart.tsx | 67 +++++----- 18 files changed, 333 insertions(+), 313 deletions(-) create mode 100644 farm_dashboard.json diff --git a/farm_dashboard.json b/farm_dashboard.json new file mode 100644 index 0000000..6556e80 --- /dev/null +++ b/farm_dashboard.json @@ -0,0 +1 @@ +{"info":{"name":"Farm Dashboard","schema":"https://schema.getpostman.com/json/collection/v2.1.0/collection.json","description":"Farm Dashboard API. GET/PATCH config (disabled_card_ids, row_order, enable_drag_reorder). GET all cards. Static mock data only. No database."},"item":[{"name":"Get config","request":{"method":"GET","header":[{"key":"Content-Type","value":"application/json"}],"url":"{{baseUrl}}/api/farm-dashboard-config/","description":"Get dashboard config: disabled_card_ids, row_order, enable_drag_reorder."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"code\": 200,\n \"msg\": \"OK\",\n \"data\": {\n \"disabled_card_ids\": [\"farmWeatherCard\", \"sensorRadarChart\"],\n \"row_order\": [\"overviewKpis\", \"weatherAlerts\", \"sensorMonitoring\", \"sensorCharts\", \"alertsWater\", \"predictions\", \"soilHeatmap\", \"ndviRecommendations\", \"economic\"],\n \"enable_drag_reorder\": true\n }\n}"}]},{"name":"Patch config (disable card)","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n \"disabled_card_ids\": [\"farmWeatherCard\", \"sensorRadarChart\"]\n}"},"url":"{{baseUrl}}/api/farm-dashboard-config/","description":"PATCH to update disabled_card_ids. Input is ignored; returns static config."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"code\": 200,\n \"msg\": \"OK\",\n \"data\": {\n \"disabled_card_ids\": [\"farmWeatherCard\", \"sensorRadarChart\"],\n \"row_order\": [\"overviewKpis\", \"weatherAlerts\", \"sensorMonitoring\", \"sensorCharts\", \"alertsWater\", \"predictions\", \"soilHeatmap\", \"ndviRecommendations\", \"economic\"],\n \"enable_drag_reorder\": true\n }\n}"}]},{"name":"Patch config (row order)","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n \"row_order\": [\"overviewKpis\", \"weatherAlerts\", \"sensorMonitoring\", \"predictions\", \"sensorCharts\", \"alertsWater\", \"soilHeatmap\", \"ndviRecommendations\", \"economic\"]\n}"},"url":"{{baseUrl}}/api/farm-dashboard-config/","description":"PATCH to update row_order. Input is ignored; returns static config."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"code\": 200,\n \"msg\": \"OK\",\n \"data\": {\n \"disabled_card_ids\": [\"farmWeatherCard\", \"sensorRadarChart\"],\n \"row_order\": [\"overviewKpis\", \"weatherAlerts\", \"sensorMonitoring\", \"sensorCharts\", \"alertsWater\", \"predictions\", \"soilHeatmap\", \"ndviRecommendations\", \"economic\"],\n \"enable_drag_reorder\": true\n }\n}"}]},{"name":"Patch config (enable drag reorder)","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n \"enable_drag_reorder\": false\n}"},"url":"{{baseUrl}}/api/farm-dashboard-config/","description":"PATCH to update enable_drag_reorder. Input is ignored; returns static config."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"code\": 200,\n \"msg\": \"OK\",\n \"data\": {\n \"disabled_card_ids\": [\"farmWeatherCard\", \"sensorRadarChart\"],\n \"row_order\": [\"overviewKpis\", \"weatherAlerts\", \"sensorMonitoring\", \"sensorCharts\", \"alertsWater\", \"predictions\", \"soilHeatmap\", \"ndviRecommendations\", \"economic\"],\n \"enable_drag_reorder\": true\n }\n}"}]},{"name":"Get all cards","request":{"method":"GET","header":[{"key":"Content-Type","value":"application/json"}],"url":"{{baseUrl}}/api/farm-dashboard/","description":"Get unified response with all 15 card payloads."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"code\": 200,\n \"msg\": \"OK\",\n \"data\": {\n \"farmOverviewKpis\": {},\n \"farmWeatherCard\": {},\n \"farmAlertsTracker\": {},\n \"sensorValuesList\": {},\n \"sensorRadarChart\": {},\n \"sensorComparisonChart\": {},\n \"anomalyDetectionCard\": {},\n \"farmAlertsTimeline\": {},\n \"waterNeedPrediction\": {},\n \"harvestPredictionCard\": {},\n \"yieldPredictionChart\": {},\n \"soilMoistureHeatmap\": {},\n \"ndviHealthCard\": {},\n \"recommendationsList\": {},\n \"economicOverview\": {}\n }\n}"}]},{"name":"Get all cards (cards path)","request":{"method":"GET","header":[{"key":"Content-Type","value":"application/json"}],"url":"{{baseUrl}}/api/farm-dashboard/cards/","description":"Get unified response with all 15 card payloads. Same as base path."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"code\": 200,\n \"msg\": \"OK\",\n \"data\": {\n \"farmOverviewKpis\": {},\n \"farmWeatherCard\": {},\n \"farmAlertsTracker\": {},\n \"sensorValuesList\": {},\n \"sensorRadarChart\": {},\n \"sensorComparisonChart\": {},\n \"anomalyDetectionCard\": {},\n \"farmAlertsTimeline\": {},\n \"waterNeedPrediction\": {},\n \"harvestPredictionCard\": {},\n \"yieldPredictionChart\": {},\n \"soilMoistureHeatmap\": {},\n \"ndviHealthCard\": {},\n \"recommendationsList\": {},\n \"economicOverview\": {}\n }\n}"}]}],"variable":[{"key":"baseUrl","value":"http://localhost:8000"}]} diff --git a/src/libs/api/services/farmDashboardService.ts b/src/libs/api/services/farmDashboardService.ts index f63b65d..4949b89 100644 --- a/src/libs/api/services/farmDashboardService.ts +++ b/src/libs/api/services/farmDashboardService.ts @@ -1,11 +1,13 @@ /** - * Farm Dashboard Config Service - * Handles API calls for dashboard customization (disabled cards, row order). - * Authenticated user required. + * Farm Dashboard Service + * Handles API calls for dashboard config and card data. + * - Config: disabled cards, row order, drag reorder + * - Cards: all 15 card payloads from /api/farm-dashboard/ */ import { apiClient } from '../client' import type { FarmDashboardConfig } from '@/views/dashboards/farm/farmDashboardConfig' +import type { CardId } from '@/views/dashboards/farm/farmDashboardConfig' export interface ApiResponse { code: number @@ -19,6 +21,25 @@ export interface FarmDashboardConfigResponse { enable_drag_reorder?: boolean } +/** API response shape for /api/farm-dashboard/ - each key matches CardId */ +export interface FarmDashboardCardsResponse { + farmOverviewKpis?: Record + farmWeatherCard?: Record + farmAlertsTracker?: Record + sensorValuesList?: Record + sensorRadarChart?: Record + sensorComparisonChart?: Record + anomalyDetectionCard?: Record + farmAlertsTimeline?: Record + waterNeedPrediction?: Record + harvestPredictionCard?: Record + yieldPredictionChart?: Record + soilMoistureHeatmap?: Record + ndviHealthCard?: Record + recommendationsList?: Record + economicOverview?: Record +} + const STORAGE_KEY = 'farm_dashboard_config' /** @@ -114,5 +135,22 @@ export const farmDashboardService = { } throw err } + }, + + /** + * Get all dashboard card data from API + * Response: { code: 200, msg: "OK", data: { farmOverviewKpis, farmWeatherCard, ... } } + */ + async getAllCards(): Promise>>> { + try { + const response = await apiClient.get>('/api/farm-dashboard/') + const raw = response?.data ?? response + if (raw && typeof raw === 'object') { + return raw as Partial>> + } + return {} + } catch { + return {} + } } } diff --git a/src/views/dashboards/farm/AnomalyDetectionCard.tsx b/src/views/dashboards/farm/AnomalyDetectionCard.tsx index 51bdae3..396526b 100644 --- a/src/views/dashboards/farm/AnomalyDetectionCard.tsx +++ b/src/views/dashboards/farm/AnomalyDetectionCard.tsx @@ -18,12 +18,13 @@ type AnomalyItem = { severity: 'warning' | 'error' } -const anomalies: AnomalyItem[] = [ - { 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' } -] +interface AnomalyDetectionCardProps { + data?: Record +} + +const AnomalyDetectionCard = ({ data }: AnomalyDetectionCardProps) => { + const anomalies = (data?.anomalies as AnomalyItem[] | undefined) ?? [] -const AnomalyDetectionCard = () => { return ( import('@/libs/styles/AppReactApexCharts')) -// Vars - Cost breakdown (stacked bar style) -const series = [ - { name: 'Water Cost', data: [120, 115, 110, 125, 118, 122] }, - { name: 'Fertilizer', data: [80, 85, 90, 75, 82, 78] } -] - type EconomicItem = { title: string value: string @@ -36,14 +30,14 @@ type EconomicItem = { avatarColor: 'primary' | 'success' | 'info' | 'warning' } -const economicData: EconomicItem[] = [ - { 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' } -] +interface EconomicOverviewProps { + data?: Record +} -const EconomicOverview = () => { +const EconomicOverview = ({ data }: EconomicOverviewProps) => { + const economicData = (data?.economicData as EconomicItem[] | undefined) ?? [] + const chartSeries = (data?.chartSeries as Array<{ name: string; data: number[] }>) ?? [] + const chartCategories = (data?.chartCategories as string[]) ?? ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'] const theme = useTheme() const options: ApexOptions = { @@ -69,7 +63,7 @@ const EconomicOverview = () => { padding: { top: -40, left: -10, right: 0, bottom: -15 } }, xaxis: { - categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], + categories: chartCategories, labels: { show: false }, axisTicks: { show: false }, axisBorder: { show: false } @@ -85,27 +79,31 @@ const EconomicOverview = () => { action={} /> - - {economicData.map((item, index) => ( - -
- - - -
- {item.value} - - {item.title} - - - {item.subtitle} - + {economicData.length > 0 && ( + + {economicData.map((item, index) => ( + +
+ + + +
+ {item.value} + + {item.title} + + + {item.subtitle} + +
-
- - ))} - - + + ))} + + )} + {chartSeries.length > 0 && ( + + )} ) diff --git a/src/views/dashboards/farm/FarmAlertsTimeline.tsx b/src/views/dashboards/farm/FarmAlertsTimeline.tsx index 3f1938c..3c4ef45 100644 --- a/src/views/dashboards/farm/FarmAlertsTimeline.tsx +++ b/src/views/dashboards/farm/FarmAlertsTimeline.tsx @@ -34,36 +34,13 @@ type AlertItem = { color: 'primary' | 'warning' | 'error' | 'info' | 'success' } -const alerts: AlertItem[] = [ - { - 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' - } -] +interface FarmAlertsTimelineProps { + data?: Record +} + +const FarmAlertsTimeline = ({ data }: FarmAlertsTimelineProps) => { + const alerts = (data?.alerts as AlertItem[] | undefined) ?? [] -const FarmAlertsTimeline = () => { return ( import('@/libs/styles/AppReactApexChart type AlertStatType = { title: string - subtitle: string + count: string avatarIcon: string avatarColor?: ThemeColor } -const data: AlertStatType[] = [ - { title: 'Water Shortage', subtitle: '2', avatarColor: 'error', avatarIcon: 'tabler-droplet-half-2' }, - { title: 'Fungal Risk', subtitle: '1', avatarColor: 'warning', avatarIcon: 'tabler-mushroom' }, - { title: 'Frost Alert', subtitle: '0', avatarColor: 'info', avatarIcon: 'tabler-snowflake' } -] +interface FarmAlertsTrackerProps { + data?: Record +} -const FarmAlertsTracker = () => { +const FarmAlertsTracker = ({ data }: FarmAlertsTrackerProps) => { + const alertStats = (data?.alertStats as AlertStatType[] | undefined) ?? [] + const totalAlerts = (data?.totalAlerts as number | undefined) ?? 0 + const radialBarValue = (data?.radialBarValue as number | undefined) ?? 30 const theme = useTheme() const disabledText = 'var(--mui-palette-text-disabled)' @@ -77,7 +78,7 @@ const FarmAlertsTracker = () => { value: { offsetY: 8, fontWeight: 500, - formatter: () => '3', + formatter: () => String(totalAlerts), color: 'var(--mui-palette-text-primary)', fontFamily: theme.typography.fontFamily, fontSize: theme.typography.h2.fontSize as string @@ -100,11 +101,11 @@ const FarmAlertsTracker = () => {
- 3 + {totalAlerts} Total Alerts
- {data.map((item, index) => ( + {alertStats.map((item, index) => (
@@ -113,13 +114,19 @@ const FarmAlertsTracker = () => { {item.title} - {item.subtitle} + {item.count}
))}
- +
) diff --git a/src/views/dashboards/farm/FarmDashboardWrapper.tsx b/src/views/dashboards/farm/FarmDashboardWrapper.tsx index cfbd9ec..d99edca 100644 --- a/src/views/dashboards/farm/FarmDashboardWrapper.tsx +++ b/src/views/dashboards/farm/FarmDashboardWrapper.tsx @@ -91,13 +91,18 @@ function mergeRowOrderAfterDrag( const FarmDashboardWrapper = () => { const { setSlotContent } = useContext(NavbarSlotContext) const [config, setConfig] = useState(DEFAULT_FARM_DASHBOARD_CONFIG) + const [cardsData, setCardsData] = useState>>>({}) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const disabledSet = new Set(config.disabledCardIds) const hasVisibleCard = useCallback( - (rowId: RowId) => ROW_CARDS[rowId].some(cardId => !disabledSet.has(cardId)), + (rowId: string) => { + const cards = ROW_CARDS[rowId as RowId] + if (!Array.isArray(cards)) return false + return cards.some(cardId => !disabledSet.has(cardId)) + }, [config.disabledCardIds] ) @@ -109,15 +114,18 @@ const FarmDashboardWrapper = () => { }) useEffect(() => { - farmDashboardService - .getConfig() - .then(data => { + Promise.all([farmDashboardService.getConfig(), farmDashboardService.getAllCards()]) + .then(([configData, cards]) => { + const validRowOrder = (configData.rowOrder ?? []).filter( + (id): id is RowId => id in ROW_CARDS + ) const merged: FarmDashboardConfig = { - disabledCardIds: data.disabledCardIds ?? [], - rowOrder: data.rowOrder?.length ? data.rowOrder : [...ROW_IDS], - enableDragReorder: data.enableDragReorder ?? true + disabledCardIds: configData.disabledCardIds ?? [], + rowOrder: validRowOrder.length ? validRowOrder : [...ROW_IDS], + enableDragReorder: configData.enableDragReorder ?? true } setConfig(merged) + setCardsData(cards ?? {}) }) .catch(() => setConfig(DEFAULT_FARM_DASHBOARD_CONFIG)) .finally(() => setLoading(false)) @@ -229,7 +237,9 @@ const FarmDashboardWrapper = () => { )} - {isOverviewRow && cards.includes('farmOverviewKpis') && } + {isOverviewRow && cards.includes('farmOverviewKpis') && ( + + )} {!isOverviewRow && cards.map((cardId: CardId) => { const size = CARD_GRID_SIZE[cardId] @@ -237,7 +247,7 @@ const FarmDashboardWrapper = () => { if (!Component) return null return ( - + ) })} diff --git a/src/views/dashboards/farm/FarmOverviewKPIs.tsx b/src/views/dashboards/farm/FarmOverviewKPIs.tsx index 927a418..5b01e76 100644 --- a/src/views/dashboards/farm/FarmOverviewKPIs.tsx +++ b/src/views/dashboards/farm/FarmOverviewKPIs.tsx @@ -6,93 +6,43 @@ import Grid from '@mui/material/Grid2' // Component Imports import CardStatsVertical from '@components/card-statistics/Vertical' -const FarmOverviewKPIs = () => { +type KpiItem = { + id: string + title: string + subtitle: string + stats: string + avatarColor?: string + avatarIcon?: string + chipText?: string + chipColor?: string +} + +interface FarmOverviewKPIsProps { + data?: Record +} + +const FarmOverviewKPIs = ({ data }: FarmOverviewKPIsProps) => { + const kpis = (data?.kpis as KpiItem[] | undefined) ?? [] + if (kpis.length === 0) return null + return ( <> - - - - - - - - - - - - - - - - - - + {kpis.map((kpi) => ( + + + + ))} ) } diff --git a/src/views/dashboards/farm/FarmWeatherCard.tsx b/src/views/dashboards/farm/FarmWeatherCard.tsx index 1f7e5b4..9cde823 100644 --- a/src/views/dashboards/farm/FarmWeatherCard.tsx +++ b/src/views/dashboards/farm/FarmWeatherCard.tsx @@ -19,10 +19,20 @@ import OptionMenu from '@core/components/option-menu' // Styled Component Imports const AppReactApexCharts = dynamic(() => import('@/libs/styles/AppReactApexCharts')) -// Vars - Mock weather data (temp variation through day) -const series = [{ data: [18, 22, 26, 28, 25, 20, 18] }] +interface FarmWeatherCardProps { + data?: Record +} -const FarmWeatherCard = () => { +const FarmWeatherCard = ({ data }: FarmWeatherCardProps) => { + const temperature = data?.temperature ?? 24 + const condition = (data?.condition as string) ?? '' + const humidity = data?.humidity ?? 45 + const windSpeed = data?.windSpeed ?? 12 + const windUnit = (data?.windUnit as string) ?? 'km/h' + const unit = (data?.unit as string) ?? '°C' + const chartData = data?.chartData as { labels?: string[]; series?: number[][] } | undefined + const seriesData = chartData?.series?.[0] ?? [18, 22, 26, 28, 25, 20, 18] + const series = [{ data: seriesData }] const theme = useTheme() const infoColor = theme.palette.info.main @@ -77,16 +87,19 @@ const FarmWeatherCard = () => { } />
- 24°C + + {temperature} + {unit} + - Humid: 45% | Wind: 12 km/h + Humid: {humidity}% | Wind: {windSpeed} {windUnit}
diff --git a/src/views/dashboards/farm/HarvestPredictionCard.tsx b/src/views/dashboards/farm/HarvestPredictionCard.tsx index 7f940a5..a0326c3 100644 --- a/src/views/dashboards/farm/HarvestPredictionCard.tsx +++ b/src/views/dashboards/farm/HarvestPredictionCard.tsx @@ -11,7 +11,16 @@ import Chip from '@mui/material/Chip' import CustomAvatar from '@core/components/mui/Avatar' import OptionMenu from '@core/components/option-menu' -const HarvestPredictionCard = () => { +interface HarvestPredictionCardProps { + data?: Record +} + +const HarvestPredictionCard = ({ data }: HarvestPredictionCardProps) => { + const harvestDate = (data?.dateFormatted as string) ?? '' + const daysUntil = (data?.daysUntil as number | undefined) ?? 0 + const daysLeftFormatted = daysUntil > 0 ? `${daysUntil} days` : '' + const description = (data?.description as string) ?? '' + return ( { />
- Oct 15, 2025 - + {harvestDate} + {daysLeftFormatted && ( + + )}
- - Based on current GDD accumulation and weather forecast. Optimal harvest window: Oct 12-18. - + {description && ( + + {description} + + )}
) diff --git a/src/views/dashboards/farm/NDVIHealthCard.tsx b/src/views/dashboards/farm/NDVIHealthCard.tsx index 5b78fa3..70d3c84 100644 --- a/src/views/dashboards/farm/NDVIHealthCard.tsx +++ b/src/views/dashboards/farm/NDVIHealthCard.tsx @@ -21,7 +21,14 @@ import CustomAvatar from '@core/components/mui/Avatar' // Styled Component Imports const AppReactApexCharts = dynamic(() => import('@/libs/styles/AppReactApexCharts')) -const NDVIHealthCard = () => { +interface NDVIHealthCardProps { + data?: Record +} + +const NDVIHealthCard = ({ data }: NDVIHealthCardProps) => { + const ndviIndex = (data?.ndviIndex as number | undefined) ?? 0 + const healthData = + (data?.healthData as Array<{ title: string; value: string; color: string; icon: string }>) ?? [] const theme = useTheme() const successColor = theme.palette.success.main const disabledText = 'var(--mui-palette-text-disabled)' @@ -72,10 +79,7 @@ const NDVIHealthCard = () => { } } - const healthData = [ - { title: 'Nitrogen Stress', value: 'Low', color: 'success', icon: 'tabler-leaf' }, - { title: 'Crop Health', value: 'Good', color: 'success', icon: 'tabler-plant' } - ] + const ndviPercent = (typeof ndviIndex === 'number' ? ndviIndex : parseFloat(String(ndviIndex)) || 0) * 100 return ( @@ -87,23 +91,31 @@ const NDVIHealthCard = () => { />
- 0.78 + {ndviIndex} NDVI Index (0-1) -
- {healthData.map((item, index) => ( -
- - - -
- {item.title} - + {healthData.length > 0 && ( +
+ {healthData.map((item, index) => ( +
+ + + +
+ {item.title} + +
-
- ))} -
+ ))} +
+ )}
- + ) diff --git a/src/views/dashboards/farm/RecommendationsList.tsx b/src/views/dashboards/farm/RecommendationsList.tsx index e2921cd..70dd431 100644 --- a/src/views/dashboards/farm/RecommendationsList.tsx +++ b/src/views/dashboards/farm/RecommendationsList.tsx @@ -20,34 +20,14 @@ type RecommendationType = { avatarColor: 'primary' | 'info' | 'success' | 'warning' | 'error' } -const data: RecommendationType[] = [ - { - 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' - } -] +interface RecommendationsListProps { + data?: Record +} + +const RecommendationsList = ({ data }: RecommendationsListProps) => { + const recommendations = (data?.recommendations as RecommendationType[] | undefined) ?? [] + if (recommendations.length === 0) return null -const RecommendationsList = () => { return ( { action={} /> - {data.map((item, index) => ( + {recommendations.map((item, index) => (
diff --git a/src/views/dashboards/farm/SensorComparisonChart.tsx b/src/views/dashboards/farm/SensorComparisonChart.tsx index 18024d8..1e73ad7 100644 --- a/src/views/dashboards/farm/SensorComparisonChart.tsx +++ b/src/views/dashboards/farm/SensorComparisonChart.tsx @@ -16,14 +16,17 @@ import type { ApexOptions } from 'apexcharts' // Styled Component Imports const AppReactApexCharts = dynamic(() => import('@/libs/styles/AppReactApexCharts')) -// Vars - Soil moisture: today vs last week (7 days) -const series = [ - { name: 'Today', data: [42, 45, 48, 52, 50, 48, 46] }, - { name: 'Last Week', data: [38, 40, 42, 45, 43, 40, 38] } -] +interface SensorComparisonChartProps { + data?: Record +} -const SensorComparisonChart = () => { +const SensorComparisonChart = ({ data }: SensorComparisonChartProps) => { + const series = (data?.series as Array<{ name: string; data: number[] }>) ?? [] + const categories = (data?.categories as string[]) ?? ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + const currentValue = data?.currentValue ?? 48 + const vsLastWeek = (data?.vsLastWeek as string) ?? '+5% vs last week' const theme = useTheme() + if (series.length === 0) return null const options: ApexOptions = { chart: { @@ -46,7 +49,7 @@ const SensorComparisonChart = () => { yaxis: { lines: { show: true } } }, xaxis: { - categories: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], + categories, labels: { style: { colors: 'var(--mui-palette-text-disabled)' } }, @@ -77,9 +80,9 @@ const SensorComparisonChart = () => { />
- 48% + {currentValue}% - +5% vs last week + {vsLastWeek}
diff --git a/src/views/dashboards/farm/SensorRadarChart.tsx b/src/views/dashboards/farm/SensorRadarChart.tsx index eb561e1..35bb222 100644 --- a/src/views/dashboards/farm/SensorRadarChart.tsx +++ b/src/views/dashboards/farm/SensorRadarChart.tsx @@ -18,18 +18,17 @@ import OptionMenu from '@core/components/option-menu' // Styled Component Imports const AppReactApexCharts = dynamic(() => import('@/libs/styles/AppReactApexCharts')) -// Vars - Today vs ideal ranges (normalized 0-100) -const series = [ - { name: 'Today', data: [75, 65, 80, 70, 85, 60] }, - { name: 'Ideal', data: [80, 70, 75, 75, 90, 50] } -] +interface SensorRadarChartProps { + data?: Record +} -const labels = ['Temp', 'Humidity', 'pH', 'EC', 'Light', 'Wind'] - -const SensorRadarChart = () => { +const SensorRadarChart = ({ data }: SensorRadarChartProps) => { + const series = (data?.series as Array<{ name: string; data: number[] }>) ?? [] + const labels = (data?.labels as string[]) ?? [] const theme = useTheme() const textDisabled = 'var(--mui-palette-text-disabled)' const divider = 'var(--mui-palette-divider)' + if (series.length === 0) return null const options: ApexOptions = { chart: { @@ -61,7 +60,7 @@ const SensorRadarChart = () => { show: true, style: { fontSize: '13px', - colors: Array(6).fill(textDisabled) + colors: Array(labels.length || 6).fill(textDisabled) } } }, diff --git a/src/views/dashboards/farm/SensorValuesList.tsx b/src/views/dashboards/farm/SensorValuesList.tsx index c55ec0f..f7b9e5f 100644 --- a/src/views/dashboards/farm/SensorValuesList.tsx +++ b/src/views/dashboards/farm/SensorValuesList.tsx @@ -20,18 +20,14 @@ type SensorDataType = { unit: string } -const data: SensorDataType[] = [ - { title: '28°C', subtitle: 'Air Temperature', trendNumber: 2.1, unit: '°C' }, - { title: '24°C', subtitle: 'Soil Temperature', trendNumber: -0.5, trend: 'negative', unit: '°C' }, - { title: '65%', subtitle: 'Air Humidity', trendNumber: 3.2, unit: '%' }, - { title: '42%', subtitle: 'Soil Moisture (10cm)', trendNumber: -1.8, trend: 'negative', unit: '%' }, - { title: '6.8', subtitle: 'Soil pH', trendNumber: 0.2, unit: 'pH' }, - { title: '1.2', subtitle: 'EC (dS/m)', trendNumber: 0.1, unit: 'dS/m' }, - { title: '850', subtitle: 'Light Intensity (lux)', trendNumber: 15.3, unit: 'lux' }, - { title: '12', subtitle: 'Wind Speed (km/h)', trendNumber: -2.4, trend: 'negative', unit: 'km/h' } -] +interface SensorValuesListProps { + data?: Record +} + +const SensorValuesList = ({ data }: SensorValuesListProps) => { + const sensors = (data?.sensors as SensorDataType[] | undefined) ?? [] + if (sensors.length === 0) return null -const SensorValuesList = () => { return ( { action={} /> - {data.map((item, index) => ( + {sensors.map((item, index) => (
diff --git a/src/views/dashboards/farm/SoilMoistureHeatmap.tsx b/src/views/dashboards/farm/SoilMoistureHeatmap.tsx index 66b3ed8..bde22af 100644 --- a/src/views/dashboards/farm/SoilMoistureHeatmap.tsx +++ b/src/views/dashboards/farm/SoilMoistureHeatmap.tsx @@ -15,20 +15,24 @@ import type { ApexOptions } from 'apexcharts' // Styled Component Imports const AppReactApexCharts = dynamic(() => import('@/libs/styles/AppReactApexCharts')) -// Generate soil moisture data: rows = field zones (Z1-Z7), cols = hours (6am-6pm) -const zones = ['Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'Z6', 'Z7'] -const hours = ['6h', '8h', '10h', '12h', '14h', '16h', '18h'] +interface HeatmapDataPoint { + x: string + y: number +} -const series = zones.map((zone, i) => ({ - name: zone, - data: hours.map((h, j) => ({ - x: h, - y: Math.floor(Math.random() * 40) + 35 - })) -})) +interface HeatmapSeries { + name: string + data: HeatmapDataPoint[] +} -const SoilMoistureHeatmap = () => { +interface SoilMoistureHeatmapProps { + data?: Record +} + +const SoilMoistureHeatmap = ({ data }: SoilMoistureHeatmapProps) => { + const series = (data?.series as HeatmapSeries[]) ?? [] const theme = useTheme() + if (series.length === 0) return null const options: ApexOptions = { chart: { diff --git a/src/views/dashboards/farm/WaterNeedPrediction.tsx b/src/views/dashboards/farm/WaterNeedPrediction.tsx index 6186438..42ba317 100644 --- a/src/views/dashboards/farm/WaterNeedPrediction.tsx +++ b/src/views/dashboards/farm/WaterNeedPrediction.tsx @@ -19,12 +19,19 @@ import OptionMenu from '@core/components/option-menu' // Styled Component Imports const AppReactApexCharts = dynamic(() => import('@/libs/styles/AppReactApexCharts')) -// Vars - 7-day water need prediction (m³) -const series = [{ name: 'Water Need', data: [420, 450, 480, 460, 490, 510, 480] }] +interface WaterNeedPredictionProps { + data?: Record +} -const WaterNeedPrediction = () => { +const WaterNeedPrediction = ({ data }: WaterNeedPredictionProps) => { + const series = (data?.series as Array<{ name: string; data: number[] }>) ?? [] + const categories = (data?.categories as string[]) ?? ['Day 1', 'Day 2', 'Day 3', 'Day 4', 'Day 5', 'Day 6', 'Day 7'] + const totalNext7Days = data?.totalNext7Days ?? 0 + const unit = (data?.unit as string) ?? 'm³' + const totalFormatted = typeof totalNext7Days === 'number' ? totalNext7Days.toLocaleString() : String(totalNext7Days) const theme = useTheme() const primaryColor = theme.palette.primary.main + if (series.length === 0) return null const options: ApexOptions = { chart: { @@ -48,7 +55,7 @@ const WaterNeedPrediction = () => { xaxis: { lines: { show: false } } }, xaxis: { - categories: ['Day 1', 'Day 2', 'Day 3', 'Day 4', 'Day 5', 'Day 6', 'Day 7'], + categories, labels: { style: { colors: 'var(--mui-palette-text-disabled)' } }, axisBorder: { show: false }, axisTicks: { show: false } @@ -56,11 +63,11 @@ const WaterNeedPrediction = () => { yaxis: { labels: { style: { colors: 'var(--mui-palette-text-disabled)' }, - formatter: (val: number) => `${val} m³` + formatter: (val: number) => `${val} ${unit}` } }, tooltip: { - y: { formatter: (val: number) => `${val} m³` } + y: { formatter: (val: number) => `${val} ${unit}` } } } @@ -73,7 +80,9 @@ const WaterNeedPrediction = () => { />
- 3,290 m³ + + {totalFormatted} {unit} + Total next 7 days diff --git a/src/views/dashboards/farm/YieldPredictionChart.tsx b/src/views/dashboards/farm/YieldPredictionChart.tsx index b2e39ce..6c23b59 100644 --- a/src/views/dashboards/farm/YieldPredictionChart.tsx +++ b/src/views/dashboards/farm/YieldPredictionChart.tsx @@ -21,14 +21,26 @@ import CustomAvatar from '@core/components/mui/Avatar' // Styled Component Imports const AppReactApexCharts = dynamic(() => import('@/libs/styles/AppReactApexCharts')) -// Vars - Yield comparison: this year vs last year (tons per month) -const 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] } -] +type SummaryItem = { + title: string + subtitle: string + amount: string + avatarColor: string + avatarIcon: string +} -const YieldPredictionChart = () => { +interface YieldPredictionChartProps { + data?: Record +} + +const YieldPredictionChart = ({ data }: YieldPredictionChartProps) => { + const series = (data?.series as Array<{ name: string; data: number[] }>) ?? [] + const categories = + (data?.categories as string[]) ?? + ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + const summary = (data?.summary as SummaryItem[]) ?? [] const theme = useTheme() + if (series.length === 0) return null const options: ApexOptions = { chart: { @@ -48,7 +60,7 @@ const YieldPredictionChart = () => { strokeDashArray: 4 }, xaxis: { - categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + categories, labels: { style: { colors: 'var(--mui-palette-text-disabled)' } } }, yaxis: { @@ -62,11 +74,6 @@ const YieldPredictionChart = () => { } } - const summaryData = [ - { 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' } - ] - return ( { /> -
- {summaryData.map((item, index) => ( -
- - - -
-
- - {item.title} + {summary.length > 0 && ( +
+ {summary.map((item, index) => ( +
+ + + +
+
+ + {item.title} + + {item.subtitle} +
+ + {item.amount} - {item.subtitle}
- - {item.amount} -
-
- ))} -
+ ))} +
+ )} )