chore: add localization to benefits overview page (en & es)

This commit is contained in:
2026-02-24 16:25:24 +05:30
parent 7e26b54c50
commit a7b34e40c8
3 changed files with 188 additions and 154 deletions

View File

@@ -536,6 +536,21 @@
"sick_days": "Sick Days", "sick_days": "Sick Days",
"vacation": "Vacation", "vacation": "Vacation",
"holidays": "Holidays" "holidays": "Holidays"
},
"overview": {
"title": "Your Benefits Overview",
"subtitle": "Manage and track your earned benefits here",
"request_payment": "Request Payment for $benefit",
"request_submitted": "Request submitted for $benefit",
"sick_leave_subtitle": "You need at least 8 hours to request sick leave",
"vacation_subtitle": "You need 40 hours to claim vacation pay",
"holidays_subtitle": "Pay holidays: Thanksgiving, Christmas, New Year",
"sick_leave_history": "SICK LEAVE HISTORY",
"compliance_banner": "Listed certificates are mandatory for employees. If the employee does not have the complete certificates, they can't proceed with their registration.",
"status": {
"pending": "Pending",
"submitted": "Submitted"
}
} }
}, },
"auto_match": { "auto_match": {

View File

@@ -536,6 +536,21 @@
"sick_days": "D\u00edas de Enfermedad", "sick_days": "D\u00edas de Enfermedad",
"vacation": "Vacaciones", "vacation": "Vacaciones",
"holidays": "Festivos" "holidays": "Festivos"
},
"overview": {
"title": "Resumen de tus Beneficios",
"subtitle": "Gestiona y sigue tus beneficios ganados aqu\u00ed",
"request_payment": "Solicitar pago por $benefit",
"request_submitted": "Solicitud enviada para $benefit",
"sick_leave_subtitle": "Necesitas al menos 8 horas para solicitar d\u00edas de enfermedad",
"vacation_subtitle": "Necesitas 40 horas para reclamar el pago de vacaciones",
"holidays_subtitle": "D\u00edas festivos pagados: Acci\u00f3n de Gracias, Navidad, A\u00f1o Nuevo",
"sick_leave_history": "HISTORIAL DE D\u00cdAS DE ENFERMEDAD",
"compliance_banner": "Los certificados listados son obligatorios para los empleados. Si el empleado no tiene los certificados completos, no puede proceder con su registro.",
"status": {
"pending": "Pendiente",
"submitted": "Enviado"
}
} }
}, },
"auto_match": { "auto_match": {

View File

@@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart'; import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart'; import 'package:krow_domain/krow_domain.dart';
import 'package:core_localization/core_localization.dart';
import 'package:staff_home/src/presentation/blocs/home_cubit.dart'; import 'package:staff_home/src/presentation/blocs/home_cubit.dart';
import 'dart:math' as math; import 'dart:math' as math;
@@ -58,186 +59,189 @@ class BenefitsOverviewPage extends StatelessWidget {
centerTitle: true, centerTitle: true,
title: Column( title: Column(
children: [ children: [
Text( Text(
'Your Benefits Overview', t.staff.home.benefits.overview.title,
style: UiTypography.title2b.textPrimary, style: UiTypography.title2b.textPrimary,
),
const SizedBox(height: 2),
Text(
t.staff.home.benefits.overview.subtitle,
style: UiTypography.footnote2r.textSecondary,
),
],
), ),
const SizedBox(height: 2), bottom: PreferredSize(
Text( preferredSize: const Size.fromHeight(1),
'Manage and track your earned benefits here', child: Container(color: UiColors.border.withOpacity(0.5), height: 1),
style: UiTypography.footnote2r.textSecondary,
), ),
], );
), }
bottom: PreferredSize( }
preferredSize: const Size.fromHeight(1),
child: Container(color: UiColors.border.withOpacity(0.5), height: 1),
),
);
}
}
class _BenefitCard extends StatelessWidget { class _BenefitCard extends StatelessWidget {
final Benefit benefit; final Benefit benefit;
const _BenefitCard({required this.benefit}); const _BenefitCard({required this.benefit});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final bool isSickLeave = benefit.title.toLowerCase().contains('sick'); final bool isSickLeave = benefit.title.toLowerCase().contains('sick');
final bool isVacation = benefit.title.toLowerCase().contains('vacation'); final bool isVacation = benefit.title.toLowerCase().contains('vacation');
final bool isHolidays = benefit.title.toLowerCase().contains('holiday'); final bool isHolidays = benefit.title.toLowerCase().contains('holiday');
final i18n = t.staff.home.benefits.overview;
return Container( return Container(
padding: const EdgeInsets.all(UiConstants.space6), padding: const EdgeInsets.all(UiConstants.space6),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: UiConstants.radiusLg, borderRadius: UiConstants.radiusLg,
border: Border.all(color: UiColors.border.withOpacity(0.5)), border: Border.all(color: UiColors.border.withOpacity(0.5)),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: UiColors.black.withOpacity(0.02), color: UiColors.black.withOpacity(0.02),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 4), offset: const Offset(0, 4),
),
],
), ),
], child: Column(
), crossAxisAlignment: CrossAxisAlignment.start,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [ children: [
_buildProgressCircle(), Row(
const SizedBox(width: UiConstants.space4), children: [
Expanded( _buildProgressCircle(),
child: Column( const SizedBox(width: UiConstants.space4),
crossAxisAlignment: CrossAxisAlignment.start, Expanded(
children: [ child: Column(
Row( crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Row(
benefit.title, mainAxisAlignment: MainAxisAlignment.spaceBetween,
style: UiTypography.body1b.textPrimary, children: [
Text(
benefit.title,
style: UiTypography.body1b.textPrimary,
),
const Icon(UiIcons.info, size: 18, color: Color(0xFFE2E8F0)),
],
),
const SizedBox(height: 4),
Text(
_getSubtitle(benefit.title),
style: UiTypography.footnote2r.textSecondary,
), ),
const Icon(UiIcons.info, size: 18, color: Color(0xFFE2E8F0)),
], ],
), ),
const SizedBox(height: 4), ),
Text( ],
_getSubtitle(benefit.title), ),
style: UiTypography.footnote2r.textSecondary, const SizedBox(height: UiConstants.space6),
if (isSickLeave) ...[
_AccordionHistory(label: i18n.sick_leave_history),
const SizedBox(height: UiConstants.space6),
],
if (isVacation || isHolidays) ...[
_buildComplianceBanner(i18n.compliance_banner),
const SizedBox(height: UiConstants.space6),
],
SizedBox(
width: double.infinity,
child: UiButton.primary(
text: i18n.request_payment(benefit: benefit.title),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0038A8),
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
), ),
], ),
onPressed: () {
// TODO: Implement payment request
UiSnackbar.show(context, message: i18n.request_submitted(benefit: benefit.title), type: UiSnackbarType.success);
},
), ),
), ),
], ],
), ),
const SizedBox(height: UiConstants.space6), );
if (isSickLeave) ...[ }
const _AccordionHistory(label: 'SICK LEAVE HISTORY'),
const SizedBox(height: UiConstants.space6), Widget _buildProgressCircle() {
], final double progress = benefit.entitlementHours > 0
if (isVacation || isHolidays) ...[ ? (benefit.remainingHours / benefit.entitlementHours)
_buildComplianceBanner(), : 0.0;
const SizedBox(height: UiConstants.space6),
], final bool isSickLeave = benefit.title.toLowerCase().contains('sick');
SizedBox( final Color circleColor = isSickLeave ? const Color(0xFF2563EB) : const Color(0xFF10B981);
width: double.infinity,
child: UiButton.primary( return SizedBox(
text: 'Request Payment for ${benefit.title}', width: 72,
style: ElevatedButton.styleFrom( height: 72,
backgroundColor: const Color(0xFF0038A8), child: CustomPaint(
padding: const EdgeInsets.symmetric(vertical: 16), painter: _CircularProgressPainter(
shape: RoundedRectangleBorder( progress: progress,
borderRadius: BorderRadius.circular(8), color: circleColor,
), backgroundColor: const Color(0xFFE2E8F0),
strokeWidth: 6,
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'${benefit.remainingHours.toInt()}/${benefit.entitlementHours.toInt()}',
style: UiTypography.body2b.textPrimary.copyWith(fontSize: 14),
),
Text(
t.client_billing.hours_suffix,
style: UiTypography.footnote2r.textTertiary.copyWith(fontSize: 9),
),
],
), ),
onPressed: () {
// TODO: Implement payment request
UiSnackbar.show(context, message: 'Request submitted for ${benefit.title}', type: UiSnackbarType.success);
},
), ),
), ),
], );
), }
);
}
Widget _buildProgressCircle() { String _getSubtitle(String title) {
final double progress = benefit.entitlementHours > 0 final i18n = t.staff.home.benefits.overview;
? (benefit.remainingHours / benefit.entitlementHours) if (title.toLowerCase().contains('sick')) {
: 0.0; return i18n.sick_leave_subtitle;
} else if (title.toLowerCase().contains('vacation')) {
final bool isSickLeave = benefit.title.toLowerCase().contains('sick'); return i18n.vacation_subtitle;
final Color circleColor = isSickLeave ? const Color(0xFF2563EB) : const Color(0xFF10B981); } else if (title.toLowerCase().contains('holiday')) {
return i18n.holidays_subtitle;
return SizedBox( }
width: 72, return '';
height: 72, }
child: CustomPaint(
painter: _CircularProgressPainter( Widget _buildComplianceBanner(String text) {
progress: progress, return Container(
color: circleColor, padding: const EdgeInsets.all(12),
backgroundColor: const Color(0xFFE2E8F0), decoration: BoxDecoration(
strokeWidth: 6, color: const Color(0xFFECFDF5),
), borderRadius: BorderRadius.circular(8),
child: Center( ),
child: Column( child: Row(
mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( const Icon(UiIcons.checkCircle, size: 16, color: Color(0xFF10B981)),
'${benefit.remainingHours.toInt()}/${benefit.entitlementHours.toInt()}', const SizedBox(width: 8),
style: UiTypography.body2b.textPrimary.copyWith(fontSize: 14), Expanded(
), child: Text(
Text( text,
'hours', style: UiTypography.footnote1r.copyWith(
style: UiTypography.footnote2r.textTertiary.copyWith(fontSize: 9), color: const Color(0xFF065F46),
fontSize: 11,
),
),
), ),
], ],
), ),
), );
), }
);
}
String _getSubtitle(String title) {
if (title.toLowerCase().contains('sick')) {
return 'You need at least 8 hours to request sick leave';
} else if (title.toLowerCase().contains('vacation')) {
return 'You need 40 hours to claim vacation pay';
} else if (title.toLowerCase().contains('holiday')) {
return 'Pay holidays: Thanksgiving, Christmas, New Year';
} }
return '';
}
Widget _buildComplianceBanner() {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFECFDF5),
borderRadius: BorderRadius.circular(8),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(UiIcons.checkCircle, size: 16, color: Color(0xFF10B981)),
const SizedBox(width: 8),
Expanded(
child: Text(
'Listed certificates are mandatory for employees. If the employee does not have the complete certificates, they can\'t proceed with their registration.',
style: UiTypography.footnote1r.copyWith(
color: const Color(0xFF065F46),
fontSize: 11,
),
),
),
],
),
);
}
}
class _CircularProgressPainter extends CustomPainter { class _CircularProgressPainter extends CustomPainter {
final double progress; final double progress;