feat: add internationalization support for TeamLeadsDashboard with translations for various labels and messages

This commit is contained in:
2025-11-14 15:22:36 +01:00
parent 1d8ee8b0ab
commit acb7117c7d
2 changed files with 72 additions and 26 deletions

View File

@@ -2,8 +2,10 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, BarChart, Bar, ComposedChart, PieChart, Pie, Cell } from 'recharts'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, BarChart, Bar, ComposedChart, PieChart, Pie, Cell } from 'recharts';
import { useTranslation } from "@/lib/i18n";
export default function TeamLeadsDashboard() { export default function TeamLeadsDashboard() {
const { t } = useTranslation();
const [chartData, setChartData] = useState([]); const [chartData, setChartData] = useState([]);
const [summaryData, setSummaryData] = useState(null); const [summaryData, setSummaryData] = useState(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@@ -63,12 +65,12 @@ export default function TeamLeadsDashboard() {
return ( return (
<div className="bg-white dark:bg-gray-800 p-3 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg"> <div className="bg-white dark:bg-gray-800 p-3 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg">
<p className="font-medium text-gray-900 dark:text-white">{`Month: ${label}`}</p> <p className="font-medium text-gray-900 dark:text-white">{`${t('teamDashboard.monthLabel')} ${label}`}</p>
<p className="text-blue-600 dark:text-blue-400 font-semibold"> <p className="text-blue-600 dark:text-blue-400 font-semibold">
{`Monthly Value: ${monthlyData ? formatCurrency(monthlyData.value) : 'N/A'}`} {`${t('teamDashboard.monthlyValue')} ${monthlyData ? formatCurrency(monthlyData.value) : t('teamDashboard.na')}`}
</p> </p>
<p className="text-green-600 dark:text-green-400 text-sm"> <p className="text-green-600 dark:text-green-400 text-sm">
{`Cumulative: ${cumulativeData ? formatCurrency(cumulativeData.value) : 'N/A'}`} {`${t('teamDashboard.cumulative')} ${cumulativeData ? formatCurrency(cumulativeData.value) : t('teamDashboard.na')}`}
</p> </p>
</div> </div>
); );
@@ -80,13 +82,13 @@ export default function TeamLeadsDashboard() {
<div className="container mx-auto px-4 py-8"> <div className="container mx-auto px-4 py-8">
<div className="flex items-center justify-between mb-8"> <div className="flex items-center justify-between mb-8">
<h1 className="text-3xl font-bold text-gray-900 dark:text-white"> <h1 className="text-3xl font-bold text-gray-900 dark:text-white">
Dashboard {t('teamDashboard.title')}
</h1> </h1>
{/* Year Filter */} {/* Year Filter */}
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<label htmlFor="year-select" className="text-sm font-medium text-gray-700 dark:text-gray-300"> <label htmlFor="year-select" className="text-sm font-medium text-gray-700 dark:text-gray-300">
Year: {t('teamDashboard.yearLabel')}
</label> </label>
<select <select
id="year-select" id="year-select"
@@ -94,7 +96,7 @@ export default function TeamLeadsDashboard() {
onChange={(e) => handleYearChange(e.target.value)} onChange={(e) => handleYearChange(e.target.value)}
className="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-white" className="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
> >
<option value="all">All Years</option> <option value="all">{t('teamDashboard.allYears')}</option>
{availableYears.map(year => ( {availableYears.map(year => (
<option key={year} value={year}>{year}</option> <option key={year} value={year}>{year}</option>
))} ))}
@@ -104,20 +106,20 @@ export default function TeamLeadsDashboard() {
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6"> <div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-6"> <h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-6">
Project Completion Value Over Time {t('teamDashboard.projectCompletionTitle')}
</h2> </h2>
{loading ? ( {loading ? (
<div className="flex items-center justify-center h-64"> <div className="flex items-center justify-center h-64">
<div className="text-gray-500 dark:text-gray-400">Loading chart data...</div> <div className="text-gray-500 dark:text-gray-400">{t('teamDashboard.loadingChart')}</div>
</div> </div>
) : error ? ( ) : error ? (
<div className="flex items-center justify-center h-64"> <div className="flex items-center justify-center h-64">
<div className="text-red-500 dark:text-red-400">Error: {error}</div> <div className="text-red-500 dark:text-red-400">{t('teamDashboard.errorPrefix')} {error}</div>
</div> </div>
) : chartData.length === 0 ? ( ) : chartData.length === 0 ? (
<div className="flex items-center justify-center h-64"> <div className="flex items-center justify-center h-64">
<div className="text-gray-500 dark:text-gray-400">No completed projects data available</div> <div className="text-gray-500 dark:text-gray-400">{t('teamDashboard.noData')}</div>
</div> </div>
) : ( ) : (
<div className="h-96"> <div className="h-96">
@@ -147,7 +149,7 @@ export default function TeamLeadsDashboard() {
<Bar <Bar
dataKey="value" dataKey="value"
fill="#3b82f6" fill="#3b82f6"
name="Monthly Value" name={t('teamDashboard.monthlyValue').replace(':', '')}
radius={[4, 4, 0, 0]} radius={[4, 4, 0, 0]}
/> />
<Line <Line
@@ -155,7 +157,7 @@ export default function TeamLeadsDashboard() {
dataKey="cumulative" dataKey="cumulative"
stroke="#10b981" stroke="#10b981"
strokeWidth={3} strokeWidth={3}
name="Cumulative Value" name={t('teamDashboard.cumulative').replace(':', '')}
dot={{ fill: '#10b981', strokeWidth: 2, r: 4 }} dot={{ fill: '#10b981', strokeWidth: 2, r: 4 }}
activeDot={{ r: 6, stroke: '#10b981', strokeWidth: 2 }} activeDot={{ r: 6, stroke: '#10b981', strokeWidth: 2 }}
/> />
@@ -168,7 +170,7 @@ export default function TeamLeadsDashboard() {
{/* Main Total Chart */} {/* Main Total Chart */}
<div className="lg:col-span-1"> <div className="lg:col-span-1">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-6"> <h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-6">
Total Portfolio {t('teamDashboard.totalPortfolio')}
</h2> </h2>
{summaryData?.total ? ( {summaryData?.total ? (
@@ -178,12 +180,12 @@ export default function TeamLeadsDashboard() {
<Pie <Pie
data={[ data={[
{ {
name: 'Realised', name: t('teamDashboard.realised'),
value: summaryData.total.realisedValue, value: summaryData.total.realisedValue,
color: '#10b981' color: '#10b981'
}, },
{ {
name: 'Unrealised', name: t('teamDashboard.unrealised'),
value: summaryData.total.unrealisedValue, value: summaryData.total.unrealisedValue,
color: '#f59e0b' color: '#f59e0b'
} }
@@ -204,20 +206,20 @@ export default function TeamLeadsDashboard() {
</div> </div>
) : ( ) : (
<div className="flex items-center justify-center h-64"> <div className="flex items-center justify-center h-64">
<div className="text-gray-500 dark:text-gray-400">No summary data available</div> <div className="text-gray-500 dark:text-gray-400">{t('teamDashboard.noSummaryData')}</div>
</div> </div>
)} )}
{summaryData?.total && ( {summaryData?.total && (
<div className="mt-4 grid grid-cols-1 gap-3"> <div className="mt-4 grid grid-cols-1 gap-3">
<div className="bg-green-50 dark:bg-green-900/20 p-3 rounded-lg"> <div className="bg-green-50 dark:bg-green-900/20 p-3 rounded-lg">
<div className="text-sm text-green-600 dark:text-green-400 font-medium">Realised Value</div> <div className="text-sm text-green-600 dark:text-green-400 font-medium">{t('teamDashboard.realisedValue')}</div>
<div className="text-xl font-bold text-green-700 dark:text-green-300"> <div className="text-xl font-bold text-green-700 dark:text-green-300">
{formatCurrency(summaryData.total.realisedValue)} {formatCurrency(summaryData.total.realisedValue)}
</div> </div>
</div> </div>
<div className="bg-amber-50 dark:bg-amber-900/20 p-3 rounded-lg"> <div className="bg-amber-50 dark:bg-amber-900/20 p-3 rounded-lg">
<div className="text-sm text-amber-600 dark:text-amber-400 font-medium">Unrealised Value</div> <div className="text-sm text-amber-600 dark:text-amber-400 font-medium">{t('teamDashboard.unrealisedValue')}</div>
<div className="text-xl font-bold text-amber-700 dark:text-amber-300"> <div className="text-xl font-bold text-amber-700 dark:text-amber-300">
{formatCurrency(summaryData.total.unrealisedValue)} {formatCurrency(summaryData.total.unrealisedValue)}
</div> </div>
@@ -229,7 +231,7 @@ export default function TeamLeadsDashboard() {
{/* Project Type Charts */} {/* Project Type Charts */}
<div className="lg:col-span-2"> <div className="lg:col-span-2">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-6"> <h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-6">
By Project Type {t('teamDashboard.byProjectType')}
</h2> </h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
@@ -245,8 +247,8 @@ export default function TeamLeadsDashboard() {
<PieChart> <PieChart>
<Pie <Pie
data={[ data={[
{ name: 'Realised', value: data.realisedValue, color: '#10b981' }, { name: t('teamDashboard.realised'), value: data.realisedValue, color: '#10b981' },
{ name: 'Unrealised', value: data.unrealisedValue, color: '#f59e0b' } { name: t('teamDashboard.unrealised'), value: data.unrealisedValue, color: '#f59e0b' }
]} ]}
cx="50%" cx="50%"
cy="50%" cy="50%"
@@ -264,13 +266,13 @@ export default function TeamLeadsDashboard() {
<div className="space-y-3"> <div className="space-y-3">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<span className="text-sm text-green-600 dark:text-green-400">Realised</span> <span className="text-sm text-green-600 dark:text-green-400">{t('teamDashboard.realised')}</span>
<span className="text-sm font-semibold text-green-700 dark:text-green-300"> <span className="text-sm font-semibold text-green-700 dark:text-green-300">
{formatCurrency(data.realisedValue)} {formatCurrency(data.realisedValue)}
</span> </span>
</div> </div>
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<span className="text-sm text-amber-600 dark:text-amber-400">Unrealised</span> <span className="text-sm text-amber-600 dark:text-amber-400">{t('teamDashboard.unrealised')}</span>
<span className="text-sm font-semibold text-amber-700 dark:text-amber-300"> <span className="text-sm font-semibold text-amber-700 dark:text-amber-300">
{formatCurrency(data.unrealisedValue)} {formatCurrency(data.unrealisedValue)}
</span> </span>

View File

@@ -116,6 +116,28 @@ const translations = {
noUpcomingDeadlines: "Brak nadchodzących terminów" noUpcomingDeadlines: "Brak nadchodzących terminów"
}, },
// Team Leads Dashboard
teamDashboard: {
title: "Dashboard",
yearLabel: "Rok:",
allYears: "Wszystkie lata",
projectCompletionTitle: "Wartość ukończenia projektów w czasie",
loadingChart: "Ładowanie danych wykresu...",
errorPrefix: "Błąd:",
noData: "Brak dostępnych danych ukończonych projektów",
totalPortfolio: "Całkowity portfel",
realised: "Zrealizowane",
unrealised: "Niezrealizowane",
noSummaryData: "Brak dostępnych danych podsumowania",
realisedValue: "Wartość zrealizowana",
unrealisedValue: "Wartość niezrealizowana",
byProjectType: "Według typu projektu",
monthLabel: "Miesiąc:",
monthlyValue: "Wartość miesięczna:",
cumulative: "Skumulowana:",
na: "N/D"
},
// Project statuses // Project statuses
projectStatus: { projectStatus: {
registered: "Zarejestrowany", registered: "Zarejestrowany",
@@ -695,6 +717,28 @@ const translations = {
noUpcomingDeadlines: "No upcoming deadlines" noUpcomingDeadlines: "No upcoming deadlines"
}, },
// Team Leads Dashboard
teamDashboard: {
title: "Dashboard",
yearLabel: "Year:",
allYears: "All Years",
projectCompletionTitle: "Project Completion Value Over Time",
loadingChart: "Loading chart data...",
errorPrefix: "Error:",
noData: "No completed projects data available",
totalPortfolio: "Total Portfolio",
realised: "Realised",
unrealised: "Unrealised",
noSummaryData: "No summary data available",
realisedValue: "Realised Value",
unrealisedValue: "Unrealised Value",
byProjectType: "By Project Type",
monthLabel: "Month:",
monthlyValue: "Monthly Value:",
cumulative: "Cumulative:",
na: "N/A"
},
projectStatus: { projectStatus: {
registered: "Registered", registered: "Registered",
in_progress_design: "In Progress (Design)", in_progress_design: "In Progress (Design)",