Remove deprecated dashboard pages and update vertical menu links for streamlined navigation
- Deleted unused pages for crop zoning, farm AI assistant, fertilization recommendation, irrigation recommendation, pest detection, plant simulator, soil data, and water data. - Updated the vertical menu to reflect the removal of these pages, ensuring a cleaner and more efficient user experience.
This commit is contained in:
@@ -95,36 +95,34 @@ const VerticalMenu = ({ scrollMenu }: Props) => {
|
|||||||
{t('dashboards')}
|
{t('dashboards')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuSection label={t('dataSection')}>
|
<MenuSection label={t('dataSection')}>
|
||||||
|
<MenuItem href="/water-data" icon={<i className="tabler-droplet" />}>
|
||||||
<MenuItem href="/dashboard/water-data" icon={<i className="tabler-droplet" />}>
|
|
||||||
{t('waterData')}
|
{t('waterData')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem href="/dashboard/soil-data" icon={<i className="tabler-seedling" />}>
|
<MenuItem href="/soil-data" icon={<i className="tabler-seedling" />}>
|
||||||
{t('soilData')}
|
{t('soilData')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem href="/dashboard/crop-zoning" icon={<i className="tabler-map-2" />}>
|
<MenuItem href="/crop-zoning" icon={<i className="tabler-map-2" />}>
|
||||||
{t('cropZoning')}
|
{t('cropZoning')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuSection>
|
</MenuSection>
|
||||||
<MenuSection label={t('simulator')}>
|
<MenuSection label={t('simulator')}>
|
||||||
<MenuItem href="/dashboard/plant-simulator" icon={<i className="tabler-flower" />}>
|
<MenuItem href="/plant-simulator" icon={<i className="tabler-flower" />}>
|
||||||
{t('plantSimulator')}
|
{t('plantSimulator')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuSection>
|
</MenuSection>
|
||||||
<MenuSection label={t('recommendation')}>
|
<MenuSection label={t('recommendation')}>
|
||||||
|
<MenuItem href="/irrigation-recommendation" icon={<i className="tabler-droplet-half-2" />}>
|
||||||
<MenuItem href="/dashboard/irrigation-recommendation" icon={<i className="tabler-droplet-half-2" />}>
|
|
||||||
{t('irrigationRecommendation')}
|
{t('irrigationRecommendation')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem href="/dashboard/fertilization-recommendation" icon={<i className="tabler-atom-2" />}>
|
<MenuItem href="/fertilization-recommendation" icon={<i className="tabler-atom-2" />}>
|
||||||
{t('fertilizationRecommendation')}
|
{t('fertilizationRecommendation')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuSection>
|
</MenuSection>
|
||||||
<MenuSection label={t('aiAssistant')}>
|
<MenuSection label={t('aiAssistant')}>
|
||||||
<MenuItem href="/dashboard/farm-ai-assistant" icon={<i className="tabler-robot" />}>
|
<MenuItem href="/farm-ai-assistant" icon={<i className="tabler-robot" />}>
|
||||||
{t('farmAiAssistant')}
|
{t('farmAiAssistant')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem href="/dashboard/pest-detection" icon={<i className="tabler-bug" />}>
|
<MenuItem href="/pest-detection" icon={<i className="tabler-bug" />}>
|
||||||
{t('pestDetection')}
|
{t('pestDetection')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuSection>
|
</MenuSection>
|
||||||
|
|||||||
@@ -18,19 +18,18 @@ import AnomalyDetectionCard from '@views/dashboards/farm/AnomalyDetectionCard'
|
|||||||
// Service
|
// Service
|
||||||
import { farmDashboardService } from '@/libs/api/services/farmDashboardService'
|
import { farmDashboardService } from '@/libs/api/services/farmDashboardService'
|
||||||
import type { CardId } from '@views/dashboards/farm/farmDashboardConfig'
|
import type { CardId } from '@views/dashboards/farm/farmDashboardConfig'
|
||||||
import { CARD_GRID_SIZE } from '@views/dashboards/farm/farmDashboardConfig'
|
|
||||||
|
|
||||||
const SOIL_CARD_IDS: CardId[] = [
|
/** هر ردیف: آرایهٔ کارتها؛ در هر ردیف فضا مساوی بین گریدها تقسیم میشود (جمع = ۱۲) */
|
||||||
'soilMoistureHeatmap',
|
const SOIL_ROWS: CardId[][] = [
|
||||||
'sensorValuesList',
|
['soilMoistureHeatmap'],
|
||||||
'sensorRadarChart',
|
['sensorValuesList', 'sensorRadarChart'],
|
||||||
'sensorComparisonChart',
|
['sensorComparisonChart', 'anomalyDetectionCard']
|
||||||
'anomalyDetectionCard'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const cardRowSx = {
|
const cardRowSx = {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
minHeight: 380,
|
||||||
'& > *': { flex: 1, minHeight: 0 }
|
'& > *': { flex: 1, minHeight: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,18 +64,28 @@ const SoilDataDashboardWrapper = () => {
|
|||||||
return (
|
return (
|
||||||
<Box position='relative'>
|
<Box position='relative'>
|
||||||
<Grid container spacing={6}>
|
<Grid container spacing={6}>
|
||||||
<Grid size={12} container spacing={6} sx={{ display: 'flex', alignItems: 'flex-start' }}>
|
{SOIL_ROWS.map((rowCards, rowIndex) => {
|
||||||
{SOIL_CARD_IDS.map(cardId => {
|
const sizePerCard = 12 / rowCards.length
|
||||||
const size = CARD_GRID_SIZE[cardId]
|
return (
|
||||||
|
<Grid
|
||||||
|
key={rowIndex}
|
||||||
|
size={12}
|
||||||
|
container
|
||||||
|
spacing={6}
|
||||||
|
sx={{ display: 'flex', alignItems: 'stretch' }}
|
||||||
|
>
|
||||||
|
{rowCards.map(cardId => {
|
||||||
const Component = CARD_COMPONENTS[cardId]
|
const Component = CARD_COMPONENTS[cardId]
|
||||||
if (!Component) return null
|
if (!Component) return null
|
||||||
return (
|
return (
|
||||||
<Grid key={cardId} size={size} sx={cardRowSx}>
|
<Grid key={cardId} size={sizePerCard} sx={cardRowSx}>
|
||||||
<Component data={cardsData?.[cardId]} />
|
<Component data={cardsData?.[cardId]} />
|
||||||
</Grid>
|
</Grid>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,18 +17,17 @@ import SensorValuesList from '@views/dashboards/farm/SensorValuesList'
|
|||||||
// Service
|
// Service
|
||||||
import { farmDashboardService } from '@/libs/api/services/farmDashboardService'
|
import { farmDashboardService } from '@/libs/api/services/farmDashboardService'
|
||||||
import type { CardId } from '@views/dashboards/farm/farmDashboardConfig'
|
import type { CardId } from '@views/dashboards/farm/farmDashboardConfig'
|
||||||
import { CARD_GRID_SIZE } from '@views/dashboards/farm/farmDashboardConfig'
|
|
||||||
|
|
||||||
const WATER_CARD_IDS: CardId[] = [
|
/** هر ردیف: آرایهٔ کارتها؛ در هر ردیف فضا مساوی بین گریدها تقسیم میشود (جمع = ۱۲) */
|
||||||
'farmWeatherCard',
|
const WATER_ROWS: CardId[][] = [
|
||||||
'farmAlertsTimeline',
|
['farmWeatherCard', 'farmAlertsTimeline', 'waterNeedPrediction'],
|
||||||
'waterNeedPrediction',
|
['sensorValuesList']
|
||||||
'sensorValuesList'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const cardRowSx = {
|
const cardRowSx = {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
minHeight: 380,
|
||||||
'& > *': { flex: 1, minHeight: 0 }
|
'& > *': { flex: 1, minHeight: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,18 +61,28 @@ const WaterDataDashboardWrapper = () => {
|
|||||||
return (
|
return (
|
||||||
<Box position='relative'>
|
<Box position='relative'>
|
||||||
<Grid container spacing={6}>
|
<Grid container spacing={6}>
|
||||||
<Grid size={12} container spacing={6} sx={{ display: 'flex', alignItems: 'flex-start' }}>
|
{WATER_ROWS.map((rowCards, rowIndex) => {
|
||||||
{WATER_CARD_IDS.map(cardId => {
|
const sizePerCard = 12 / rowCards.length
|
||||||
const size = CARD_GRID_SIZE[cardId]
|
return (
|
||||||
|
<Grid
|
||||||
|
key={rowIndex}
|
||||||
|
size={12}
|
||||||
|
container
|
||||||
|
spacing={6}
|
||||||
|
sx={{ display: 'flex', alignItems: 'stretch' }}
|
||||||
|
>
|
||||||
|
{rowCards.map(cardId => {
|
||||||
const Component = CARD_COMPONENTS[cardId]
|
const Component = CARD_COMPONENTS[cardId]
|
||||||
if (!Component) return null
|
if (!Component) return null
|
||||||
return (
|
return (
|
||||||
<Grid key={cardId} size={size} sx={cardRowSx}>
|
<Grid key={cardId} size={sizePerCard} sx={cardRowSx}>
|
||||||
<Component data={cardsData?.[cardId]} />
|
<Component data={cardsData?.[cardId]} />
|
||||||
</Grid>
|
</Grid>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ type CropZoningMapProps = {
|
|||||||
onZoneClick?: (zone: ZoneFeatureProperties) => void
|
onZoneClick?: (zone: ZoneFeatureProperties) => void
|
||||||
optimizationKey?: number
|
optimizationKey?: number
|
||||||
className?: string
|
className?: string
|
||||||
|
/** منطقهٔ اولیه از دیتای ماک؛ وقتی مقدار دارد نقشه فقط نمایشی است و کاربر نمیتواند منطقه را تغییر دهد */
|
||||||
|
initialAreaGeoJson?: MapDrawGeoJSON | null
|
||||||
|
/** غیرفعال کردن رسم و ویرایش منطقه توسط کاربر */
|
||||||
|
readOnly?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CropZoningMap({
|
export default function CropZoningMap({
|
||||||
@@ -29,7 +33,9 @@ export default function CropZoningMap({
|
|||||||
onAreaChange,
|
onAreaChange,
|
||||||
onZoneClick,
|
onZoneClick,
|
||||||
optimizationKey = 0,
|
optimizationKey = 0,
|
||||||
className = ''
|
className = '',
|
||||||
|
initialAreaGeoJson = null,
|
||||||
|
readOnly = false
|
||||||
}: CropZoningMapProps) {
|
}: CropZoningMapProps) {
|
||||||
const mapRef = useRef<HTMLDivElement>(null)
|
const mapRef = useRef<HTMLDivElement>(null)
|
||||||
const mapInstanceRef = useRef<L.Map | null>(null)
|
const mapInstanceRef = useRef<L.Map | null>(null)
|
||||||
@@ -124,6 +130,7 @@ export default function CropZoningMap({
|
|||||||
const drawnItems = L.featureGroup().addTo(map)
|
const drawnItems = L.featureGroup().addTo(map)
|
||||||
drawnItemsRef.current = drawnItems
|
drawnItemsRef.current = drawnItems
|
||||||
|
|
||||||
|
if (!readOnly) {
|
||||||
const drawControl = new L.Control.Draw({
|
const drawControl = new L.Control.Draw({
|
||||||
position: 'topright',
|
position: 'topright',
|
||||||
draw: {
|
draw: {
|
||||||
@@ -136,9 +143,9 @@ export default function CropZoningMap({
|
|||||||
},
|
},
|
||||||
edit: { featureGroup: drawnItems, remove: true }
|
edit: { featureGroup: drawnItems, remove: true }
|
||||||
})
|
})
|
||||||
|
|
||||||
map.addControl(drawControl)
|
map.addControl(drawControl)
|
||||||
drawControlRef.current = drawControl
|
drawControlRef.current = drawControl
|
||||||
|
}
|
||||||
|
|
||||||
const getGeoJsonFromDrawn = (): MapDrawGeoJSON => {
|
const getGeoJsonFromDrawn = (): MapDrawGeoJSON => {
|
||||||
const geojson = drawnItems.toGeoJSON()
|
const geojson = drawnItems.toGeoJSON()
|
||||||
@@ -159,6 +166,12 @@ export default function CropZoningMap({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (readOnly && initialAreaGeoJson && initialAreaGeoJson.geometry && (initialAreaGeoJson.geometry as { type: string }).type === 'Polygon') {
|
||||||
|
drawnItems.clearLayers()
|
||||||
|
L.geoJSON(initialAreaGeoJson as Feature<Polygon>).eachLayer((layer) => drawnItems.addLayer(layer))
|
||||||
|
emitAndRender()
|
||||||
|
}
|
||||||
|
|
||||||
const onCreated = (e: L.LeafletEvent) => {
|
const onCreated = (e: L.LeafletEvent) => {
|
||||||
const event = e as L.DrawEvents.Created
|
const event = e as L.DrawEvents.Created
|
||||||
drawnItems.clearLayers()
|
drawnItems.clearLayers()
|
||||||
@@ -175,17 +188,23 @@ export default function CropZoningMap({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!readOnly) {
|
||||||
map.on(L.Draw.Event.CREATED, onCreated)
|
map.on(L.Draw.Event.CREATED, onCreated)
|
||||||
map.on(L.Draw.Event.EDITED, onEdited)
|
map.on(L.Draw.Event.EDITED, onEdited)
|
||||||
map.on(L.Draw.Event.DELETED, onDeleted)
|
map.on(L.Draw.Event.DELETED, onDeleted)
|
||||||
|
}
|
||||||
|
|
||||||
mapInstanceRef.current = map
|
mapInstanceRef.current = map
|
||||||
|
|
||||||
cleanupFn = () => {
|
cleanupFn = () => {
|
||||||
|
if (!readOnly) {
|
||||||
map.off(L.Draw.Event.CREATED, onCreated)
|
map.off(L.Draw.Event.CREATED, onCreated)
|
||||||
map.off(L.Draw.Event.EDITED, onEdited)
|
map.off(L.Draw.Event.EDITED, onEdited)
|
||||||
map.off(L.Draw.Event.DELETED, onDeleted)
|
map.off(L.Draw.Event.DELETED, onDeleted)
|
||||||
map.removeControl(drawControl)
|
if (drawControlRef.current) {
|
||||||
|
map.removeControl(drawControlRef.current)
|
||||||
|
}
|
||||||
|
}
|
||||||
if (zonesLayerRef.current) map.removeLayer(zonesLayerRef.current)
|
if (zonesLayerRef.current) map.removeLayer(zonesLayerRef.current)
|
||||||
map.remove()
|
map.remove()
|
||||||
mapInstanceRef.current = null
|
mapInstanceRef.current = null
|
||||||
|
|||||||
@@ -96,19 +96,32 @@ export default function CropZoningWeatherSection() {
|
|||||||
const humidity = (weatherData.humidity as number) ?? 45
|
const humidity = (weatherData.humidity as number) ?? 45
|
||||||
const windSpeed = (weatherData.windSpeed as number) ?? 12
|
const windSpeed = (weatherData.windSpeed as number) ?? 12
|
||||||
|
|
||||||
|
const cardRowSx = {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column' as const,
|
||||||
|
minHeight: 200,
|
||||||
|
'& > *': { flex: 1, minHeight: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className='pis-0 pie-0'>
|
<Box className='pis-0 pie-0'>
|
||||||
<Typography variant='h6' className='mbe-4 font-semibold'>
|
<Typography variant='h6' className='mbe-4 font-semibold'>
|
||||||
{t('title')}
|
{t('title')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid size={{ xs: 12, md: 6, lg: 4 }}>
|
{/* Row 1: Weather + 3 KPI cards — equal width (3 each on md+) */}
|
||||||
|
<Grid
|
||||||
|
size={12}
|
||||||
|
container
|
||||||
|
spacing={3}
|
||||||
|
sx={{ display: 'flex', alignItems: 'stretch' }}
|
||||||
|
>
|
||||||
|
<Grid size={{ xs: 12, md: 3 }} sx={cardRowSx}>
|
||||||
<FarmWeatherCard data={weatherData} />
|
<FarmWeatherCard data={weatherData} />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid size={{ xs: 12, sm: 4, md: 3 }} sx={cardRowSx}>
|
||||||
<Grid size={{ xs: 12, sm: 6, md: 4, lg: 2 }}>
|
<Card sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||||
<Card>
|
<CardContent className='flex flex-1 flex-col items-center justify-center gap-2'>
|
||||||
<CardContent className='flex flex-col items-center gap-2'>
|
|
||||||
<i className='tabler-temperature text-3xl text-error' />
|
<i className='tabler-temperature text-3xl text-error' />
|
||||||
<Typography variant='body2' color='text.secondary'>
|
<Typography variant='body2' color='text.secondary'>
|
||||||
{t('temperature')}
|
{t('temperature')}
|
||||||
@@ -120,10 +133,9 @@ export default function CropZoningWeatherSection() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid size={{ xs: 12, sm: 4, md: 3 }} sx={cardRowSx}>
|
||||||
<Grid size={{ xs: 12, sm: 6, md: 4, lg: 2 }}>
|
<Card sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||||
<Card>
|
<CardContent className='flex flex-1 flex-col items-center justify-center gap-2'>
|
||||||
<CardContent className='flex flex-col items-center gap-2'>
|
|
||||||
<i className='tabler-droplet text-3xl text-info' />
|
<i className='tabler-droplet text-3xl text-info' />
|
||||||
<Typography variant='body2' color='text.secondary'>
|
<Typography variant='body2' color='text.secondary'>
|
||||||
{t('humidity')}
|
{t('humidity')}
|
||||||
@@ -132,10 +144,9 @@ export default function CropZoningWeatherSection() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid size={{ xs: 12, sm: 4, md: 3 }} sx={cardRowSx}>
|
||||||
<Grid size={{ xs: 12, sm: 6, md: 4, lg: 2 }}>
|
<Card sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||||
<Card>
|
<CardContent className='flex flex-1 flex-col items-center justify-center gap-2'>
|
||||||
<CardContent className='flex flex-col items-center gap-2'>
|
|
||||||
<i className='tabler-wind text-3xl text-success' />
|
<i className='tabler-wind text-3xl text-success' />
|
||||||
<Typography variant='body2' color='text.secondary'>
|
<Typography variant='body2' color='text.secondary'>
|
||||||
{t('windSpeed')}
|
{t('windSpeed')}
|
||||||
@@ -146,14 +157,16 @@ export default function CropZoningWeatherSection() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Grid size={{ xs: 12 }}>
|
{/* Row 2: Forecast chart — full width */}
|
||||||
<Card>
|
<Grid size={12} sx={cardRowSx}>
|
||||||
|
<Card sx={{ height: '100%', display: 'flex', flexDirection: 'column', minHeight: 280 }}>
|
||||||
<CardHeader
|
<CardHeader
|
||||||
title={t('forecastChart')}
|
title={t('forecastChart')}
|
||||||
subheader={t('forecastSubheader')}
|
subheader={t('forecastSubheader')}
|
||||||
/>
|
/>
|
||||||
<CardContent>
|
<CardContent sx={{ flex: 1, minHeight: 0 }}>
|
||||||
<AppReactApexCharts
|
<AppReactApexCharts
|
||||||
type='line'
|
type='line'
|
||||||
height={260}
|
height={260}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import ZoneLegend from './ZoneLegend'
|
|||||||
import LayerControl from './LayerControl'
|
import LayerControl from './LayerControl'
|
||||||
import ZoneDetailPanel from './ZoneDetailPanel'
|
import ZoneDetailPanel from './ZoneDetailPanel'
|
||||||
import CropZoningWeatherSection from './CropZoningWeatherSection'
|
import CropZoningWeatherSection from './CropZoningWeatherSection'
|
||||||
|
import { MOCK_AREA_GEOJSON } from './cropZoningMockData'
|
||||||
import type { LayerType } from './cropZoningTypes'
|
import type { LayerType } from './cropZoningTypes'
|
||||||
import type { ZoneFeatureProperties } from './cropZoningTypes'
|
import type { ZoneFeatureProperties } from './cropZoningTypes'
|
||||||
import type { MapDrawGeoJSON } from './CropZoningMap'
|
import type { MapDrawGeoJSON } from './CropZoningMap'
|
||||||
@@ -26,7 +27,7 @@ const MapComponent = dynamic(() => Promise.resolve(CropZoningMap), {
|
|||||||
|
|
||||||
export default function CropZoningWrapper() {
|
export default function CropZoningWrapper() {
|
||||||
const t = useTranslations('cropZoning')
|
const t = useTranslations('cropZoning')
|
||||||
const [areaGeoJson, setAreaGeoJson] = useState<MapDrawGeoJSON>(null)
|
const [areaGeoJson, setAreaGeoJson] = useState<MapDrawGeoJSON>(MOCK_AREA_GEOJSON)
|
||||||
const [activeLayer, setActiveLayer] = useState<LayerType>('crops')
|
const [activeLayer, setActiveLayer] = useState<LayerType>('crops')
|
||||||
const [selectedZone, setSelectedZone] = useState<ZoneFeatureProperties | null>(null)
|
const [selectedZone, setSelectedZone] = useState<ZoneFeatureProperties | null>(null)
|
||||||
const [panelOpen, setPanelOpen] = useState(false)
|
const [panelOpen, setPanelOpen] = useState(false)
|
||||||
@@ -58,6 +59,8 @@ export default function CropZoningWrapper() {
|
|||||||
onZoneClick={handleZoneClick}
|
onZoneClick={handleZoneClick}
|
||||||
optimizationKey={optimizationKey}
|
optimizationKey={optimizationKey}
|
||||||
className='min-bs-[400px]'
|
className='min-bs-[400px]'
|
||||||
|
initialAreaGeoJson={MOCK_AREA_GEOJSON}
|
||||||
|
readOnly
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import type { MapDrawGeoJSON } from './CropZoningMap'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* منطقهٔ ثابت برای نمایش روی نقشه (دیتای ماک).
|
||||||
|
* کاربر امکان تغییر یا رسم منطقهٔ جدید را ندارد.
|
||||||
|
* مختصات: یک چندضلعی حول تهران [35.6892, 51.389] — در GeoJSON به صورت [lng, lat].
|
||||||
|
*/
|
||||||
|
export const MOCK_AREA_GEOJSON: MapDrawGeoJSON = {
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {},
|
||||||
|
geometry: {
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [
|
||||||
|
[
|
||||||
|
[51.38, 35.68],
|
||||||
|
[51.40, 35.68],
|
||||||
|
[51.40, 35.70],
|
||||||
|
[51.38, 35.70],
|
||||||
|
[51.38, 35.68]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} as MapDrawGeoJSON
|
||||||
@@ -415,6 +415,7 @@ const GrowthChart = memo(function GrowthChart({
|
|||||||
|
|
||||||
const chartOptions = {
|
const chartOptions = {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
animation: { duration: 0 },
|
animation: { duration: 0 },
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { labels: { font: { size: 11 } } },
|
legend: { labels: { font: { size: 11 } } },
|
||||||
@@ -510,7 +511,14 @@ const GrowthChart = memo(function GrowthChart({
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Line data={data} options={chartOptions} />
|
return (
|
||||||
|
<Box
|
||||||
|
className='is-full'
|
||||||
|
sx={{ minHeight: { xs: 380, sm: 340, md: 320 }, height: { xs: 380, sm: 340, md: 320 } }}
|
||||||
|
>
|
||||||
|
<Line data={data} options={chartOptions} />
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// ─── Main Component ───────────────────────────────────────────────────────────
|
// ─── Main Component ───────────────────────────────────────────────────────────
|
||||||
@@ -715,12 +723,12 @@ export default function PlantSimulator() {
|
|||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className='flex flex-col gap-6 is-full'>
|
<Box className='flex flex-col gap-6 min-is-0 is-full'>
|
||||||
<Typography variant='h4' className='text-center font-bold'>
|
<Typography variant='h4' className='text-center font-bold'>
|
||||||
🌱 {t('title')}
|
🌱 {t('title')}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Grid container spacing={6} className='max-w-6xl mx-auto'>
|
<Grid container spacing={6} className='min-is-0 is-full'>
|
||||||
{/* ── Left: Plant visualization ── */}
|
{/* ── Left: Plant visualization ── */}
|
||||||
<Grid size={{ xs: 12, lg: 4 }} className='flex flex-col items-center gap-4'>
|
<Grid size={{ xs: 12, lg: 4 }} className='flex flex-col items-center gap-4'>
|
||||||
<Card className='is-full flex flex-col items-center p-6'>
|
<Card className='is-full flex flex-col items-center p-6'>
|
||||||
|
|||||||
Reference in New Issue
Block a user