Add plant simulator feature with Persian localization and UI enhancements
- Introduced a new Plant Simulator section in the dashboard with a dedicated menu item. - Added Persian translations for plant growth metrics and chart titles. - Enhanced the GrowthChart component to utilize localized labels for better user experience. - Updated PlantSimulator component to display plant statistics and growth visualization. - Improved UI layout with Material-UI components for a more polished appearance.
This commit is contained in:
@@ -37,6 +37,7 @@
|
||||
"waterData": "دیتاهای آب",
|
||||
"soilData": "اطلاعات خاک",
|
||||
"cropZoning": "زونبندی کشت",
|
||||
"plantSimulator": "شبیهساز رشد گیاه",
|
||||
"dataSection": "بخش دادهها",
|
||||
"crm": "مدیریت ارتباط با مشتری",
|
||||
"analytics": "تحلیلها",
|
||||
@@ -561,5 +562,35 @@
|
||||
"forecastChart": "پیشبینی هوا",
|
||||
"forecastSubheader": "دما و رطوبت در ۷ روز آینده"
|
||||
}
|
||||
},
|
||||
"plantSimulator": {
|
||||
"title": "شبیهساز رشد گیاه",
|
||||
"height": "ارتفاع",
|
||||
"leaves": "برگ",
|
||||
"branches": "شاخه",
|
||||
"fruits": "میوه",
|
||||
"yield": "محصول (g)",
|
||||
"yieldRate": "سرعت (g/s)",
|
||||
"maxGrowthReached": "گیاه به حداکثر رشد رسید!",
|
||||
"controls": "کنترلها",
|
||||
"stop": "توقف",
|
||||
"start": "شروع",
|
||||
"reset": "ریست",
|
||||
"growthSpeed": "سرعت رشد",
|
||||
"light": "نور",
|
||||
"water": "آب",
|
||||
"effectiveRate": "نرخ رشد مؤثر",
|
||||
"chartTitle": "نمودار رشد گیاه",
|
||||
"chartHeight": "ارتفاع",
|
||||
"chartLeaves": "برگ",
|
||||
"chartHeightPx": "ارتفاع (px)",
|
||||
"chartLeafCount": "تعداد برگ",
|
||||
"chartYield": "محصول (g)",
|
||||
"chartYieldRate": "سرعت محصول (g/s)",
|
||||
"progressGrowth": "پیشرفت رشد",
|
||||
"lightStatus": "وضعیت نور",
|
||||
"waterStatus": "وضعیت آب",
|
||||
"yieldStatus": "محصول دهی",
|
||||
"description": "این شبیهساز رشد گیاه را بر اساس سرعت پایه، میزان نور خورشید و آب دریافتی محاسبه میکند. هر برگ به صورت تدریجی روی ساقه ظاهر شده و با حرکت طبیعی در باد نمایش داده میشود. محصولدهی (g) پس از ۲۰٪ رشد شروع شده و با تعداد برگ، نور و آب شتاب میگیرد. سرعت محصول (g/s) نشاندهنده نرخ لحظهای تولید است. نمودار تغییرات همه شاخصها را در طول زمان ثبت میکند."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,9 @@ const VerticalMenu = ({ scrollMenu }: Props) => {
|
||||
<MenuItem href="/dashboard/crop-zoning" icon={<i className="tabler-map-2" />}>
|
||||
{t('cropZoning')}
|
||||
</MenuItem>
|
||||
<MenuItem href="/dashboard/plant-simulator" icon={<i className="tabler-flower" />}>
|
||||
{t('plantSimulator')}
|
||||
</MenuItem>
|
||||
</MenuSection>
|
||||
|
||||
</Menu>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef, useState, useCallback, memo } from 'react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
LineElement,
|
||||
@@ -13,6 +14,12 @@ import {
|
||||
Filler
|
||||
} from 'chart.js'
|
||||
import { Line } from 'react-chartjs-2'
|
||||
import Card from '@mui/material/Card'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Box from '@mui/material/Box'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Button from '@mui/material/Button'
|
||||
|
||||
ChartJS.register(LineElement, PointElement, LinearScale, CategoryScale, Title, Tooltip, Legend, Filler)
|
||||
|
||||
@@ -393,59 +400,6 @@ function PlantSVG({ plant, tick, running }: { plant: PlantState; tick: number; r
|
||||
|
||||
// ─── Growth Chart ─────────────────────────────────────────────────────────────
|
||||
|
||||
const CHART_OPTIONS = {
|
||||
responsive: true,
|
||||
animation: { duration: 0 },
|
||||
plugins: {
|
||||
legend: { labels: { color: '#e2e8f0', font: { size: 11 } } },
|
||||
title: {
|
||||
display: true,
|
||||
text: 'نمودار رشد گیاه',
|
||||
color: '#e2e8f0',
|
||||
font: { size: 14 }
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
ticks: { color: '#94a3b8', maxTicksLimit: 8 },
|
||||
grid: { color: 'rgba(148,163,184,0.1)' }
|
||||
},
|
||||
yHeight: {
|
||||
type: 'linear' as const,
|
||||
position: 'left' as const,
|
||||
min: 0,
|
||||
max: MAX_HEIGHT,
|
||||
ticks: { color: '#4a7c59' },
|
||||
grid: { color: 'rgba(148,163,184,0.1)' },
|
||||
title: { display: true, text: 'ارتفاع', color: '#4a7c59' }
|
||||
},
|
||||
yLeaf: {
|
||||
type: 'linear' as const,
|
||||
position: 'right' as const,
|
||||
min: 0,
|
||||
max: MAX_LEAVES,
|
||||
ticks: { color: '#f9c74f' },
|
||||
grid: { display: false },
|
||||
title: { display: true, text: 'برگ', color: '#f9c74f' }
|
||||
},
|
||||
yYield: {
|
||||
type: 'linear' as const,
|
||||
position: 'left' as const,
|
||||
min: 0,
|
||||
max: MAX_YIELD,
|
||||
display: false,
|
||||
grid: { display: false }
|
||||
},
|
||||
yYieldRate: {
|
||||
type: 'linear' as const,
|
||||
position: 'right' as const,
|
||||
min: 0,
|
||||
display: false,
|
||||
grid: { display: false }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const GrowthChart = memo(function GrowthChart({
|
||||
heightHistory,
|
||||
leafHistory,
|
||||
@@ -457,13 +411,63 @@ const GrowthChart = memo(function GrowthChart({
|
||||
yieldHistory: number[]
|
||||
yieldRateHistory: number[]
|
||||
}) {
|
||||
const t = useTranslations('plantSimulator')
|
||||
|
||||
const chartOptions = {
|
||||
responsive: true,
|
||||
animation: { duration: 0 },
|
||||
plugins: {
|
||||
legend: { labels: { font: { size: 11 } } },
|
||||
title: {
|
||||
display: true,
|
||||
text: t('chartTitle'),
|
||||
font: { size: 14 }
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
ticks: { maxTicksLimit: 8 }
|
||||
},
|
||||
yHeight: {
|
||||
type: 'linear' as const,
|
||||
position: 'left' as const,
|
||||
min: 0,
|
||||
max: MAX_HEIGHT,
|
||||
title: { display: true, text: t('chartHeight') }
|
||||
},
|
||||
yLeaf: {
|
||||
type: 'linear' as const,
|
||||
position: 'right' as const,
|
||||
min: 0,
|
||||
max: MAX_LEAVES,
|
||||
grid: { display: false },
|
||||
title: { display: true, text: t('chartLeaves') }
|
||||
},
|
||||
yYield: {
|
||||
type: 'linear' as const,
|
||||
position: 'left' as const,
|
||||
min: 0,
|
||||
max: MAX_YIELD,
|
||||
display: false,
|
||||
grid: { display: false }
|
||||
},
|
||||
yYieldRate: {
|
||||
type: 'linear' as const,
|
||||
position: 'right' as const,
|
||||
min: 0,
|
||||
display: false,
|
||||
grid: { display: false }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const labels = heightHistory.map((_, i) => `${i}s`)
|
||||
|
||||
const data = {
|
||||
labels,
|
||||
datasets: [
|
||||
{
|
||||
label: 'ارتفاع (px)',
|
||||
label: t('chartHeightPx'),
|
||||
data: heightHistory,
|
||||
borderColor: '#4a7c59',
|
||||
backgroundColor: 'rgba(74,124,89,0.10)',
|
||||
@@ -473,7 +477,7 @@ const GrowthChart = memo(function GrowthChart({
|
||||
yAxisID: 'yHeight'
|
||||
},
|
||||
{
|
||||
label: 'تعداد برگ',
|
||||
label: t('chartLeafCount'),
|
||||
data: leafHistory,
|
||||
borderColor: '#f9c74f',
|
||||
backgroundColor: 'rgba(249,199,79,0.10)',
|
||||
@@ -483,7 +487,7 @@ const GrowthChart = memo(function GrowthChart({
|
||||
yAxisID: 'yLeaf'
|
||||
},
|
||||
{
|
||||
label: 'محصول (g)',
|
||||
label: t('chartYield'),
|
||||
data: yieldHistory,
|
||||
borderColor: '#f97316',
|
||||
backgroundColor: 'rgba(249,115,22,0.10)',
|
||||
@@ -493,7 +497,7 @@ const GrowthChart = memo(function GrowthChart({
|
||||
yAxisID: 'yYield'
|
||||
},
|
||||
{
|
||||
label: 'سرعت محصول (g/s)',
|
||||
label: t('chartYieldRate'),
|
||||
data: yieldRateHistory,
|
||||
borderColor: '#a78bfa',
|
||||
backgroundColor: 'rgba(167,139,250,0.10)',
|
||||
@@ -506,7 +510,7 @@ const GrowthChart = memo(function GrowthChart({
|
||||
]
|
||||
}
|
||||
|
||||
return <Line data={data} options={CHART_OPTIONS} />
|
||||
return <Line data={data} options={chartOptions} />
|
||||
})
|
||||
|
||||
// ─── Main Component ───────────────────────────────────────────────────────────
|
||||
@@ -698,207 +702,232 @@ export default function PlantSimulator() {
|
||||
}
|
||||
}, [running])
|
||||
|
||||
const t = useTranslations('plantSimulator')
|
||||
const isFinished = plant.height >= MAX_HEIGHT
|
||||
|
||||
const statItems: { value: string | number; label: string; color: 'success' | 'warning' | 'error' | 'secondary' }[] = [
|
||||
{ value: Math.round(plant.height), label: t('height'), color: 'success' },
|
||||
{ value: plant.leaves.length, label: t('leaves'), color: 'warning' },
|
||||
{ value: plant.branches.length, label: t('branches'), color: 'success' },
|
||||
{ value: plant.fruits.length, label: t('fruits'), color: 'error' },
|
||||
{ value: plant.yield.toFixed(1), label: t('yield'), color: 'warning' },
|
||||
{ value: plant.yieldRate.toFixed(2), label: t('yieldRate'), color: 'secondary' }
|
||||
]
|
||||
|
||||
return (
|
||||
<div className='min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 text-slate-100 p-6'>
|
||||
<h1 className='text-3xl font-bold text-center mb-8 tracking-tight'>
|
||||
🌱 شبیهساز رشد گیاه
|
||||
</h1>
|
||||
|
||||
<div className='max-w-6xl mx-auto grid grid-cols-1 lg:grid-cols-3 gap-6'>
|
||||
<Box className='flex flex-col gap-6 is-full'>
|
||||
<Typography variant='h4' className='text-center font-bold'>
|
||||
🌱 {t('title')}
|
||||
</Typography>
|
||||
|
||||
<Grid container spacing={6} className='max-w-6xl mx-auto'>
|
||||
{/* ── Left: Plant visualization ── */}
|
||||
<div className='lg:col-span-1 flex flex-col items-center gap-4'>
|
||||
<div className='bg-slate-800 border border-slate-700 rounded-2xl p-6 w-full flex flex-col items-center shadow-xl'>
|
||||
<PlantSVG plant={plant} tick={plant.tick} running={running} />
|
||||
<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'>
|
||||
<CardContent className='flex flex-col items-center gap-4 is-full p-0'>
|
||||
<PlantSVG plant={plant} tick={plant.tick} running={running} />
|
||||
|
||||
{/* Stats */}
|
||||
<div className='mt-4 grid grid-cols-3 gap-2 w-full text-sm'>
|
||||
<div className='bg-green-900/40 border border-green-700/40 rounded-xl p-2.5 text-center'>
|
||||
<div className='text-green-400 font-semibold text-base'>{Math.round(plant.height)}</div>
|
||||
<div className='text-slate-400 text-[10px]'>ارتفاع</div>
|
||||
</div>
|
||||
<div className='bg-yellow-900/40 border border-yellow-700/40 rounded-xl p-2.5 text-center'>
|
||||
<div className='text-yellow-400 font-semibold text-base'>{plant.leaves.length}</div>
|
||||
<div className='text-slate-400 text-[10px]'>برگ</div>
|
||||
</div>
|
||||
<div className='bg-emerald-900/40 border border-emerald-700/40 rounded-xl p-2.5 text-center'>
|
||||
<div className='text-emerald-400 font-semibold text-base'>{plant.branches.length}</div>
|
||||
<div className='text-slate-400 text-[10px]'>شاخه</div>
|
||||
</div>
|
||||
<div className='bg-red-900/40 border border-red-700/40 rounded-xl p-2.5 text-center'>
|
||||
<div className='text-red-400 font-semibold text-base'>{plant.fruits.length}</div>
|
||||
<div className='text-slate-400 text-[10px]'>میوه</div>
|
||||
</div>
|
||||
<div className='bg-orange-900/40 border border-orange-700/40 rounded-xl p-2.5 text-center'>
|
||||
<div className='text-orange-400 font-semibold text-base'>{plant.yield.toFixed(1)}</div>
|
||||
<div className='text-slate-400 text-[10px]'>محصول (g)</div>
|
||||
</div>
|
||||
<div className='bg-violet-900/40 border border-violet-700/40 rounded-xl p-2.5 text-center'>
|
||||
<div className='text-violet-400 font-semibold text-base'>{plant.yieldRate.toFixed(2)}</div>
|
||||
<div className='text-slate-400 text-[10px]'>سرعت (g/s)</div>
|
||||
</div>
|
||||
</div>
|
||||
<Grid container spacing={2} className='is-full'>
|
||||
{statItems.map((item, idx) => (
|
||||
<Grid key={idx} size={{ xs: 4 }}>
|
||||
<Card variant='outlined' className='text-center p-2.5'>
|
||||
<Typography variant='h6' color={item.color}>
|
||||
{item.value}
|
||||
</Typography>
|
||||
<Typography variant='caption' color='text.secondary'>
|
||||
{item.label}
|
||||
</Typography>
|
||||
</Card>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
|
||||
{isFinished && (
|
||||
<div className='mt-3 text-yellow-300 text-sm font-medium animate-pulse'>
|
||||
🌼 گیاه به حداکثر رشد رسید!
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{isFinished && (
|
||||
<Typography variant='body2' color='warning.main' className='font-medium animate-pulse'>
|
||||
🌼 {t('maxGrowthReached')}
|
||||
</Typography>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Controls */}
|
||||
<div className='bg-slate-800 border border-slate-700 rounded-2xl p-5 w-full shadow-xl space-y-4'>
|
||||
<h2 className='font-semibold text-slate-300 text-base mb-1'>کنترلها</h2>
|
||||
<Card className='is-full p-5'>
|
||||
<CardContent className='space-y-4 p-0'>
|
||||
<Typography variant='subtitle1' component='h2' className='font-semibold'>
|
||||
{t('controls')}
|
||||
</Typography>
|
||||
|
||||
{/* Start / Stop / Reset */}
|
||||
<div className='flex gap-2'>
|
||||
<button
|
||||
onClick={() => setRunning(r => !r)}
|
||||
disabled={isFinished}
|
||||
className={`flex-1 py-2 rounded-xl font-semibold transition-all text-sm
|
||||
${running
|
||||
? 'bg-red-600 hover:bg-red-500'
|
||||
: 'bg-green-600 hover:bg-green-500'}
|
||||
disabled:opacity-40 disabled:cursor-not-allowed`}
|
||||
>
|
||||
{running ? '⏸ توقف' : '▶ شروع'}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { setRunning(false); reset() }}
|
||||
className='px-4 py-2 rounded-xl bg-slate-600 hover:bg-slate-500 text-sm font-semibold transition-all'
|
||||
>
|
||||
↺ ریست
|
||||
</button>
|
||||
</div>
|
||||
<Box className='flex gap-2'>
|
||||
<Button
|
||||
variant='contained'
|
||||
color={running ? 'error' : 'success'}
|
||||
onClick={() => setRunning(r => !r)}
|
||||
disabled={isFinished}
|
||||
fullWidth
|
||||
>
|
||||
{running ? `⏸ ${t('stop')}` : `▶ ${t('start')}`}
|
||||
</Button>
|
||||
<Button
|
||||
variant='outlined'
|
||||
color='secondary'
|
||||
onClick={() => { setRunning(false); reset() }}
|
||||
>
|
||||
↺ {t('reset')}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{/* Speed slider */}
|
||||
<div>
|
||||
<label className='flex justify-between text-sm text-slate-400 mb-1'>
|
||||
<span>سرعت رشد</span>
|
||||
<span className='text-white font-medium'>{speed.toFixed(1)}×</span>
|
||||
</label>
|
||||
<input
|
||||
type='range' min={0.5} max={5} step={0.5}
|
||||
value={speed}
|
||||
onChange={e => setSpeed(Number(e.target.value))}
|
||||
className='w-full accent-green-500'
|
||||
/>
|
||||
</div>
|
||||
<Box>
|
||||
<Typography variant='body2' color='text.secondary' className='flex justify-between mbe-1'>
|
||||
<span>{t('growthSpeed')}</span>
|
||||
<span className='font-medium'>{speed.toFixed(1)}×</span>
|
||||
</Typography>
|
||||
<input
|
||||
type='range'
|
||||
min={0.5}
|
||||
max={5}
|
||||
step={0.5}
|
||||
value={speed}
|
||||
onChange={e => setSpeed(Number(e.target.value))}
|
||||
className='w-full'
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Light */}
|
||||
<div>
|
||||
<label className='flex justify-between text-sm text-slate-400 mb-1'>
|
||||
<span>☀️ نور</span>
|
||||
<span className='text-yellow-400 font-medium'>{env.light}%</span>
|
||||
</label>
|
||||
<input
|
||||
type='range' min={0} max={100} step={5}
|
||||
value={env.light}
|
||||
onChange={e => setEnv(prev => ({ ...prev, light: Number(e.target.value) }))}
|
||||
className='w-full accent-yellow-400'
|
||||
/>
|
||||
</div>
|
||||
<Box>
|
||||
<Typography variant='body2' color='text.secondary' className='flex justify-between mbe-1'>
|
||||
<span>☀️ {t('light')}</span>
|
||||
<span className='font-medium'>{env.light}%</span>
|
||||
</Typography>
|
||||
<input
|
||||
type='range'
|
||||
min={0}
|
||||
max={100}
|
||||
step={5}
|
||||
value={env.light}
|
||||
onChange={e => setEnv(prev => ({ ...prev, light: Number(e.target.value) }))}
|
||||
className='w-full'
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Water */}
|
||||
<div>
|
||||
<label className='flex justify-between text-sm text-slate-400 mb-1'>
|
||||
<span>💧 آب</span>
|
||||
<span className='text-blue-400 font-medium'>{env.water}%</span>
|
||||
</label>
|
||||
<input
|
||||
type='range' min={0} max={100} step={5}
|
||||
value={env.water}
|
||||
onChange={e => setEnv(prev => ({ ...prev, water: Number(e.target.value) }))}
|
||||
className='w-full accent-blue-400'
|
||||
/>
|
||||
</div>
|
||||
<Box>
|
||||
<Typography variant='body2' color='text.secondary' className='flex justify-between mbe-1'>
|
||||
<span>💧 {t('water')}</span>
|
||||
<span className='font-medium'>{env.water}%</span>
|
||||
</Typography>
|
||||
<input
|
||||
type='range'
|
||||
min={0}
|
||||
max={100}
|
||||
step={5}
|
||||
value={env.water}
|
||||
onChange={e => setEnv(prev => ({ ...prev, water: Number(e.target.value) }))}
|
||||
className='w-full'
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Effective rate indicator */}
|
||||
<div className='bg-slate-700/50 rounded-xl p-3 text-xs text-slate-400'>
|
||||
نرخ رشد مؤثر:{' '}
|
||||
<span className='text-green-400 font-semibold'>
|
||||
{growthRate(env, speed).toFixed(2)}×
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Card variant='outlined' className='p-3'>
|
||||
<Typography variant='caption' color='text.secondary'>
|
||||
{t('effectiveRate')}{' '}
|
||||
<Typography component='span' variant='caption' color='success.main' fontWeight='bold'>
|
||||
{growthRate(env, speed).toFixed(2)}×
|
||||
</Typography>
|
||||
</Typography>
|
||||
</Card>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
|
||||
{/* ── Right: Chart ── */}
|
||||
<div className='lg:col-span-2 bg-slate-800 border border-slate-700 rounded-2xl p-6 shadow-xl'>
|
||||
<GrowthChart
|
||||
heightHistory={heightHistory}
|
||||
leafHistory={leafHistory}
|
||||
yieldHistory={yieldHistory}
|
||||
yieldRateHistory={yieldRateHistory}
|
||||
/>
|
||||
<Grid size={{ xs: 12, lg: 8 }}>
|
||||
<Card className='p-6'>
|
||||
<CardContent>
|
||||
<GrowthChart
|
||||
heightHistory={heightHistory}
|
||||
leafHistory={leafHistory}
|
||||
yieldHistory={yieldHistory}
|
||||
yieldRateHistory={yieldRateHistory}
|
||||
/>
|
||||
|
||||
{/* Info cards */}
|
||||
<div className='mt-6 grid grid-cols-2 lg:grid-cols-4 gap-4 text-sm'>
|
||||
<div className='bg-slate-700/60 rounded-xl p-4 border border-slate-600'>
|
||||
<div className='text-slate-400 mb-1'>پیشرفت رشد</div>
|
||||
<div className='w-full bg-slate-600 rounded-full h-2 mb-1'>
|
||||
<div
|
||||
className='bg-green-500 h-2 rounded-full transition-all'
|
||||
style={{ width: `${(plant.height / MAX_HEIGHT) * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className='text-green-400 font-semibold'>
|
||||
{Math.round((plant.height / MAX_HEIGHT) * 100)}%
|
||||
</div>
|
||||
</div>
|
||||
<Grid container spacing={4} className='mt-6'>
|
||||
<Grid size={{ xs: 12, sm: 6, lg: 3 }}>
|
||||
<Card variant='outlined' className='p-4'>
|
||||
<Typography variant='body2' color='text.secondary' className='mbe-1'>
|
||||
{t('progressGrowth')}
|
||||
</Typography>
|
||||
<Box className='w-full rounded-full overflow-hidden bg-action-hover mbe-1' sx={{ height: 8 }}>
|
||||
<Box
|
||||
className='bg-success rounded-full h-full transition-all'
|
||||
sx={{ width: `${(plant.height / MAX_HEIGHT) * 100}%` }}
|
||||
/>
|
||||
</Box>
|
||||
<Typography variant='body2' color='success.main' fontWeight='bold'>
|
||||
{Math.round((plant.height / MAX_HEIGHT) * 100)}%
|
||||
</Typography>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6, lg: 3 }}>
|
||||
<Card variant='outlined' className='p-4'>
|
||||
<Typography variant='body2' color='text.secondary' className='mbe-1'>
|
||||
{t('lightStatus')}
|
||||
</Typography>
|
||||
<Box className='w-full rounded-full overflow-hidden bg-action-hover mbe-1' sx={{ height: 8 }}>
|
||||
<Box
|
||||
className='bg-warning rounded-full h-full transition-all'
|
||||
sx={{ width: `${env.light}%` }}
|
||||
/>
|
||||
</Box>
|
||||
<Typography variant='body2' color='warning.main' fontWeight='bold'>
|
||||
{env.light}%
|
||||
</Typography>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6, lg: 3 }}>
|
||||
<Card variant='outlined' className='p-4'>
|
||||
<Typography variant='body2' color='text.secondary' className='mbe-1'>
|
||||
{t('waterStatus')}
|
||||
</Typography>
|
||||
<Box className='w-full rounded-full overflow-hidden bg-action-hover mbe-1' sx={{ height: 8 }}>
|
||||
<Box
|
||||
className='bg-info rounded-full h-full transition-all'
|
||||
sx={{ width: `${env.water}%` }}
|
||||
/>
|
||||
</Box>
|
||||
<Typography variant='body2' color='info.main' fontWeight='bold'>
|
||||
{env.water}%
|
||||
</Typography>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6, lg: 3 }}>
|
||||
<Card variant='outlined' className='p-4'>
|
||||
<Typography variant='body2' color='text.secondary' className='mbe-1'>
|
||||
{t('yieldStatus')}
|
||||
</Typography>
|
||||
<Box className='w-full rounded-full overflow-hidden bg-action-hover mbe-1' sx={{ height: 8 }}>
|
||||
<Box
|
||||
className='bg-warning rounded-full h-full transition-all'
|
||||
sx={{ width: `${(plant.yield / MAX_YIELD) * 100}%` }}
|
||||
/>
|
||||
</Box>
|
||||
<Box className='flex justify-between items-center'>
|
||||
<Typography variant='body2' color='warning.main' fontWeight='bold'>
|
||||
{plant.yield.toFixed(1)}g
|
||||
</Typography>
|
||||
<Typography variant='caption' color='secondary.main'>
|
||||
{plant.yieldRate.toFixed(3)} g/s
|
||||
</Typography>
|
||||
</Box>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<div className='bg-slate-700/60 rounded-xl p-4 border border-slate-600'>
|
||||
<div className='text-slate-400 mb-1'>وضعیت نور</div>
|
||||
<div className='w-full bg-slate-600 rounded-full h-2 mb-1'>
|
||||
<div
|
||||
className='bg-yellow-400 h-2 rounded-full transition-all'
|
||||
style={{ width: `${env.light}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className='text-yellow-400 font-semibold'>{env.light}%</div>
|
||||
</div>
|
||||
|
||||
<div className='bg-slate-700/60 rounded-xl p-4 border border-slate-600'>
|
||||
<div className='text-slate-400 mb-1'>وضعیت آب</div>
|
||||
<div className='w-full bg-slate-600 rounded-full h-2 mb-1'>
|
||||
<div
|
||||
className='bg-blue-400 h-2 rounded-full transition-all'
|
||||
style={{ width: `${env.water}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className='text-blue-400 font-semibold'>{env.water}%</div>
|
||||
</div>
|
||||
|
||||
<div className='bg-slate-700/60 rounded-xl p-4 border border-slate-600'>
|
||||
<div className='text-slate-400 mb-1'>محصول دهی</div>
|
||||
<div className='w-full bg-slate-600 rounded-full h-2 mb-1'>
|
||||
<div
|
||||
className='bg-orange-400 h-2 rounded-full transition-all'
|
||||
style={{ width: `${(plant.yield / MAX_YIELD) * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex justify-between items-center'>
|
||||
<span className='text-orange-400 font-semibold'>{plant.yield.toFixed(1)}g</span>
|
||||
<span className='text-violet-400 text-xs'>{plant.yieldRate.toFixed(3)} g/s</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div className='mt-6 bg-slate-700/30 border border-slate-600/50 rounded-xl p-4 text-xs text-slate-400 leading-6'>
|
||||
<p>
|
||||
این شبیهساز رشد گیاه را بر اساس سرعت پایه، میزان نور خورشید و آب دریافتی
|
||||
محاسبه میکند. هر برگ به صورت تدریجی روی ساقه ظاهر شده و با حرکت طبیعی
|
||||
در باد نمایش داده میشود. <strong className='text-slate-300'>محصولدهی (g)</strong> پس از ۲۰٪ رشد شروع شده
|
||||
و با تعداد برگ، نور و آب شتاب میگیرد. <strong className='text-slate-300'>سرعت محصول (g/s)</strong> نشاندهنده
|
||||
نرخ لحظهای تولید است. نمودار تغییرات همه شاخصها را در طول زمان ثبت میکند.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<Card variant='outlined' className='mt-6 p-4'>
|
||||
<Typography variant='body2' color='text.secondary' className='leading-6'>
|
||||
{t('description')}
|
||||
</Typography>
|
||||
</Card>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user