localization reports page
This commit is contained in:
@@ -1254,6 +1254,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"all_shifts_title": "ALL SHIFTS",
|
"all_shifts_title": "ALL SHIFTS",
|
||||||
|
"no_shifts_today": "No shifts scheduled for today",
|
||||||
"shift_item": {
|
"shift_item": {
|
||||||
"time": "Time",
|
"time": "Time",
|
||||||
"workers": "Workers",
|
"workers": "Workers",
|
||||||
@@ -1289,6 +1290,7 @@
|
|||||||
"sun": "Sun"
|
"sun": "Sun"
|
||||||
},
|
},
|
||||||
"spend_by_industry": "Spend by Industry",
|
"spend_by_industry": "Spend by Industry",
|
||||||
|
"no_industry_data": "No industry data available",
|
||||||
"industries": {
|
"industries": {
|
||||||
"hospitality": "Hospitality",
|
"hospitality": "Hospitality",
|
||||||
"events": "Events",
|
"events": "Events",
|
||||||
@@ -1319,41 +1321,16 @@
|
|||||||
},
|
},
|
||||||
"forecast_report": {
|
"forecast_report": {
|
||||||
"title": "Forecast Report",
|
"title": "Forecast Report",
|
||||||
"subtitle": "Next 4 weeks projection",
|
"subtitle": "Projected spend & staffing",
|
||||||
"summary": {
|
"metrics": {
|
||||||
"four_week": "4-Week Forecast",
|
"projected_spend": "Projected Spend",
|
||||||
"avg_weekly": "Avg Weekly",
|
"workers_needed": "Workers Needed"
|
||||||
"total_shifts": "Total Shifts",
|
|
||||||
"total_hours": "Total Hours",
|
|
||||||
"total_projected": "Total projected",
|
|
||||||
"per_week": "Per week",
|
|
||||||
"scheduled": "Scheduled",
|
|
||||||
"worker_hours": "Worker hours"
|
|
||||||
},
|
},
|
||||||
"spending_forecast": "Spending Forecast",
|
"chart_title": "Spending Forecast",
|
||||||
"weekly_breakdown": "WEEKLY BREAKDOWN",
|
"daily_projections": "DAILY PROJECTIONS",
|
||||||
"breakdown_headings": {
|
"empty_state": "No projections available",
|
||||||
"shifts": "Shifts",
|
"shift_item": {
|
||||||
"hours": "Hours",
|
"workers_needed": "$count workers needed"
|
||||||
"avg_shift": "Avg/Shift"
|
|
||||||
},
|
|
||||||
"insights": {
|
|
||||||
"title": "Forecast Insights",
|
|
||||||
"insight_1": {
|
|
||||||
"prefix": "Demand is expected to spike by ",
|
|
||||||
"highlight": "25%",
|
|
||||||
"suffix": " in week 3"
|
|
||||||
},
|
|
||||||
"insight_2": {
|
|
||||||
"prefix": "Projected spend for next month is ",
|
|
||||||
"highlight": "USD 68.4k",
|
|
||||||
"suffix": ""
|
|
||||||
},
|
|
||||||
"insight_3": {
|
|
||||||
"prefix": "Consider increasing budget for ",
|
|
||||||
"highlight": "Holiday Season",
|
|
||||||
"suffix": " coverage"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"export_message": "Exporting Forecast Report (Placeholder)"
|
"export_message": "Exporting Forecast Report (Placeholder)"
|
||||||
@@ -1364,7 +1341,9 @@
|
|||||||
"subtitle": "Key metrics & benchmarks",
|
"subtitle": "Key metrics & benchmarks",
|
||||||
"overall_score": {
|
"overall_score": {
|
||||||
"title": "Overall Performance Score",
|
"title": "Overall Performance Score",
|
||||||
"label": "Excellent"
|
"excellent": "Excellent",
|
||||||
|
"good": "Good",
|
||||||
|
"needs_work": "Needs Work"
|
||||||
},
|
},
|
||||||
"kpis_title": "KEY PERFORMANCE INDICATORS",
|
"kpis_title": "KEY PERFORMANCE INDICATORS",
|
||||||
"kpis": {
|
"kpis": {
|
||||||
@@ -1373,8 +1352,11 @@
|
|||||||
"on_time_rate": "On-Time Rate",
|
"on_time_rate": "On-Time Rate",
|
||||||
"avg_fill_time": "Avg Fill Time",
|
"avg_fill_time": "Avg Fill Time",
|
||||||
"target_prefix": "Target: ",
|
"target_prefix": "Target: ",
|
||||||
|
"target_hours": "$hours hrs",
|
||||||
|
"target_percent": "$percent%",
|
||||||
"met": "✓ Met",
|
"met": "✓ Met",
|
||||||
"close": "↗ Close"
|
"close": "→ Close",
|
||||||
|
"miss": "✗ Miss"
|
||||||
},
|
},
|
||||||
"additional_metrics_title": "ADDITIONAL METRICS",
|
"additional_metrics_title": "ADDITIONAL METRICS",
|
||||||
"additional_metrics": {
|
"additional_metrics": {
|
||||||
@@ -1425,13 +1407,13 @@
|
|||||||
"title": "Reliability Insights",
|
"title": "Reliability Insights",
|
||||||
"insight_1": {
|
"insight_1": {
|
||||||
"prefix": "Your no-show rate of ",
|
"prefix": "Your no-show rate of ",
|
||||||
"highlight": "1.2%",
|
"highlight": "$rate%",
|
||||||
"suffix": " is below industry average"
|
"suffix": " is $comparison industry average"
|
||||||
},
|
},
|
||||||
"insight_2": {
|
"insight_2": {
|
||||||
"prefix": "",
|
"prefix": "",
|
||||||
"highlight": "1 worker",
|
"highlight": "$count worker(s)",
|
||||||
"suffix": " has multiple incidents this month"
|
"suffix": " have multiple incidents this month"
|
||||||
},
|
},
|
||||||
"insight_3": {
|
"insight_3": {
|
||||||
"prefix": "Consider implementing ",
|
"prefix": "Consider implementing ",
|
||||||
@@ -1439,6 +1421,7 @@
|
|||||||
"suffix": " 24hrs before shifts"
|
"suffix": " 24hrs before shifts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"empty_state": "No workers flagged for no-shows",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"export_message": "Exporting No-Show Report (Placeholder)"
|
"export_message": "Exporting No-Show Report (Placeholder)"
|
||||||
}
|
}
|
||||||
@@ -1452,9 +1435,11 @@
|
|||||||
"needs_help": "Needs Help"
|
"needs_help": "Needs Help"
|
||||||
},
|
},
|
||||||
"next_7_days": "NEXT 7 DAYS",
|
"next_7_days": "NEXT 7 DAYS",
|
||||||
|
"empty_state": "No shifts scheduled",
|
||||||
"shift_item": {
|
"shift_item": {
|
||||||
"confirmed_workers": "$confirmed/$needed workers confirmed",
|
"confirmed_workers": "$confirmed/$needed workers confirmed",
|
||||||
"spots_remaining": "$count spots remaining",
|
"spots_remaining": "$count spots remaining",
|
||||||
|
"one_spot_remaining": "1 spot remaining",
|
||||||
"fully_staffed": "Fully staffed"
|
"fully_staffed": "Fully staffed"
|
||||||
},
|
},
|
||||||
"insights": {
|
"insights": {
|
||||||
|
|||||||
@@ -175,21 +175,21 @@ class _CoverageReportPageState extends State<CoverageReportPage> {
|
|||||||
children: [
|
children: [
|
||||||
_CoverageStatCard(
|
_CoverageStatCard(
|
||||||
icon: UiIcons.trendingUp,
|
icon: UiIcons.trendingUp,
|
||||||
label: 'Avg Coverage',
|
label: context.t.client_reports.coverage_report.metrics.avg_coverage,
|
||||||
value: '${report.overallCoverage.toStringAsFixed(0)}%',
|
value: '${report.overallCoverage.toStringAsFixed(0)}%',
|
||||||
iconColor: UiColors.primary,
|
iconColor: UiColors.primary,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
_CoverageStatCard(
|
_CoverageStatCard(
|
||||||
icon: UiIcons.checkCircle,
|
icon: UiIcons.checkCircle,
|
||||||
label: 'Full',
|
label: context.t.client_reports.coverage_report.metrics.full,
|
||||||
value: fullDays.toString(),
|
value: fullDays.toString(),
|
||||||
iconColor: UiColors.success,
|
iconColor: UiColors.success,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
_CoverageStatCard(
|
_CoverageStatCard(
|
||||||
icon: UiIcons.warning,
|
icon: UiIcons.warning,
|
||||||
label: 'Needs Help',
|
label: context.t.client_reports.coverage_report.metrics.needs_help,
|
||||||
value: needsHelpDays.toString(),
|
value: needsHelpDays.toString(),
|
||||||
iconColor: UiColors.error,
|
iconColor: UiColors.error,
|
||||||
),
|
),
|
||||||
@@ -209,8 +209,8 @@ class _CoverageReportPageState extends State<CoverageReportPage> {
|
|||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
|
|
||||||
// Section label
|
// Section label
|
||||||
const Text(
|
Text(
|
||||||
'NEXT 7 DAYS',
|
context.t.client_reports.coverage_report.next_7_days,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -224,9 +224,9 @@ class _CoverageReportPageState extends State<CoverageReportPage> {
|
|||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(40),
|
padding: const EdgeInsets.all(40),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: const Text(
|
child: Text(
|
||||||
'No shifts scheduled',
|
context.t.client_reports.coverage_report.empty_state,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
color: UiColors.textSecondary,
|
color: UiColors.textSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -398,7 +398,7 @@ class _DayCoverageCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
'$filled/$needed workers confirmed',
|
context.t.client_reports.coverage_report.shift_item.confirmed_workers(confirmed: filled.toString(), needed: needed.toString()),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: UiColors.textSecondary,
|
color: UiColors.textSecondary,
|
||||||
@@ -442,8 +442,10 @@ class _DayCoverageCard extends StatelessWidget {
|
|||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: Text(
|
child: Text(
|
||||||
isFullyStaffed
|
isFullyStaffed
|
||||||
? 'Fully staffed'
|
? context.t.client_reports.coverage_report.shift_item.fully_staffed
|
||||||
: '$spotsRemaining spot${spotsRemaining != 1 ? 's' : ''} remaining',
|
: spotsRemaining == 1
|
||||||
|
? context.t.client_reports.coverage_report.shift_item.one_spot_remaining
|
||||||
|
: context.t.client_reports.coverage_report.shift_item.spots_remaining(count: spotsRemaining.toString()),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: isFullyStaffed
|
color: isFullyStaffed
|
||||||
|
|||||||
@@ -335,7 +335,7 @@ class _DailyOpsReportPageState extends State<DailyOpsReportPage> {
|
|||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 40),
|
padding: EdgeInsets.symmetric(vertical: 40),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text('No shifts scheduled for today'),
|
child: Text(context.t.client_reports.daily_ops_report.no_shifts_today),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
@@ -579,7 +579,7 @@ class _ShiftListItem extends StatelessWidget {
|
|||||||
_infoItem(
|
_infoItem(
|
||||||
context,
|
context,
|
||||||
UiIcons.trendingUp,
|
UiIcons.trendingUp,
|
||||||
'Rate',
|
context.t.client_reports.daily_ops_report.shift_item.rate,
|
||||||
rate),
|
rate),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ class _ForecastReportPageState extends State<ForecastReportPage> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _ForecastSummaryCard(
|
child: _ForecastSummaryCard(
|
||||||
label: 'Projected Spend',
|
label: context.t.client_reports.forecast_report.metrics.projected_spend,
|
||||||
value: NumberFormat.currency(symbol: r'$')
|
value: NumberFormat.currency(symbol: r'$')
|
||||||
.format(report.projectedSpend),
|
.format(report.projectedSpend),
|
||||||
icon: UiIcons.dollar,
|
icon: UiIcons.dollar,
|
||||||
@@ -130,7 +130,7 @@ class _ForecastReportPageState extends State<ForecastReportPage> {
|
|||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _ForecastSummaryCard(
|
child: _ForecastSummaryCard(
|
||||||
label: 'Workers Needed',
|
label: context.t.client_reports.forecast_report.metrics.workers_needed,
|
||||||
value: report.projectedWorkers.toString(),
|
value: report.projectedWorkers.toString(),
|
||||||
icon: UiIcons.users,
|
icon: UiIcons.users,
|
||||||
color: UiColors.primary,
|
color: UiColors.primary,
|
||||||
@@ -158,7 +158,7 @@ class _ForecastReportPageState extends State<ForecastReportPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Spending Forecast',
|
context.t.client_reports.forecast_report.chart_title,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -178,7 +178,7 @@ class _ForecastReportPageState extends State<ForecastReportPage> {
|
|||||||
|
|
||||||
// Daily List
|
// Daily List
|
||||||
Text(
|
Text(
|
||||||
'DAILY PROJECTIONS',
|
context.t.client_reports.forecast_report.daily_projections,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -188,7 +188,7 @@ class _ForecastReportPageState extends State<ForecastReportPage> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
if (report.chartData.isEmpty)
|
if (report.chartData.isEmpty)
|
||||||
const Center(child: Text('No projections available'))
|
Center(child: Text(context.t.client_reports.forecast_report.empty_state))
|
||||||
else
|
else
|
||||||
...report.chartData.map((point) => _ForecastListItem(
|
...report.chartData.map((point) => _ForecastListItem(
|
||||||
date: DateFormat('EEE, MMM dd').format(point.date),
|
date: DateFormat('EEE, MMM dd').format(point.date),
|
||||||
@@ -348,7 +348,7 @@ class _ForecastListItem extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(date, style: const TextStyle(fontWeight: FontWeight.bold)),
|
Text(date, style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
Text('$workers workers needed', style: const TextStyle(fontSize: 11, color: UiColors.textSecondary)),
|
Text(context.t.client_reports.forecast_report.shift_item.workers_needed(count: workers), style: const TextStyle(fontSize: 11, color: UiColors.textSecondary)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(cost, style: const TextStyle(fontWeight: FontWeight.bold, color: UiColors.primary)),
|
Text(cost, style: const TextStyle(fontWeight: FontWeight.bold, color: UiColors.primary)),
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ class _NoShowReportPageState extends State<NoShowReportPage> {
|
|||||||
child: _SummaryChip(
|
child: _SummaryChip(
|
||||||
icon: UiIcons.warning,
|
icon: UiIcons.warning,
|
||||||
iconColor: UiColors.error,
|
iconColor: UiColors.error,
|
||||||
label: 'No-Shows',
|
label: context.t.client_reports.no_show_report.metrics.no_shows,
|
||||||
value: report.totalNoShows.toString(),
|
value: report.totalNoShows.toString(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -167,7 +167,7 @@ class _NoShowReportPageState extends State<NoShowReportPage> {
|
|||||||
child: _SummaryChip(
|
child: _SummaryChip(
|
||||||
icon: UiIcons.trendingUp,
|
icon: UiIcons.trendingUp,
|
||||||
iconColor: UiColors.textWarning,
|
iconColor: UiColors.textWarning,
|
||||||
label: 'Rate',
|
label: context.t.client_reports.no_show_report.metrics.rate,
|
||||||
value:
|
value:
|
||||||
'${report.noShowRate.toStringAsFixed(1)}%',
|
'${report.noShowRate.toStringAsFixed(1)}%',
|
||||||
),
|
),
|
||||||
@@ -177,7 +177,7 @@ class _NoShowReportPageState extends State<NoShowReportPage> {
|
|||||||
child: _SummaryChip(
|
child: _SummaryChip(
|
||||||
icon: UiIcons.user,
|
icon: UiIcons.user,
|
||||||
iconColor: UiColors.primary,
|
iconColor: UiColors.primary,
|
||||||
label: 'Workers',
|
label: context.t.client_reports.no_show_report.metrics.workers,
|
||||||
value: uniqueWorkers.toString(),
|
value: uniqueWorkers.toString(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -204,9 +204,9 @@ class _NoShowReportPageState extends State<NoShowReportPage> {
|
|||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(40),
|
padding: const EdgeInsets.all(40),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: const Text(
|
child: Text(
|
||||||
'No workers flagged for no-shows',
|
context.t.client_reports.no_show_report.empty_state,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
color: UiColors.textSecondary,
|
color: UiColors.textSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -232,9 +232,9 @@ class _NoShowReportPageState extends State<NoShowReportPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
Text(
|
||||||
'💡 Reliability Insights',
|
'💡 ${context.t.client_reports.no_show_report.insights.title}',
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: UiColors.textPrimary,
|
color: UiColors.textPrimary,
|
||||||
@@ -243,21 +243,19 @@ class _NoShowReportPageState extends State<NoShowReportPage> {
|
|||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
_InsightLine(
|
_InsightLine(
|
||||||
text:
|
text:
|
||||||
'· Your no-show rate of ${report.noShowRate.toStringAsFixed(1)}% is '
|
'· ${context.t.client_reports.no_show_report.insights.insight_1.prefix}${context.t.client_reports.no_show_report.insights.insight_1.highlight(rate: report.noShowRate.toStringAsFixed(1))}${context.t.client_reports.no_show_report.insights.insight_1.suffix(comparison: report.noShowRate < 5 ? 'below' : 'above')}',
|
||||||
'${report.noShowRate < 5 ? 'below' : 'above'} industry average',
|
|
||||||
),
|
),
|
||||||
if (report.flaggedWorkers.any(
|
if (report.flaggedWorkers.any(
|
||||||
(w) => w.noShowCount > 1,
|
(w) => w.noShowCount > 1,
|
||||||
))
|
))
|
||||||
_InsightLine(
|
_InsightLine(
|
||||||
text:
|
text:
|
||||||
'· ${report.flaggedWorkers.where((w) => w.noShowCount > 1).length} '
|
'· ${context.t.client_reports.no_show_report.insights.insight_2.highlight(count: report.flaggedWorkers.where((w) => w.noShowCount > 1).length.toString())} ${context.t.client_reports.no_show_report.insights.insight_2.suffix}',
|
||||||
'worker(s) have multiple incidents this month',
|
|
||||||
bold: true,
|
bold: true,
|
||||||
),
|
),
|
||||||
const _InsightLine(
|
_InsightLine(
|
||||||
text:
|
text:
|
||||||
'· Consider implementing confirmation reminders 24hrs before shifts',
|
'· ${context.t.client_reports.no_show_report.insights.insight_3.prefix}${context.t.client_reports.no_show_report.insights.insight_3.highlight}${context.t.client_reports.no_show_report.insights.insight_3.suffix}',
|
||||||
bold: true,
|
bold: true,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -352,9 +350,9 @@ class _WorkerCard extends StatelessWidget {
|
|||||||
const _WorkerCard({required this.worker});
|
const _WorkerCard({required this.worker});
|
||||||
|
|
||||||
String _riskLabel(int count) {
|
String _riskLabel(int count) {
|
||||||
if (count >= 3) return 'High Risk';
|
if (count >= 3) return context.t.client_reports.no_show_report.risks.high;
|
||||||
if (count == 2) return 'Medium Risk';
|
if (count == 2) return context.t.client_reports.no_show_report.risks.medium;
|
||||||
return 'Low Risk';
|
return context.t.client_reports.no_show_report.risks.low;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color _riskColor(int count) {
|
Color _riskColor(int count) {
|
||||||
@@ -421,7 +419,7 @@ class _WorkerCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${worker.noShowCount} no-show${worker.noShowCount > 1 ? 's' : ''}',
|
context.t.client_reports.no_show_report.no_show_count(count: worker.noShowCount.toString()),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: UiColors.textSecondary,
|
color: UiColors.textSecondary,
|
||||||
@@ -458,9 +456,9 @@ class _WorkerCard extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
Text(
|
||||||
'Latest incident',
|
context.t.client_reports.no_show_report.latest_incident,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: UiColors.textSecondary,
|
color: UiColors.textSecondary,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -50,10 +50,10 @@ class _PerformanceReportPageState extends State<PerformanceReportPage> {
|
|||||||
.clamp(0.0, 100.0);
|
.clamp(0.0, 100.0);
|
||||||
|
|
||||||
final scoreLabel = overallScore >= 90
|
final scoreLabel = overallScore >= 90
|
||||||
? 'Excellent'
|
? context.t.client_reports.performance_report.overall_score.excellent
|
||||||
: overallScore >= 75
|
: overallScore >= 75
|
||||||
? 'Good'
|
? context.t.client_reports.performance_report.overall_score.good
|
||||||
: 'Needs Work';
|
: context.t.client_reports.performance_report.overall_score.needs_work;
|
||||||
final scoreLabelColor = overallScore >= 90
|
final scoreLabelColor = overallScore >= 90
|
||||||
? UiColors.success
|
? UiColors.success
|
||||||
: overallScore >= 75
|
: overallScore >= 75
|
||||||
@@ -70,8 +70,8 @@ class _PerformanceReportPageState extends State<PerformanceReportPage> {
|
|||||||
_KpiData(
|
_KpiData(
|
||||||
icon: UiIcons.users,
|
icon: UiIcons.users,
|
||||||
iconColor: UiColors.primary,
|
iconColor: UiColors.primary,
|
||||||
label: 'Fill Rate',
|
label: context.t.client_reports.performance_report.kpis.fill_rate,
|
||||||
target: 'Target: 95%',
|
target: context.t.client_reports.performance_report.kpis.target_percent(percent: '95'),
|
||||||
value: report.fillRate,
|
value: report.fillRate,
|
||||||
displayValue: '${report.fillRate.toStringAsFixed(0)}%',
|
displayValue: '${report.fillRate.toStringAsFixed(0)}%',
|
||||||
barColor: UiColors.primary,
|
barColor: UiColors.primary,
|
||||||
@@ -81,8 +81,8 @@ class _PerformanceReportPageState extends State<PerformanceReportPage> {
|
|||||||
_KpiData(
|
_KpiData(
|
||||||
icon: UiIcons.checkCircle,
|
icon: UiIcons.checkCircle,
|
||||||
iconColor: UiColors.success,
|
iconColor: UiColors.success,
|
||||||
label: 'Completion Rate',
|
label: context.t.client_reports.performance_report.kpis.completion_rate,
|
||||||
target: 'Target: 98%',
|
target: context.t.client_reports.performance_report.kpis.target_percent(percent: '98'),
|
||||||
value: report.completionRate,
|
value: report.completionRate,
|
||||||
displayValue: '${report.completionRate.toStringAsFixed(0)}%',
|
displayValue: '${report.completionRate.toStringAsFixed(0)}%',
|
||||||
barColor: UiColors.success,
|
barColor: UiColors.success,
|
||||||
@@ -92,8 +92,8 @@ class _PerformanceReportPageState extends State<PerformanceReportPage> {
|
|||||||
_KpiData(
|
_KpiData(
|
||||||
icon: UiIcons.clock,
|
icon: UiIcons.clock,
|
||||||
iconColor: const Color(0xFF9B59B6),
|
iconColor: const Color(0xFF9B59B6),
|
||||||
label: 'On-Time Rate',
|
label: context.t.client_reports.performance_report.kpis.on_time_rate,
|
||||||
target: 'Target: 97%',
|
target: context.t.client_reports.performance_report.kpis.target_percent(percent: '97'),
|
||||||
value: report.onTimeRate,
|
value: report.onTimeRate,
|
||||||
displayValue: '${report.onTimeRate.toStringAsFixed(0)}%',
|
displayValue: '${report.onTimeRate.toStringAsFixed(0)}%',
|
||||||
barColor: const Color(0xFF9B59B6),
|
barColor: const Color(0xFF9B59B6),
|
||||||
@@ -103,8 +103,8 @@ class _PerformanceReportPageState extends State<PerformanceReportPage> {
|
|||||||
_KpiData(
|
_KpiData(
|
||||||
icon: UiIcons.trendingUp,
|
icon: UiIcons.trendingUp,
|
||||||
iconColor: const Color(0xFFF39C12),
|
iconColor: const Color(0xFFF39C12),
|
||||||
label: 'Avg Fill Time',
|
label: context.t.client_reports.performance_report.kpis.avg_fill_time,
|
||||||
target: 'Target: 3 hrs',
|
target: context.t.client_reports.performance_report.kpis.target_hours(hours: '3'),
|
||||||
// invert: lower is better — show as % of target met
|
// invert: lower is better — show as % of target met
|
||||||
value: report.avgFillTimeHours == 0
|
value: report.avgFillTimeHours == 0
|
||||||
? 100
|
? 100
|
||||||
@@ -256,8 +256,8 @@ class _PerformanceReportPageState extends State<PerformanceReportPage> {
|
|||||||
color: UiColors.primary,
|
color: UiColors.primary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
const Text(
|
Text(
|
||||||
'Overall Performance Score',
|
context.t.client_reports.performance_report.overall_score.title,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: UiColors.textSecondary,
|
color: UiColors.textSecondary,
|
||||||
@@ -313,9 +313,9 @@ class _PerformanceReportPageState extends State<PerformanceReportPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
Text(
|
||||||
'KEY PERFORMANCE INDICATORS',
|
context.t.client_reports.performance_report.kpis_title,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: UiColors.textSecondary,
|
color: UiColors.textSecondary,
|
||||||
@@ -381,10 +381,10 @@ class _KpiRow extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final badgeText = kpi.met
|
final badgeText = kpi.met
|
||||||
? '✓ Met'
|
? context.t.client_reports.performance_report.kpis.met
|
||||||
: kpi.close
|
: kpi.close
|
||||||
? '→ Close'
|
? context.t.client_reports.performance_report.kpis.close
|
||||||
: '✗ Miss';
|
: context.t.client_reports.performance_report.kpis.miss;
|
||||||
final badgeColor = kpi.met
|
final badgeColor = kpi.met
|
||||||
? UiColors.success
|
? UiColors.success
|
||||||
: kpi.close
|
: kpi.close
|
||||||
|
|||||||
@@ -220,9 +220,9 @@ class _SpendReportPageState extends State<SpendReportPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
Text(
|
||||||
'Daily Spend Trend',
|
context.t.client_reports.spend_report.chart_title,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: UiColors.textPrimary,
|
color: UiColors.textPrimary,
|
||||||
@@ -475,9 +475,9 @@ class _SpendByIndustryCard extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
Text(
|
||||||
'Spend by Industry',
|
context.t.client_reports.spend_report.spend_by_industry,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: UiColors.textPrimary,
|
color: UiColors.textPrimary,
|
||||||
@@ -485,12 +485,12 @@ class _SpendByIndustryCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
if (industries.isEmpty)
|
if (industries.isEmpty)
|
||||||
const Center(
|
Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'No industry data available',
|
context.t.client_reports.spend_report.no_industry_data,
|
||||||
style: TextStyle(color: UiColors.textSecondary),
|
style: const TextStyle(color: UiColors.textSecondary),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -533,7 +533,7 @@ class _SpendByIndustryCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Text(
|
Text(
|
||||||
'${ind.percentage.toStringAsFixed(1)}% of total',
|
context.t.client_reports.spend_report.percent_total(percent: ind.percentage.toStringAsFixed(1)),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
color: UiColors.textDescription,
|
color: UiColors.textDescription,
|
||||||
|
|||||||
Reference in New Issue
Block a user