'use client' // React Imports import type { RefObject } from 'react' import { useEffect, useMemo, useState, useCallback, useContext } from 'react' import { useTranslations } from 'next-intl' // Context Imports import NavbarSlotContext from '@/contexts/navbarSlotContext' // MUI Imports import Grid from '@mui/material/Grid2' import IconButton from '@mui/material/IconButton' import Box from '@mui/material/Box' import CircularProgress from '@mui/material/CircularProgress' // Third-party imports import { useDragAndDrop } from '@formkit/drag-and-drop/react' import { animations } from '@formkit/drag-and-drop' // Component Imports import FarmOverviewKPIs from '@views/dashboards/farm/FarmOverviewKPIs' import FarmWeatherCard from '@views/dashboards/farm/FarmWeatherCard' import FarmAlertsTracker from '@views/dashboards/farm/FarmAlertsTracker' import SensorValuesList from '@views/dashboards/farm/SensorValuesList' import SensorRadarChart from '@views/dashboards/farm/SensorRadarChart' import SensorComparisonChart from '@views/dashboards/farm/SensorComparisonChart' import FarmAlertsTimeline from '@views/dashboards/farm/FarmAlertsTimeline' import WaterNeedPrediction from '@views/dashboards/farm/WaterNeedPrediction' import YieldPredictionChart from '@views/dashboards/farm/YieldPredictionChart' import HarvestPredictionCard from '@views/dashboards/farm/HarvestPredictionCard' import SoilMoistureHeatmap from '@views/dashboards/farm/SoilMoistureHeatmap' import AnomalyDetectionCard from '@views/dashboards/farm/AnomalyDetectionCard' import NDVIHealthCard from '@views/dashboards/farm/NDVIHealthCard' import RecommendationsList from '@views/dashboards/farm/RecommendationsList' import EconomicOverview from '@views/dashboards/farm/EconomicOverview' // Config & Service import { ROW_IDS, ROW_CARDS, CARD_GRID_SIZE, DEFAULT_FARM_DASHBOARD_CONFIG, type RowId, type CardId, type FarmDashboardConfig } from '@views/dashboards/farm/farmDashboardConfig' import { farmDashboardService } from '@/libs/api/services/farmDashboardService' import FarmDashboardSettingsDropdown from '@views/dashboards/farm/FarmDashboardSettingsDropdown' const cardRowSx = { display: 'flex', flexDirection: 'column', '& > *': { flex: 1, minHeight: 0 } } const CARD_COMPONENTS: Record = { farmOverviewKpis: FarmOverviewKPIs, farmWeatherCard: FarmWeatherCard, farmAlertsTracker: FarmAlertsTracker, sensorValuesList: SensorValuesList, sensorRadarChart: SensorRadarChart, sensorComparisonChart: SensorComparisonChart, anomalyDetectionCard: AnomalyDetectionCard, farmAlertsTimeline: FarmAlertsTimeline, waterNeedPrediction: WaterNeedPrediction, harvestPredictionCard: HarvestPredictionCard, yieldPredictionChart: YieldPredictionChart, soilMoistureHeatmap: SoilMoistureHeatmap, ndviHealthCard: NDVIHealthCard, recommendationsList: RecommendationsList, economicOverview: EconomicOverview } function mergeRowOrderAfterDrag( currentRowOrder: string[], newVisibleOrder: string[], visibleRows: string[] ): string[] { const result = [...currentRowOrder] let visibleIndex = 0 for (let i = 0; i < result.length; i++) { if (visibleRows.includes(result[i])) { result[i] = newVisibleOrder[visibleIndex++] } } return result } const FarmDashboardWrapper = () => { const t = useTranslations('farmDashboard') const { setSlotContent } = useContext(NavbarSlotContext) const [config, setConfig] = useState(DEFAULT_FARM_DASHBOARD_CONFIG) const cardLabels = useMemo( () => Object.fromEntries( ( [ 'farmOverviewKpis', 'farmWeatherCard', 'farmAlertsTracker', 'sensorValuesList', 'sensorRadarChart', 'sensorComparisonChart', 'anomalyDetectionCard', 'farmAlertsTimeline', 'waterNeedPrediction', 'harvestPredictionCard', 'yieldPredictionChart', 'soilMoistureHeatmap', 'ndviHealthCard', 'recommendationsList', 'economicOverview' ] as CardId[] ).map((id) => [id, t(`cards.${id}`)]) ) as Record, [t] ) const rowLabels = useMemo( () => Object.fromEntries( ( [ 'overviewKpis', 'weatherAlerts', 'sensorMonitoring', 'sensorCharts', 'alertsWater', 'predictions', 'soilHeatmap', 'ndviRecommendations', 'economic' ] as RowId[] ).map((id) => [id, t(`rows.${id}`)]) ) as Record, [t] ) const [cardsData, setCardsData] = useState>>>({}) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const disabledSet = new Set(config.disabledCardIds) const hasVisibleCard = useCallback( (rowId: string) => { const cards = ROW_CARDS[rowId as RowId] if (!Array.isArray(cards)) return false return cards.some(cardId => !disabledSet.has(cardId)) }, [config.disabledCardIds] ) const visibleRowOrder = config.rowOrder.filter(hasVisibleCard) const [containerRef, orderedRows, setOrderedRows] = useDragAndDrop(visibleRowOrder, { plugins: [animations()], dragHandle: '.row-drag-handle' }) useEffect(() => { 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: 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)) }, []) useEffect(() => { setOrderedRows(visibleRowOrder) // eslint-disable-next-line react-hooks/exhaustive-deps }, [config.disabledCardIds]) useEffect(() => { if (loading) return if (JSON.stringify(orderedRows) === JSON.stringify(visibleRowOrder)) return const newRowOrder = mergeRowOrderAfterDrag(config.rowOrder, orderedRows, visibleRowOrder) setConfig(prev => ({ ...prev, rowOrder: newRowOrder })) setSaving(true) farmDashboardService .updateConfig({ rowOrder: newRowOrder }) .then(updated => setConfig(updated)) .catch(() => {}) .finally(() => setSaving(false)) // eslint-disable-next-line react-hooks/exhaustive-deps }, [orderedRows]) const handleToggleDragReorder = useCallback((enabled: boolean) => { setConfig(prev => ({ ...prev, enableDragReorder: enabled })) setSaving(true) farmDashboardService .updateConfig({ enableDragReorder: enabled }) .then(updated => setConfig(updated)) .finally(() => setSaving(false)) }, []) const handleToggleCard = useCallback( (cardId: CardId, disabled: boolean) => { const next = disabled ? [...config.disabledCardIds, cardId] : config.disabledCardIds.filter(id => id !== cardId) setConfig(prev => ({ ...prev, disabledCardIds: next })) setSaving(true) farmDashboardService .updateConfig({ disabledCardIds: next }) .then(updated => setConfig(updated)) .catch(() => setConfig(prev => ({ ...prev, disabledCardIds: next }))) .finally(() => setSaving(false)) }, [config.disabledCardIds] ) useEffect(() => { setSlotContent( ) return () => setSlotContent(null) }, [setSlotContent, config.disabledCardIds, config.enableDragReorder, handleToggleCard, handleToggleDragReorder, saving]) if (loading) { return ( ) } return ( }> {orderedRows.map((rowId: string) => { const cards = ROW_CARDS[rowId as RowId].filter(cardId => !disabledSet.has(cardId)) if (cards.length === 0) return null const isOverviewRow = rowId === 'overviewKpis' return ( {config.enableDragReorder !== false && ( )} {isOverviewRow && cards.includes('farmOverviewKpis') && ( )} {!isOverviewRow && cards.map((cardId: CardId) => { const size = CARD_GRID_SIZE[cardId] const Component = CARD_COMPONENTS[cardId] if (!Component) return null return ( ) })} ) })} ) } export default FarmDashboardWrapper