Enhance farm dashboard localization by adding Persian translations for various UI elements, including subheaders, option menus, labels, and fallback text. Refactor components to utilize the new translations, improving the overall user experience for Persian-speaking users.
This commit is contained in:
@@ -34,14 +34,14 @@ const AnomalyDetectionCard = ({ data }: AnomalyDetectionCardProps) => {
|
||||
<CardHeader
|
||||
avatar={<i className='tabler-alert-triangle text-xl' />}
|
||||
title={t('cards.anomalyDetectionCard')}
|
||||
subheader='Out of range values'
|
||||
action={<OptionMenu options={['View All', 'Configure', 'Export']} />}
|
||||
subheader={t('subheaders.outOfRangeValues')}
|
||||
action={<OptionMenu options={[t('optionMenu.viewAll'), t('optionMenu.configure'), t('optionMenu.export')]} />}
|
||||
sx={{ '& .MuiCardHeader-avatar': { mr: 3 } }}
|
||||
/>
|
||||
<CardContent className='flex flex-col gap-3'>
|
||||
{anomalies.length === 0 ? (
|
||||
<Typography variant='body2' color='text.secondary'>
|
||||
No anomalies detected. All sensors within optimal range.
|
||||
{t('empty.noAnomalies')}
|
||||
</Typography>
|
||||
) : (
|
||||
anomalies.map((item, index) => (
|
||||
@@ -58,7 +58,7 @@ const AnomalyDetectionCard = ({ data }: AnomalyDetectionCardProps) => {
|
||||
/>
|
||||
</div>
|
||||
<Typography variant='caption' color='text.secondary'>
|
||||
Value: {item.value} (Expected: {item.expected}) · Deviation: {item.deviation}
|
||||
{t('labels.value')}: {item.value} ({t('labels.expected')}: {item.expected}) · {t('labels.deviation')}: {item.deviation}
|
||||
</Typography>
|
||||
</div>
|
||||
))
|
||||
|
||||
@@ -39,7 +39,9 @@ const EconomicOverview = ({ data }: EconomicOverviewProps) => {
|
||||
const t = useTranslations('farmDashboard')
|
||||
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 chartCategories =
|
||||
(data?.chartCategories as string[]) ??
|
||||
[t('fallback.monthJan'), t('fallback.monthFeb'), t('fallback.monthMar'), t('fallback.monthApr'), t('fallback.monthMay'), t('fallback.monthJun')]
|
||||
const theme = useTheme()
|
||||
|
||||
const options: ApexOptions = {
|
||||
@@ -77,8 +79,8 @@ const EconomicOverview = ({ data }: EconomicOverviewProps) => {
|
||||
<Card>
|
||||
<CardHeader
|
||||
title={t('cards.economicOverview')}
|
||||
subheader='Costs & ROI'
|
||||
action={<OptionMenu options={['Export PDF', 'Export Excel', 'Details']} />}
|
||||
subheader={t('subheaders.costsAndRoi')}
|
||||
action={<OptionMenu options={[t('optionMenu.exportPdf'), t('optionMenu.exportExcel'), t('optionMenu.details')]} />}
|
||||
/>
|
||||
<CardContent className='flex flex-col gap-4'>
|
||||
{economicData.length > 0 && (
|
||||
|
||||
@@ -51,8 +51,8 @@ const FarmAlertsTimeline = ({ data }: FarmAlertsTimelineProps) => {
|
||||
avatar={<i className='tabler-bell-ring text-xl' />}
|
||||
title={t('cards.farmAlertsTimeline')}
|
||||
titleTypographyProps={{ variant: 'h5' }}
|
||||
subheader='Explainable recommendations'
|
||||
action={<OptionMenu options={['View All', 'Configure', 'Export']} />}
|
||||
subheader={t('subheaders.explainableRecommendations')}
|
||||
action={<OptionMenu options={[t('optionMenu.viewAll'), t('optionMenu.configure'), t('optionMenu.export')]} />}
|
||||
sx={{ '& .MuiCardHeader-avatar': { mr: 3 } }}
|
||||
/>
|
||||
<CardContent className='flex flex-col gap-6 pbe-5'>
|
||||
|
||||
@@ -46,7 +46,7 @@ const FarmAlertsTracker = ({ data }: FarmAlertsTrackerProps) => {
|
||||
|
||||
const options: ApexOptions = {
|
||||
stroke: { dashArray: 10 },
|
||||
labels: ['Active Alerts'],
|
||||
labels: [t('labels.activeAlerts')],
|
||||
colors: ['var(--mui-palette-warning-main)'],
|
||||
states: {
|
||||
hover: { filter: { type: 'none' } },
|
||||
@@ -97,14 +97,14 @@ const FarmAlertsTracker = ({ data }: FarmAlertsTrackerProps) => {
|
||||
<Card>
|
||||
<CardHeader
|
||||
title={t('cards.farmAlertsTracker')}
|
||||
subheader='Requires Attention'
|
||||
action={<OptionMenu options={['View All', 'Dismiss', 'Settings']} />}
|
||||
subheader={t('subheaders.requiresAttention')}
|
||||
action={<OptionMenu options={[t('optionMenu.viewAll'), t('optionMenu.dismiss'), t('optionMenu.settings')]} />}
|
||||
/>
|
||||
<CardContent className='flex flex-col sm:flex-row items-center justify-between gap-7'>
|
||||
<div className='flex flex-col gap-6 is-full sm:is-[unset]'>
|
||||
<div className='flex flex-col'>
|
||||
<Typography variant='h2'>{totalAlerts}</Typography>
|
||||
<Typography>Total Alerts</Typography>
|
||||
<Typography>{t('labels.totalAlerts')}</Typography>
|
||||
</div>
|
||||
<div className='flex flex-col gap-4 is-full'>
|
||||
{alertStats.map((item, index) => (
|
||||
|
||||
@@ -91,7 +91,7 @@ const FarmWeatherCard = ({ data }: FarmWeatherCardProps) => {
|
||||
title={t('cards.farmWeatherCard')}
|
||||
subheader={condition ? `${condition}, ${temperature}${unit}` : `${temperature}${unit}`}
|
||||
className='pbe-3'
|
||||
action={<OptionMenu options={['Refresh', '7-day forecast', 'Details']} />}
|
||||
action={<OptionMenu options={[t('optionMenu.refresh'), t('optionMenu.sevenDayForecast'), t('optionMenu.details')]} />}
|
||||
/>
|
||||
<CardContent>
|
||||
<div className='flex items-center gap-2 mbe-2'>
|
||||
@@ -101,7 +101,7 @@ const FarmWeatherCard = ({ data }: FarmWeatherCardProps) => {
|
||||
{unit}
|
||||
</Typography>
|
||||
<Typography color='text.disabled' variant='body2'>
|
||||
Humid: {humidity}% | Wind: {windSpeed} {windUnit}
|
||||
{t('labels.humid')}: {humidity}% | {t('labels.wind')}: {windSpeed} {windUnit}
|
||||
</Typography>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
@@ -22,7 +22,7 @@ const HarvestPredictionCard = ({ data }: HarvestPredictionCardProps) => {
|
||||
const t = useTranslations('farmDashboard')
|
||||
const harvestDate = (data?.dateFormatted as string) ?? ''
|
||||
const daysUntil = (data?.daysUntil as number | undefined) ?? 0
|
||||
const daysLeftFormatted = daysUntil > 0 ? `${daysUntil} days` : ''
|
||||
const daysLeftFormatted = daysUntil > 0 ? `${daysUntil} ${t('labels.days')}` : ''
|
||||
const description = (data?.description as string) ?? ''
|
||||
|
||||
return (
|
||||
@@ -34,8 +34,8 @@ const HarvestPredictionCard = ({ data }: HarvestPredictionCardProps) => {
|
||||
</CustomAvatar>
|
||||
}
|
||||
title={t('cards.harvestPredictionCard')}
|
||||
subheader='AI Estimated Date'
|
||||
action={<OptionMenu options={['Details', 'Adjust', 'Export']} />}
|
||||
subheader={t('subheaders.aiEstimatedDate')}
|
||||
action={<OptionMenu options={[t('optionMenu.details'), t('optionMenu.adjust'), t('optionMenu.export')]} />}
|
||||
/>
|
||||
<CardContent className='flex flex-col gap-4'>
|
||||
<div className='flex items-center gap-4'>
|
||||
|
||||
@@ -37,7 +37,7 @@ const NDVIHealthCard = ({ data }: NDVIHealthCardProps) => {
|
||||
|
||||
const options: ApexOptions = {
|
||||
stroke: { dashArray: 10 },
|
||||
labels: ['NDVI'],
|
||||
labels: [t('labels.ndvi')],
|
||||
colors: [successColor],
|
||||
states: {
|
||||
hover: { filter: { type: 'none' } },
|
||||
@@ -88,13 +88,13 @@ const NDVIHealthCard = ({ data }: NDVIHealthCardProps) => {
|
||||
<CardHeader
|
||||
avatar={<i className='tabler-chart-radar text-xl' />}
|
||||
title={t('cards.ndviHealthCard')}
|
||||
subheader='Vegetation Index'
|
||||
subheader={t('subheaders.vegetationIndex')}
|
||||
sx={{ '& .MuiCardHeader-avatar': { mr: 3 } }}
|
||||
/>
|
||||
<CardContent className='flex flex-col sm:flex-row items-center justify-between gap-6'>
|
||||
<div className='flex flex-col gap-4 is-full sm:is-[unset]'>
|
||||
<Typography variant='h2'>{ndviIndex}</Typography>
|
||||
<Typography variant='body2'>NDVI Index (0-1)</Typography>
|
||||
<Typography variant='body2'>{t('labels.ndviIndex')}</Typography>
|
||||
{healthData.length > 0 && (
|
||||
<div className='flex flex-col gap-3'>
|
||||
{healthData.map((item, index) => (
|
||||
|
||||
@@ -36,8 +36,8 @@ const RecommendationsList = ({ data }: RecommendationsListProps) => {
|
||||
<Card>
|
||||
<CardHeader
|
||||
title={t('cards.recommendationsList')}
|
||||
subheader='Action Items'
|
||||
action={<OptionMenu options={['Export', 'Snooze', 'Mark Done']} />}
|
||||
subheader={t('subheaders.actionItems')}
|
||||
action={<OptionMenu options={[t('optionMenu.export'), t('optionMenu.snooze'), t('optionMenu.markDone')]} />}
|
||||
/>
|
||||
<CardContent className='flex flex-col gap-4'>
|
||||
{recommendations.map((item, index) => (
|
||||
|
||||
@@ -24,9 +24,11 @@ interface SensorComparisonChartProps {
|
||||
const SensorComparisonChart = ({ data }: SensorComparisonChartProps) => {
|
||||
const t = useTranslations('farmDashboard')
|
||||
const series = (data?.series as Array<{ name: string; data: number[] }>) ?? []
|
||||
const categories = (data?.categories as string[]) ?? ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||||
const categories =
|
||||
(data?.categories as string[]) ??
|
||||
[t('fallback.mon'), t('fallback.tue'), t('fallback.wed'), t('fallback.thu'), t('fallback.fri'), t('fallback.sat'), t('fallback.sun')]
|
||||
const currentValue = data?.currentValue ?? 48
|
||||
const vsLastWeek = (data?.vsLastWeek as string) ?? '+5% vs last week'
|
||||
const vsLastWeek = (data?.vsLastWeek as string) ?? t('fallback.plusPercentVsLastWeek', { val: '5' })
|
||||
const theme = useTheme()
|
||||
if (series.length === 0) return null
|
||||
|
||||
@@ -78,7 +80,7 @@ const SensorComparisonChart = ({ data }: SensorComparisonChartProps) => {
|
||||
<Card>
|
||||
<CardHeader
|
||||
title={t('cards.sensorComparisonChart')}
|
||||
subheader='Today vs Last Week'
|
||||
subheader={t('subheaders.todayVsLastWeek')}
|
||||
/>
|
||||
<CardContent>
|
||||
<div className='flex items-center gap-4 mbe-4'>
|
||||
|
||||
@@ -73,8 +73,8 @@ const SensorRadarChart = ({ data }: SensorRadarChartProps) => {
|
||||
<Card>
|
||||
<CardHeader
|
||||
title={t('cards.sensorRadarChart')}
|
||||
subheader='Today vs Ideal Ranges'
|
||||
action={<OptionMenu options={['Today', 'This Week', 'This Month']} />}
|
||||
subheader={t('subheaders.todayVsIdealRanges')}
|
||||
action={<OptionMenu options={[t('optionMenu.today'), t('optionMenu.thisWeek'), t('optionMenu.thisMonth')]} />}
|
||||
/>
|
||||
<CardContent>
|
||||
<AppReactApexCharts type='radar' height={373} width='100%' series={series} options={options} />
|
||||
|
||||
@@ -36,8 +36,8 @@ const SensorValuesList = ({ data }: SensorValuesListProps) => {
|
||||
<Card>
|
||||
<CardHeader
|
||||
title={t('cards.sensorValuesList')}
|
||||
subheader='Real-time Data'
|
||||
action={<OptionMenu options={['Last Hour', 'Last 24h', 'Last 7 Days']} />}
|
||||
subheader={t('subheaders.realtimeData')}
|
||||
action={<OptionMenu options={[t('optionMenu.lastHour'), t('optionMenu.last24h'), t('optionMenu.last7Days')]} />}
|
||||
/>
|
||||
<CardContent className='flex flex-col gap-4'>
|
||||
{sensors.map((item, index) => (
|
||||
|
||||
@@ -52,10 +52,10 @@ const SoilMoistureHeatmap = ({ data }: SoilMoistureHeatmapProps) => {
|
||||
enableShades: false,
|
||||
colorScale: {
|
||||
ranges: [
|
||||
{ from: 0, to: 30, name: 'Low', color: '#ff6b6b' },
|
||||
{ from: 31, to: 50, name: 'Moderate', color: '#ffd93d' },
|
||||
{ from: 51, to: 70, name: 'Optimal', color: '#6bcb77' },
|
||||
{ from: 71, to: 100, name: 'High', color: '#4d96ff' }
|
||||
{ from: 0, to: 30, name: t('colorScale.low'), color: '#ff6b6b' },
|
||||
{ from: 31, to: 50, name: t('colorScale.moderate'), color: '#ffd93d' },
|
||||
{ from: 51, to: 70, name: t('colorScale.optimal'), color: '#6bcb77' },
|
||||
{ from: 71, to: 100, name: t('colorScale.high'), color: '#4d96ff' }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ const SoilMoistureHeatmap = ({ data }: SoilMoistureHeatmapProps) => {
|
||||
<Card>
|
||||
<CardHeader
|
||||
title={t('cards.soilMoistureHeatmap')}
|
||||
subheader='Field Zones by Time'
|
||||
subheader={t('subheaders.fieldZonesByTime')}
|
||||
/>
|
||||
<CardContent>
|
||||
<AppReactApexCharts type='heatmap' width='100%' height={350} options={options} series={series} />
|
||||
|
||||
@@ -29,7 +29,9 @@ interface WaterNeedPredictionProps {
|
||||
const WaterNeedPrediction = ({ data }: WaterNeedPredictionProps) => {
|
||||
const t = useTranslations('farmDashboard')
|
||||
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 categories =
|
||||
(data?.categories as string[]) ??
|
||||
[1, 2, 3, 4, 5, 6, 7].map(n => t('fallback.dayN', { n: String(n) }))
|
||||
const totalNext7Days = data?.totalNext7Days ?? 0
|
||||
const unit = (data?.unit as string) ?? 'm³'
|
||||
const totalFormatted = typeof totalNext7Days === 'number' ? totalNext7Days.toLocaleString() : String(totalNext7Days)
|
||||
@@ -79,8 +81,8 @@ const WaterNeedPrediction = ({ data }: WaterNeedPredictionProps) => {
|
||||
<Card>
|
||||
<CardHeader
|
||||
title={t('cards.waterNeedPrediction')}
|
||||
subheader='AI Forecast'
|
||||
action={<OptionMenu options={['Export', 'Adjust', 'Details']} />}
|
||||
subheader={t('subheaders.aiForecast')}
|
||||
action={<OptionMenu options={[t('optionMenu.export'), t('optionMenu.adjust'), t('optionMenu.details')]} />}
|
||||
/>
|
||||
<CardContent>
|
||||
<div className='flex items-center gap-4 mbe-4'>
|
||||
@@ -88,7 +90,7 @@ const WaterNeedPrediction = ({ data }: WaterNeedPredictionProps) => {
|
||||
{totalFormatted} {unit}
|
||||
</Typography>
|
||||
<Typography variant='body2' color='text.secondary'>
|
||||
Total next 7 days
|
||||
{t('labels.totalNext7Days')}
|
||||
</Typography>
|
||||
</div>
|
||||
<AppReactApexCharts type='area' height={250} width='100%' series={series} options={options} />
|
||||
|
||||
@@ -39,7 +39,20 @@ 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']
|
||||
[
|
||||
t('fallback.monthJan'),
|
||||
t('fallback.monthFeb'),
|
||||
t('fallback.monthMar'),
|
||||
t('fallback.monthApr'),
|
||||
t('fallback.monthMay'),
|
||||
t('fallback.monthJun'),
|
||||
t('fallback.monthJul'),
|
||||
t('fallback.monthAug'),
|
||||
t('fallback.monthSep'),
|
||||
t('fallback.monthOct'),
|
||||
t('fallback.monthNov'),
|
||||
t('fallback.monthDec')
|
||||
]
|
||||
const summary = (data?.summary as SummaryItem[]) ?? []
|
||||
const theme = useTheme()
|
||||
if (series.length === 0) return null
|
||||
@@ -68,11 +81,11 @@ const YieldPredictionChart = ({ data }: YieldPredictionChartProps) => {
|
||||
yaxis: {
|
||||
labels: {
|
||||
style: { colors: 'var(--mui-palette-text-disabled)' },
|
||||
formatter: (val: number) => `${val}t`
|
||||
formatter: (val: number) => `${val} ${t('labels.tons')}`
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
y: { formatter: (val: number) => `${val} tons` }
|
||||
y: { formatter: (val: number) => `${val} ${t('labels.tons')}` }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,8 +93,8 @@ const YieldPredictionChart = ({ data }: YieldPredictionChartProps) => {
|
||||
<Card>
|
||||
<CardHeader
|
||||
title={t('cards.yieldPredictionChart')}
|
||||
subheader='This Year vs Last Year'
|
||||
action={<OptionMenu options={['Export', 'Compare', 'Details']} />}
|
||||
subheader={t('subheaders.thisYearVsLastYear')}
|
||||
action={<OptionMenu options={[t('optionMenu.export'), t('optionMenu.compare'), t('optionMenu.details')]} />}
|
||||
/>
|
||||
<CardContent className='flex flex-col gap-4'>
|
||||
<AppReactApexCharts type='line' height={280} width='100%' series={series} options={options} />
|
||||
|
||||
Reference in New Issue
Block a user