fix compilations
This commit is contained in:
@@ -66,18 +66,6 @@ class _SessionListenerState extends State<SessionListener> {
|
|||||||
_sessionExpiredDialogShown = false;
|
_sessionExpiredDialogShown = false;
|
||||||
debugPrint('[SessionListener] Authenticated: ${state.userId}');
|
debugPrint('[SessionListener] Authenticated: ${state.userId}');
|
||||||
|
|
||||||
if (StaffSessionStore.instance.session == null) {
|
|
||||||
try {
|
|
||||||
final AuthRepositoryInterface authRepo =
|
|
||||||
Modular.get<AuthRepositoryInterface>();
|
|
||||||
await authRepo.restoreSession();
|
|
||||||
} catch (e) {
|
|
||||||
if (mounted) {
|
|
||||||
_proceedToLogin();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Navigate to the main app
|
// Navigate to the main app
|
||||||
Modular.to.toStaffHome();
|
Modular.to.toStaffHome();
|
||||||
|
|||||||
@@ -1214,24 +1214,6 @@
|
|||||||
"performance": "Performance Report"
|
"performance": "Performance Report"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ai_insights": {
|
|
||||||
"title": "AI Insights",
|
|
||||||
"insight_1": {
|
|
||||||
"prefix": "You could save ",
|
|
||||||
"highlight": "USD 1,200/month",
|
|
||||||
"suffix": " by booking workers 48hrs in advance"
|
|
||||||
},
|
|
||||||
"insight_2": {
|
|
||||||
"prefix": "Weekend demand is ",
|
|
||||||
"highlight": "40% higher",
|
|
||||||
"suffix": " - consider scheduling earlier"
|
|
||||||
},
|
|
||||||
"insight_3": {
|
|
||||||
"prefix": "Your top 5 workers complete ",
|
|
||||||
"highlight": "95% of shifts",
|
|
||||||
"suffix": " - mark them as preferred"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"daily_ops_report": {
|
"daily_ops_report": {
|
||||||
"title": "Daily Ops Report",
|
"title": "Daily Ops Report",
|
||||||
"subtitle": "Real-time shift tracking",
|
"subtitle": "Real-time shift tracking",
|
||||||
@@ -1297,24 +1279,6 @@
|
|||||||
"retail": "Retail"
|
"retail": "Retail"
|
||||||
},
|
},
|
||||||
"percent_total": "$percent% of total",
|
"percent_total": "$percent% of total",
|
||||||
"insights": {
|
|
||||||
"title": "Cost Insights",
|
|
||||||
"insight_1": {
|
|
||||||
"prefix": "Your spend is ",
|
|
||||||
"highlight": "8% lower",
|
|
||||||
"suffix": " than last week"
|
|
||||||
},
|
|
||||||
"insight_2": {
|
|
||||||
"prefix": "",
|
|
||||||
"highlight": "Friday",
|
|
||||||
"suffix": " had the highest spend (USD 4.1k)"
|
|
||||||
},
|
|
||||||
"insight_3": {
|
|
||||||
"prefix": "Hospitality accounts for ",
|
|
||||||
"highlight": "48%",
|
|
||||||
"suffix": " of total costs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"export_message": "Exporting Spend Report (Placeholder)"
|
"export_message": "Exporting Spend Report (Placeholder)"
|
||||||
}
|
}
|
||||||
@@ -1365,24 +1329,6 @@
|
|||||||
"worker_pool": "Worker Pool",
|
"worker_pool": "Worker Pool",
|
||||||
"avg_rating": "Avg Rating"
|
"avg_rating": "Avg Rating"
|
||||||
},
|
},
|
||||||
"insights": {
|
|
||||||
"title": "Performance Insights",
|
|
||||||
"insight_1": {
|
|
||||||
"prefix": "Your fill rate is ",
|
|
||||||
"highlight": "4% above",
|
|
||||||
"suffix": " industry benchmark"
|
|
||||||
},
|
|
||||||
"insight_2": {
|
|
||||||
"prefix": "Worker retention is at ",
|
|
||||||
"highlight": "high",
|
|
||||||
"suffix": " levels this quarter"
|
|
||||||
},
|
|
||||||
"insight_3": {
|
|
||||||
"prefix": "On-time arrival improved by ",
|
|
||||||
"highlight": "12%",
|
|
||||||
"suffix": " since last month"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"export_message": "Exporting Performance Report (Placeholder)"
|
"export_message": "Exporting Performance Report (Placeholder)"
|
||||||
}
|
}
|
||||||
@@ -1403,24 +1349,6 @@
|
|||||||
"medium": "Medium Risk",
|
"medium": "Medium Risk",
|
||||||
"low": "Low Risk"
|
"low": "Low Risk"
|
||||||
},
|
},
|
||||||
"insights": {
|
|
||||||
"title": "Reliability Insights",
|
|
||||||
"insight_1": {
|
|
||||||
"prefix": "Your no-show rate of ",
|
|
||||||
"highlight": "$rate%",
|
|
||||||
"suffix": " is $comparison industry average"
|
|
||||||
},
|
|
||||||
"insight_2": {
|
|
||||||
"prefix": "",
|
|
||||||
"highlight": "$count worker(s)",
|
|
||||||
"suffix": " have multiple incidents this month"
|
|
||||||
},
|
|
||||||
"insight_3": {
|
|
||||||
"prefix": "Consider implementing ",
|
|
||||||
"highlight": "confirmation reminders",
|
|
||||||
"suffix": " 24hrs before shifts"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"empty_state": "No workers flagged for no-shows",
|
"empty_state": "No workers flagged for no-shows",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"export_message": "Exporting No-Show Report (Placeholder)"
|
"export_message": "Exporting No-Show Report (Placeholder)"
|
||||||
@@ -1442,24 +1370,6 @@
|
|||||||
"one_spot_remaining": "1 spot remaining",
|
"one_spot_remaining": "1 spot remaining",
|
||||||
"fully_staffed": "Fully staffed"
|
"fully_staffed": "Fully staffed"
|
||||||
},
|
},
|
||||||
"insights": {
|
|
||||||
"title": "Coverage Insights",
|
|
||||||
"insight_1": {
|
|
||||||
"prefix": "Your average coverage rate is ",
|
|
||||||
"highlight": "96%",
|
|
||||||
"suffix": " - above industry standard"
|
|
||||||
},
|
|
||||||
"insight_2": {
|
|
||||||
"prefix": "",
|
|
||||||
"highlight": "2 days",
|
|
||||||
"suffix": " need immediate attention to reach full coverage"
|
|
||||||
},
|
|
||||||
"insight_3": {
|
|
||||||
"prefix": "Weekend coverage is typically ",
|
|
||||||
"highlight": "98%",
|
|
||||||
"suffix": " vs weekday 94%"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"export_message": "Exporting Coverage Report (Placeholder)"
|
"export_message": "Exporting Coverage Report (Placeholder)"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1214,24 +1214,6 @@
|
|||||||
"performance": "Informe de Rendimiento"
|
"performance": "Informe de Rendimiento"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ai_insights": {
|
|
||||||
"title": "Perspectivas de IA",
|
|
||||||
"insight_1": {
|
|
||||||
"prefix": "Podrías ahorrar ",
|
|
||||||
"highlight": "USD 1,200/mes",
|
|
||||||
"suffix": " reservando trabajadores con 48h de antelación"
|
|
||||||
},
|
|
||||||
"insight_2": {
|
|
||||||
"prefix": "La demanda del fin de semana es un ",
|
|
||||||
"highlight": "40% superior",
|
|
||||||
"suffix": " - considera programar antes"
|
|
||||||
},
|
|
||||||
"insight_3": {
|
|
||||||
"prefix": "Tus 5 mejores trabajadores completan el ",
|
|
||||||
"highlight": "95% de los turnos",
|
|
||||||
"suffix": " - márcalos como preferidos"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"daily_ops_report": {
|
"daily_ops_report": {
|
||||||
"title": "Informe de Ops Diarias",
|
"title": "Informe de Ops Diarias",
|
||||||
"subtitle": "Seguimiento de turnos en tiempo real",
|
"subtitle": "Seguimiento de turnos en tiempo real",
|
||||||
@@ -1254,6 +1236,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"all_shifts_title": "TODOS LOS TURNOS",
|
"all_shifts_title": "TODOS LOS TURNOS",
|
||||||
|
"no_shifts_today": "No hay turnos programados para hoy",
|
||||||
"shift_item": {
|
"shift_item": {
|
||||||
"time": "Hora",
|
"time": "Hora",
|
||||||
"workers": "Trabajadores",
|
"workers": "Trabajadores",
|
||||||
@@ -1266,7 +1249,7 @@
|
|||||||
"completed": "Completado"
|
"completed": "Completado"
|
||||||
},
|
},
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"export_message": "Exportando Informe de Operaciones Diarias (Marcador de posición)"
|
"export_message": "Exportando Informe de Ops Diarias (Marcador de posición)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spend_report": {
|
"spend_report": {
|
||||||
@@ -1295,65 +1278,23 @@
|
|||||||
"retail": "Venta minorista"
|
"retail": "Venta minorista"
|
||||||
},
|
},
|
||||||
"percent_total": "$percent% del total",
|
"percent_total": "$percent% del total",
|
||||||
"insights": {
|
"no_industry_data": "No hay datos de la industria disponibles",
|
||||||
"title": "Perspectivas de Costos",
|
|
||||||
"insight_1": {
|
|
||||||
"prefix": "Tu gasto es un ",
|
|
||||||
"highlight": "8% menor",
|
|
||||||
"suffix": " que la semana pasada"
|
|
||||||
},
|
|
||||||
"insight_2": {
|
|
||||||
"prefix": "El ",
|
|
||||||
"highlight": "Viernes",
|
|
||||||
"suffix": " tuvo el mayor gasto (USD 4.1k)"
|
|
||||||
},
|
|
||||||
"insight_3": {
|
|
||||||
"prefix": "La hostelería representa el ",
|
|
||||||
"highlight": "48%",
|
|
||||||
"suffix": " de los costos totales"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"export_message": "Exportando Informe de Gastos (Marcador de posición)"
|
"export_message": "Exportando Informe de Gastos (Marcador de posición)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"forecast_report": {
|
"forecast_report": {
|
||||||
"title": "Informe de Previsión",
|
"title": "Informe de Previsión",
|
||||||
"subtitle": "Proyección para las próximas 4 semanas",
|
"subtitle": "Gastos y personal proyectados",
|
||||||
"summary": {
|
"metrics": {
|
||||||
"four_week": "Previsión de 4 Semanas",
|
"projected_spend": "Gasto Proyectado",
|
||||||
"avg_weekly": "Promedio Semanal",
|
"workers_needed": "Trabajadores Necesarios"
|
||||||
"total_shifts": "Total de Turnos",
|
|
||||||
"total_hours": "Total de Horas",
|
|
||||||
"total_projected": "Total proyectado",
|
|
||||||
"per_week": "Por semana",
|
|
||||||
"scheduled": "Programado",
|
|
||||||
"worker_hours": "Horas de trabajadores"
|
|
||||||
},
|
},
|
||||||
"spending_forecast": "Previsión de Gastos",
|
"chart_title": "Previsión de Gastos",
|
||||||
"weekly_breakdown": "DESGLOSE SEMANAL",
|
"daily_projections": "PROYECCIONES DIARIAS",
|
||||||
"breakdown_headings": {
|
"empty_state": "No hay proyecciones disponibles",
|
||||||
"shifts": "Turnos",
|
"shift_item": {
|
||||||
"hours": "Horas",
|
"workers_needed": "$count trabajadores necesarios"
|
||||||
"avg_shift": "Prom/Turno"
|
|
||||||
},
|
|
||||||
"insights": {
|
|
||||||
"title": "Perspectivas de Previsión",
|
|
||||||
"insight_1": {
|
|
||||||
"prefix": "Se espera que la demanda aumente un ",
|
|
||||||
"highlight": "25%",
|
|
||||||
"suffix": " en la semana 3"
|
|
||||||
},
|
|
||||||
"insight_2": {
|
|
||||||
"prefix": "El gasto proyectado para el próximo mes es de ",
|
|
||||||
"highlight": "USD 68.4k",
|
|
||||||
"suffix": ""
|
|
||||||
},
|
|
||||||
"insight_3": {
|
|
||||||
"prefix": "Considera aumentar el presupuesto para la cobertura de ",
|
|
||||||
"highlight": "Temporada de Vacaciones",
|
|
||||||
"suffix": ""
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"export_message": "Exportando Informe de Previsión (Marcador de posición)"
|
"export_message": "Exportando Informe de Previsión (Marcador de posición)"
|
||||||
@@ -1364,7 +1305,9 @@
|
|||||||
"subtitle": "Métricas clave y comparativas",
|
"subtitle": "Métricas clave y comparativas",
|
||||||
"overall_score": {
|
"overall_score": {
|
||||||
"title": "Puntuación de Rendimiento General",
|
"title": "Puntuación de Rendimiento General",
|
||||||
"label": "Excelente"
|
"excellent": "Excelente",
|
||||||
|
"good": "Bueno",
|
||||||
|
"needs_work": "Necesita Mejorar"
|
||||||
},
|
},
|
||||||
"kpis_title": "INDICADORES CLAVE DE RENDIMIENTO (KPI)",
|
"kpis_title": "INDICADORES CLAVE DE RENDIMIENTO (KPI)",
|
||||||
"kpis": {
|
"kpis": {
|
||||||
@@ -1373,8 +1316,11 @@
|
|||||||
"on_time_rate": "Tasa de Puntualidad",
|
"on_time_rate": "Tasa de Puntualidad",
|
||||||
"avg_fill_time": "Tiempo Promedio de Llenado",
|
"avg_fill_time": "Tiempo Promedio de Llenado",
|
||||||
"target_prefix": "Objetivo: ",
|
"target_prefix": "Objetivo: ",
|
||||||
|
"target_hours": "$hours hrs",
|
||||||
|
"target_percent": "$percent%",
|
||||||
"met": "✓ Cumplido",
|
"met": "✓ Cumplido",
|
||||||
"close": "↗ Cerca"
|
"close": "→ Cerca",
|
||||||
|
"miss": "✗ Fallido"
|
||||||
},
|
},
|
||||||
"additional_metrics_title": "MÉTRICAS ADICIONALES",
|
"additional_metrics_title": "MÉTRICAS ADICIONALES",
|
||||||
"additional_metrics": {
|
"additional_metrics": {
|
||||||
@@ -1383,24 +1329,6 @@
|
|||||||
"worker_pool": "Grupo de Trabajadores",
|
"worker_pool": "Grupo de Trabajadores",
|
||||||
"avg_rating": "Calificación Promedio"
|
"avg_rating": "Calificación Promedio"
|
||||||
},
|
},
|
||||||
"insights": {
|
|
||||||
"title": "Perspectivas de Rendimiento",
|
|
||||||
"insight_1": {
|
|
||||||
"prefix": "Tu tasa de llenado es un ",
|
|
||||||
"highlight": "4% superior",
|
|
||||||
"suffix": " al promedio de la industria"
|
|
||||||
},
|
|
||||||
"insight_2": {
|
|
||||||
"prefix": "La retención de trabajadores está en niveles ",
|
|
||||||
"highlight": "altos",
|
|
||||||
"suffix": " este trimestre"
|
|
||||||
},
|
|
||||||
"insight_3": {
|
|
||||||
"prefix": "La llegada puntual mejoró un ",
|
|
||||||
"highlight": "12%",
|
|
||||||
"suffix": " desde el mes pasado"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"export_message": "Exportando Informe de Rendimiento (Marcador de posición)"
|
"export_message": "Exportando Informe de Rendimiento (Marcador de posición)"
|
||||||
}
|
}
|
||||||
@@ -1421,24 +1349,7 @@
|
|||||||
"medium": "Riesgo Medio",
|
"medium": "Riesgo Medio",
|
||||||
"low": "Riesgo Bajo"
|
"low": "Riesgo Bajo"
|
||||||
},
|
},
|
||||||
"insights": {
|
"empty_state": "No hay trabajadores señalados por faltas",
|
||||||
"title": "Perspectivas de Confiabilidad",
|
|
||||||
"insight_1": {
|
|
||||||
"prefix": "Tu tasa de faltas del ",
|
|
||||||
"highlight": "1.2%",
|
|
||||||
"suffix": " está por debajo del promedio de la industria"
|
|
||||||
},
|
|
||||||
"insight_2": {
|
|
||||||
"prefix": "",
|
|
||||||
"highlight": "1 trabajador",
|
|
||||||
"suffix": " tiene múltiples incidentes este mes"
|
|
||||||
},
|
|
||||||
"insight_3": {
|
|
||||||
"prefix": "Considera implementar ",
|
|
||||||
"highlight": "recordatorios de confirmación",
|
|
||||||
"suffix": " 24h antes de los turnos"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"export_message": "Exportando Informe de Faltas (Marcador de posición)"
|
"export_message": "Exportando Informe de Faltas (Marcador de posición)"
|
||||||
}
|
}
|
||||||
@@ -1452,29 +1363,13 @@
|
|||||||
"needs_help": "Necesita Ayuda"
|
"needs_help": "Necesita Ayuda"
|
||||||
},
|
},
|
||||||
"next_7_days": "PRÓXIMOS 7 DÍAS",
|
"next_7_days": "PRÓXIMOS 7 DÍAS",
|
||||||
|
"empty_state": "No hay turnos programados",
|
||||||
"shift_item": {
|
"shift_item": {
|
||||||
"confirmed_workers": "$confirmed/$needed trabajadores confirmados",
|
"confirmed_workers": "$confirmed/$needed trabajadores confirmados",
|
||||||
"spots_remaining": "$count puestos restantes",
|
"spots_remaining": "$count puestos restantes",
|
||||||
|
"one_spot_remaining": "1 puesto restante",
|
||||||
"fully_staffed": "Totalmente cubierto"
|
"fully_staffed": "Totalmente cubierto"
|
||||||
},
|
},
|
||||||
"insights": {
|
|
||||||
"title": "Perspectivas de Cobertura",
|
|
||||||
"insight_1": {
|
|
||||||
"prefix": "Tu tasa de cobertura promedio es del ",
|
|
||||||
"highlight": "96%",
|
|
||||||
"suffix": " - por encima del estándar de la industria"
|
|
||||||
},
|
|
||||||
"insight_2": {
|
|
||||||
"prefix": "",
|
|
||||||
"highlight": "2 días",
|
|
||||||
"suffix": " necesitan atención inmediata para alcanzar la cobertura completa"
|
|
||||||
},
|
|
||||||
"insight_3": {
|
|
||||||
"prefix": "La cobertura de fin de semana es típicamente del ",
|
|
||||||
"highlight": "98%",
|
|
||||||
"suffix": " vs 94% en días laborables"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"export_message": "Exportando Informe de Cobertura (Marcador de posición)"
|
"export_message": "Exportando Informe de Cobertura (Marcador de posición)"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
|
|||||||
.date(orderTimestamp)
|
.date(orderTimestamp)
|
||||||
.startDate(startTimestamp)
|
.startDate(startTimestamp)
|
||||||
.endDate(endTimestamp)
|
.endDate(endTimestamp)
|
||||||
.recurringDays(order.recurringDays)
|
.recurringDays(fdc.AnyValue(order.recurringDays))
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
final String orderId = orderResult.data.order_insert.id;
|
final String orderId = orderResult.data.order_insert.id;
|
||||||
@@ -299,7 +299,7 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
|
|||||||
.status(dc.OrderStatus.POSTED)
|
.status(dc.OrderStatus.POSTED)
|
||||||
.date(orderTimestamp)
|
.date(orderTimestamp)
|
||||||
.startDate(startTimestamp)
|
.startDate(startTimestamp)
|
||||||
.permanentDays(order.permanentDays)
|
.permanentDays(fdc.AnyValue(order.permanentDays))
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
final String orderId = orderResult.data.order_insert.id;
|
final String orderId = orderResult.data.order_insert.id;
|
||||||
|
|||||||
@@ -332,8 +332,8 @@ class _DailyOpsReportPageState extends State<DailyOpsReportPage> {
|
|||||||
|
|
||||||
// Shift List
|
// Shift List
|
||||||
if (report.shifts.isEmpty)
|
if (report.shifts.isEmpty)
|
||||||
const Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 40),
|
padding: const EdgeInsets.symmetric(vertical: 40),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(context.t.client_reports.daily_ops_report.no_shifts_today),
|
child: Text(context.t.client_reports.daily_ops_report.no_shifts_today),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -216,53 +216,7 @@ class _NoShowReportPageState extends State<NoShowReportPage> {
|
|||||||
(worker) => _WorkerCard(worker: worker),
|
(worker) => _WorkerCard(worker: worker),
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 40),
|
||||||
|
|
||||||
// ── Reliability Insights box (matches prototype) ──
|
|
||||||
Container(
|
|
||||||
width: double.infinity,
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFFFF8E1),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
border: Border.all(
|
|
||||||
color: UiColors.textWarning.withOpacity(0.3),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'💡 ${context.t.client_reports.no_show_report.insights.title}',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
_InsightLine(
|
|
||||||
text:
|
|
||||||
'· ${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')}',
|
|
||||||
),
|
|
||||||
if (report.flaggedWorkers.any(
|
|
||||||
(w) => w.noShowCount > 1,
|
|
||||||
))
|
|
||||||
_InsightLine(
|
|
||||||
text:
|
|
||||||
'· ${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}',
|
|
||||||
bold: true,
|
|
||||||
),
|
|
||||||
_InsightLine(
|
|
||||||
text:
|
|
||||||
'· ${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,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 100),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -349,7 +303,7 @@ class _WorkerCard extends StatelessWidget {
|
|||||||
|
|
||||||
const _WorkerCard({required this.worker});
|
const _WorkerCard({required this.worker});
|
||||||
|
|
||||||
String _riskLabel(int count) {
|
String _riskLabel(BuildContext context, int count) {
|
||||||
if (count >= 3) return context.t.client_reports.no_show_report.risks.high;
|
if (count >= 3) return context.t.client_reports.no_show_report.risks.high;
|
||||||
if (count == 2) return context.t.client_reports.no_show_report.risks.medium;
|
if (count == 2) return context.t.client_reports.no_show_report.risks.medium;
|
||||||
return context.t.client_reports.no_show_report.risks.low;
|
return context.t.client_reports.no_show_report.risks.low;
|
||||||
@@ -369,7 +323,7 @@ class _WorkerCard extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final riskLabel = _riskLabel(worker.noShowCount);
|
final riskLabel = _riskLabel(context, worker.noShowCount);
|
||||||
final riskColor = _riskColor(worker.noShowCount);
|
final riskColor = _riskColor(worker.noShowCount);
|
||||||
final riskBg = _riskBg(worker.noShowCount);
|
final riskBg = _riskBg(worker.noShowCount);
|
||||||
|
|
||||||
@@ -487,25 +441,3 @@ class _WorkerCard extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── Insight line ─────────────────────────────────────────────────────────────
|
// ── Insight line ─────────────────────────────────────────────────────────────
|
||||||
class _InsightLine extends StatelessWidget {
|
|
||||||
final String text;
|
|
||||||
final bool bold;
|
|
||||||
|
|
||||||
const _InsightLine({required this.text, this.bold = false});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 6),
|
|
||||||
child: Text(
|
|
||||||
text,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
color: UiColors.textPrimary,
|
|
||||||
fontWeight: bold ? FontWeight.w600 : FontWeight.normal,
|
|
||||||
height: 1.4,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -316,6 +316,7 @@ class _ReportsPageState extends State<ReportsPage>
|
|||||||
color: UiColors.textPrimary,
|
color: UiColors.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
/*
|
||||||
TextButton.icon(
|
TextButton.icon(
|
||||||
onPressed: () {},
|
onPressed: () {},
|
||||||
icon: const Icon(UiIcons.download, size: 16),
|
icon: const Icon(UiIcons.download, size: 16),
|
||||||
@@ -328,6 +329,7 @@ class _ReportsPageState extends State<ReportsPage>
|
|||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
*/
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -392,89 +394,7 @@ class _ReportsPageState extends State<ReportsPage>
|
|||||||
|
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
// AI Insights
|
const SizedBox(height: 40),
|
||||||
Container(
|
|
||||||
width: double.infinity,
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: UiColors.tagInProgress.withOpacity(0.3),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: UiColors.black.withOpacity(0.02),
|
|
||||||
blurRadius: 2,
|
|
||||||
offset: const Offset(0, 1),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'💡 ${context.t.client_reports.ai_insights.title}',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
_InsightRow(
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: context.t.client_reports.ai_insights
|
|
||||||
.insight_1.prefix),
|
|
||||||
TextSpan(
|
|
||||||
text: context.t.client_reports.ai_insights
|
|
||||||
.insight_1.highlight,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: context.t.client_reports.ai_insights
|
|
||||||
.insight_1.suffix,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
_InsightRow(
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: context.t.client_reports.ai_insights
|
|
||||||
.insight_2.prefix),
|
|
||||||
TextSpan(
|
|
||||||
text: context.t.client_reports.ai_insights
|
|
||||||
.insight_2.highlight,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: context.t.client_reports.ai_insights
|
|
||||||
.insight_2.suffix,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
_InsightRow(
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: context.t.client_reports.ai_insights
|
|
||||||
.insight_3.prefix,
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: context.t.client_reports.ai_insights
|
|
||||||
.insight_3.highlight,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: context.t.client_reports.ai_insights
|
|
||||||
.insight_3.suffix),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 100),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -661,36 +581,3 @@ class _ReportCard extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InsightRow extends StatelessWidget {
|
|
||||||
final List<InlineSpan> children;
|
|
||||||
|
|
||||||
const _InsightRow({required this.children});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 8.0),
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'• ',
|
|
||||||
style: TextStyle(color: UiColors.textSecondary, fontSize: 14),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text.rich(
|
|
||||||
TextSpan(
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: UiColors.textSecondary,
|
|
||||||
height: 1.4,
|
|
||||||
),
|
|
||||||
children: children,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -215,57 +215,12 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
|||||||
staffRecord = staffResponse.data.staffs.first;
|
staffRecord = staffResponse.data.staffs.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _setSession(firebaseUser.uid, user, staffRecord);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> restoreSession() async {
|
|
||||||
final User? firebaseUser = await _service.auth.authStateChanges().first;
|
|
||||||
if (firebaseUser == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reuse the same logic as verifyOtp to fetch user/staff and set session
|
|
||||||
// We can't reuse verifyOtp directly because it requires verificationId/smsCode
|
|
||||||
// So we fetch the data manually here.
|
|
||||||
|
|
||||||
final QueryResult<GetUserByIdData, GetUserByIdVariables> response =
|
|
||||||
await _service.run(
|
|
||||||
() => _service.connector.getUserById(id: firebaseUser.uid).execute(),
|
|
||||||
requiresAuthentication: false,
|
|
||||||
);
|
|
||||||
final GetUserByIdUser? user = response.data.user;
|
|
||||||
|
|
||||||
if (user == null) {
|
|
||||||
// User authenticated in Firebase but not in our DB?
|
|
||||||
// Should likely sign out or handle gracefully.
|
|
||||||
await _service.auth.signOut();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final QueryResult<GetStaffByUserIdData, GetStaffByUserIdVariables>
|
|
||||||
staffResponse = await _service.run(
|
|
||||||
() => _service.connector.getStaffByUserId(userId: firebaseUser.uid).execute(),
|
|
||||||
requiresAuthentication: false,
|
|
||||||
);
|
|
||||||
|
|
||||||
final GetStaffByUserIdStaffs? staffRecord =
|
|
||||||
staffResponse.data.staffs.firstOrNull;
|
|
||||||
|
|
||||||
_setSession(firebaseUser.uid, user, staffRecord);
|
|
||||||
}
|
|
||||||
|
|
||||||
domain.User _setSession(
|
|
||||||
String uid,
|
|
||||||
GetUserByIdUser? user,
|
|
||||||
GetStaffByUserIdStaffs? staffRecord,
|
|
||||||
) {
|
|
||||||
//TO-DO: create(registration) user and staff account
|
//TO-DO: create(registration) user and staff account
|
||||||
//TO-DO: save user data locally
|
//TO-DO: save user data locally
|
||||||
final domain.User domainUser = domain.User(
|
final domain.User domainUser = domain.User(
|
||||||
id: uid,
|
id: firebaseUser.uid,
|
||||||
email: user?.email ?? '',
|
email: user?.email ?? '',
|
||||||
phone: user?.phone, // Use user.phone locally if needed, but domain.User expects it
|
phone: user?.phone,
|
||||||
role: user?.role.stringValue ?? 'USER',
|
role: user?.role.stringValue ?? 'USER',
|
||||||
);
|
);
|
||||||
final domain.Staff? domainStaff = staffRecord == null
|
final domain.Staff? domainStaff = staffRecord == null
|
||||||
@@ -288,4 +243,5 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
|||||||
);
|
);
|
||||||
return domainUser;
|
return domainUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,4 @@ abstract interface class AuthRepositoryInterface {
|
|||||||
/// Signs out the current user.
|
/// Signs out the current user.
|
||||||
Future<void> signOut();
|
Future<void> signOut();
|
||||||
|
|
||||||
/// Restores the user session if a user is already signed in.
|
|
||||||
Future<void> restoreSession();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class StaffAuthenticationModule extends Module {
|
|||||||
// Repositories
|
// Repositories
|
||||||
i.addLazySingleton<ProfileSetupRepository>(ProfileSetupRepositoryImpl.new);
|
i.addLazySingleton<ProfileSetupRepository>(ProfileSetupRepositoryImpl.new);
|
||||||
i.addLazySingleton<PlaceRepository>(PlaceRepositoryImpl.new);
|
i.addLazySingleton<PlaceRepository>(PlaceRepositoryImpl.new);
|
||||||
|
i.addLazySingleton<AuthRepositoryInterface>(AuthRepositoryImpl.new);
|
||||||
|
|
||||||
// UseCases
|
// UseCases
|
||||||
i.addLazySingleton(SignInWithPhoneUseCase.new);
|
i.addLazySingleton(SignInWithPhoneUseCase.new);
|
||||||
@@ -52,10 +53,6 @@ class StaffAuthenticationModule extends Module {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void exportedBinds(Injector i) {
|
|
||||||
i.addLazySingleton<AuthRepositoryInterface>(AuthRepositoryImpl.new);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void routes(RouteManager r) {
|
void routes(RouteManager r) {
|
||||||
|
|||||||
Reference in New Issue
Block a user