Merge branch 'dev' into feature/centralized-data-error-handling and resolve conflicts

This commit is contained in:
2026-02-11 12:34:29 +05:30
158 changed files with 10945 additions and 5478 deletions

View File

@@ -32,9 +32,7 @@ class AttirePage extends StatelessWidget {
),
title: Text(
t.staff_profile_attire.title,
style: UiTypography.headline3m.copyWith(
color: UiColors.textPrimary,
),
style: UiTypography.headline3m.textPrimary,
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0),

View File

@@ -11,8 +11,8 @@ class AttireInfoCard extends StatelessWidget {
return Container(
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
color: UiColors.primary.withOpacity(0.08),
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
color: UiColors.primary.withValues(alpha: 0.08),
borderRadius: UiConstants.radiusLg,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -26,16 +26,12 @@ class AttireInfoCard extends StatelessWidget {
children: <Widget>[
Text(
t.staff_profile_attire.info_card.title,
style: UiTypography.body2m.copyWith(
color: UiColors.textPrimary,
),
style: UiTypography.body2m.textPrimary,
),
const SizedBox(height: 2),
Text(
t.staff_profile_attire.info_card.description,
style: UiTypography.body2r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.body2r.textSecondary,
),
],
),

View File

@@ -24,12 +24,12 @@ class EmergencyContactScreen extends StatelessWidget {
appBar: AppBar(
elevation: 0,
leading: IconButton(
icon: Icon(UiIcons.chevronLeft, color: UiColors.textSecondary),
icon: const Icon(UiIcons.chevronLeft, color: UiColors.textSecondary),
onPressed: () => Modular.to.pop(),
),
title: Text(
'Emergency Contact',
style: UiTypography.title1m.copyWith(color: UiColors.textPrimary),
style: UiTypography.title1m.textPrimary,
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0),
@@ -62,11 +62,11 @@ class EmergencyContactScreen extends StatelessWidget {
children: [
Expanded(
child: SingleChildScrollView(
padding: EdgeInsets.all(UiConstants.space6),
padding: const EdgeInsets.all(UiConstants.space6),
child: Column(
children: [
const EmergencyContactInfoBanner(),
SizedBox(height: UiConstants.space6),
const SizedBox(height: UiConstants.space6),
...state.contacts.asMap().entries.map(
(entry) => EmergencyContactFormItem(
index: entry.key,
@@ -75,7 +75,7 @@ class EmergencyContactScreen extends StatelessWidget {
),
),
const EmergencyContactAddButton(),
SizedBox(height: UiConstants.space16),
const SizedBox(height: UiConstants.space16),
],
),
),

View File

@@ -12,20 +12,20 @@ class EmergencyContactAddButton extends StatelessWidget {
child: TextButton.icon(
onPressed: () =>
context.read<EmergencyContactBloc>().add(EmergencyContactAdded()),
icon: Icon(UiIcons.add, size: 20.0),
icon: const Icon(UiIcons.add, size: 20.0),
label: Text(
'Add Another Contact',
style: UiTypography.title2b,
),
style: TextButton.styleFrom(
foregroundColor: UiColors.primary,
padding: EdgeInsets.symmetric(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space6,
vertical: UiConstants.space3,
),
shape: RoundedRectangleBorder(
borderRadius: UiConstants.radiusFull,
side: BorderSide(color: UiColors.primary),
side: const BorderSide(color: UiColors.primary),
),
),
),

View File

@@ -19,8 +19,8 @@ class EmergencyContactFormItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(bottom: UiConstants.space4),
padding: EdgeInsets.all(UiConstants.space4),
margin: const EdgeInsets.only(bottom: UiConstants.space4),
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
color: UiColors.bgPopup,
borderRadius: UiConstants.radiusLg,
@@ -30,33 +30,27 @@ class EmergencyContactFormItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeader(context),
SizedBox(height: UiConstants.space4),
const SizedBox(height: UiConstants.space4),
_buildLabel('Full Name'),
_buildTextField(
initialValue: contact.name,
hint: 'Contact name',
icon: UiIcons.user,
onChanged: (val) => context.read<EmergencyContactBloc>().add(
EmergencyContactUpdated(
index,
contact.copyWith(name: val),
),
),
EmergencyContactUpdated(index, contact.copyWith(name: val)),
),
),
SizedBox(height: UiConstants.space4),
const SizedBox(height: UiConstants.space4),
_buildLabel('Phone Number'),
_buildTextField(
initialValue: contact.phone,
hint: '+1 (555) 000-0000',
icon: UiIcons.phone,
onChanged: (val) => context.read<EmergencyContactBloc>().add(
EmergencyContactUpdated(
index,
contact.copyWith(phone: val),
),
),
EmergencyContactUpdated(index, contact.copyWith(phone: val)),
),
),
SizedBox(height: UiConstants.space4),
const SizedBox(height: UiConstants.space4),
_buildLabel('Relationship'),
_buildDropdown(
context,
@@ -65,11 +59,11 @@ class EmergencyContactFormItem extends StatelessWidget {
onChanged: (val) {
if (val != null) {
context.read<EmergencyContactBloc>().add(
EmergencyContactUpdated(
index,
contact.copyWith(relationship: val),
),
);
EmergencyContactUpdated(
index,
contact.copyWith(relationship: val),
),
);
}
},
),
@@ -85,7 +79,7 @@ class EmergencyContactFormItem extends StatelessWidget {
required ValueChanged<RelationshipType?> onChanged,
}) {
return Container(
padding: EdgeInsets.symmetric(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space4,
vertical: UiConstants.space2,
),
@@ -99,13 +93,13 @@ class EmergencyContactFormItem extends StatelessWidget {
value: value,
isExpanded: true,
dropdownColor: UiColors.bgPopup,
icon: Icon(UiIcons.chevronDown, color: UiColors.iconSecondary),
icon: const Icon(UiIcons.chevronDown, color: UiColors.iconSecondary),
items: items.map((type) {
return DropdownMenuItem<RelationshipType>(
value: type,
child: Text(
_formatRelationship(type),
style: UiTypography.body1r.copyWith(color: UiColors.textPrimary),
style: UiTypography.body1r.textPrimary,
),
);
}).toList(),
@@ -116,11 +110,15 @@ class EmergencyContactFormItem extends StatelessWidget {
}
String _formatRelationship(RelationshipType type) {
switch(type) {
case RelationshipType.family: return 'Family';
case RelationshipType.spouse: return 'Spouse';
case RelationshipType.friend: return 'Friend';
case RelationshipType.other: return 'Other';
switch (type) {
case RelationshipType.family:
return 'Family';
case RelationshipType.spouse:
return 'Spouse';
case RelationshipType.friend:
return 'Friend';
case RelationshipType.other:
return 'Other';
}
}
@@ -128,22 +126,17 @@ class EmergencyContactFormItem extends StatelessWidget {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Contact ${index + 1}',
style: UiTypography.title2m.copyWith(
color: UiColors.textPrimary,
),
),
Text('Contact ${index + 1}', style: UiTypography.title2m.textPrimary),
if (totalContacts > 1)
IconButton(
icon: Icon(
icon: const Icon(
UiIcons.delete,
color: UiColors.textError,
size: 20.0,
),
onPressed: () => context
.read<EmergencyContactBloc>()
.add(EmergencyContactRemoved(index)),
onPressed: () => context.read<EmergencyContactBloc>().add(
EmergencyContactRemoved(index),
),
),
],
);
@@ -151,13 +144,8 @@ class EmergencyContactFormItem extends StatelessWidget {
Widget _buildLabel(String label) {
return Padding(
padding: EdgeInsets.only(bottom: UiConstants.space2),
child: Text(
label,
style: UiTypography.body2m.copyWith(
color: UiColors.textSecondary,
),
),
padding: const EdgeInsets.only(bottom: UiConstants.space2),
child: Text(label, style: UiTypography.body2m.textSecondary),
);
}
@@ -169,16 +157,16 @@ class EmergencyContactFormItem extends StatelessWidget {
}) {
return TextFormField(
initialValue: initialValue,
style: UiTypography.body1r.copyWith(
color: UiColors.textPrimary,
),
style: UiTypography.body1r.textPrimary,
decoration: InputDecoration(
hintText: hint,
hintStyle: TextStyle(color: UiColors.textPlaceholder),
hintStyle: const TextStyle(color: UiColors.textPlaceholder),
prefixIcon: Icon(icon, color: UiColors.textSecondary, size: 20.0),
filled: true,
fillColor: UiColors.bgPopup,
contentPadding: EdgeInsets.symmetric(vertical: UiConstants.space4),
contentPadding: const EdgeInsets.symmetric(
vertical: UiConstants.space4,
),
border: OutlineInputBorder(
borderRadius: UiConstants.radiusLg,
borderSide: BorderSide(color: UiColors.border),
@@ -196,4 +184,3 @@ class EmergencyContactFormItem extends StatelessWidget {
);
}
}

View File

@@ -7,14 +7,14 @@ class EmergencyContactInfoBanner extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(UiConstants.space4),
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
color: UiColors.accent.withOpacity(0.2),
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
color: UiColors.accent.withValues(alpha: 0.2),
borderRadius: UiConstants.radiusLg,
),
child: Text(
'Please provide at least one emergency contact. This information will only be used in case of an emergency during your shifts.',
style: UiTypography.body2r.copyWith(color: UiColors.textPrimary),
style: UiTypography.body2r.textPrimary,
),
);
}

View File

@@ -30,19 +30,18 @@ class EmergencyContactSaveButton extends StatelessWidget {
builder: (context, state) {
final isLoading = state.status == EmergencyContactStatus.saving;
return Container(
padding: EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
padding: const EdgeInsets.all(UiConstants.space4),
decoration: const BoxDecoration(
color: UiColors.bgPopup,
border: Border(top: BorderSide(color: UiColors.border)),
),
child: SafeArea(
child: UiButton.primary(
fullWidth: true,
onPressed: state.isValid && !isLoading
? () => _onSave(context)
: null,
onPressed:
state.isValid && !isLoading ? () => _onSave(context) : null,
child: isLoading
? SizedBox(
? const SizedBox(
height: 20.0,
width: 20.0,
child: CircularProgressIndicator(

View File

@@ -80,16 +80,16 @@ class ExperiencePage extends StatelessWidget {
children: [
Expanded(
child: SingleChildScrollView(
padding: EdgeInsets.all(UiConstants.space5),
padding: const EdgeInsets.all(UiConstants.space5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ExperienceSectionTitle(title: i18n.industries_title),
Text(
i18n.industries_subtitle,
style: UiTypography.body2m.copyWith(color: UiColors.textSecondary),
style: UiTypography.body2m.textSecondary,
),
SizedBox(height: UiConstants.space3),
const SizedBox(height: UiConstants.space3),
Wrap(
spacing: UiConstants.space2,
runSpacing: UiConstants.space2,
@@ -97,23 +97,26 @@ class ExperiencePage extends StatelessWidget {
.map(
(i) => UiChip(
label: _getIndustryLabel(i18n.industries, i),
isSelected: state.selectedIndustries.contains(i),
onTap: () => BlocProvider.of<ExperienceBloc>(context)
.add(ExperienceIndustryToggled(i)),
variant: state.selectedIndustries.contains(i)
? UiChipVariant.primary
: UiChipVariant.secondary,
isSelected:
state.selectedIndustries.contains(i),
onTap: () =>
BlocProvider.of<ExperienceBloc>(context)
.add(ExperienceIndustryToggled(i)),
variant:
state.selectedIndustries.contains(i)
? UiChipVariant.primary
: UiChipVariant.secondary,
),
)
.toList(),
),
SizedBox(height: UiConstants.space6),
const SizedBox(height: UiConstants.space6),
ExperienceSectionTitle(title: i18n.skills_title),
Text(
i18n.skills_subtitle,
style: UiTypography.body2m.copyWith(color: UiColors.textSecondary),
style: UiTypography.body2m.textSecondary,
),
SizedBox(height: UiConstants.space3),
const SizedBox(height: UiConstants.space3),
Wrap(
spacing: UiConstants.space2,
runSpacing: UiConstants.space2,
@@ -121,19 +124,22 @@ class ExperiencePage extends StatelessWidget {
.map(
(s) => UiChip(
label: _getSkillLabel(i18n.skills, s),
isSelected: state.selectedSkills.contains(s.value),
onTap: () => BlocProvider.of<ExperienceBloc>(context)
.add(ExperienceSkillToggled(s.value)),
variant: state.selectedSkills.contains(s.value)
? UiChipVariant.primary
: UiChipVariant.secondary,
isSelected:
state.selectedSkills.contains(s.value),
onTap: () =>
BlocProvider.of<ExperienceBloc>(context)
.add(ExperienceSkillToggled(s.value)),
variant:
state.selectedSkills.contains(s.value)
? UiChipVariant.primary
: UiChipVariant.secondary,
),
)
.toList(),
),
],
),
),
),
),
_buildSaveButton(context, state, i18n),
],
@@ -155,9 +161,9 @@ class ExperiencePage extends StatelessWidget {
children: [
Text(
i18n.custom_skills_title,
style: UiTypography.body2m.copyWith(color: UiColors.textSecondary),
style: UiTypography.body2m.textSecondary,
),
SizedBox(height: UiConstants.space2),
const SizedBox(height: UiConstants.space2),
Wrap(
spacing: UiConstants.space2,
runSpacing: UiConstants.space2,
@@ -172,10 +178,14 @@ class ExperiencePage extends StatelessWidget {
);
}
Widget _buildSaveButton(BuildContext context, ExperienceState state, dynamic i18n) {
Widget _buildSaveButton(
BuildContext context,
ExperienceState state,
dynamic i18n,
) {
return Container(
padding: EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
padding: const EdgeInsets.all(UiConstants.space4),
decoration: const BoxDecoration(
color: UiColors.bgPopup,
border: Border(top: BorderSide(color: UiColors.border)),
),
@@ -183,16 +193,21 @@ class ExperiencePage extends StatelessWidget {
child: UiButton.primary(
onPressed: state.status == ExperienceStatus.loading
? null
: () => BlocProvider.of<ExperienceBloc>(context).add(ExperienceSubmitted()),
: () => BlocProvider.of<ExperienceBloc>(context)
.add(ExperienceSubmitted()),
fullWidth: true,
text: state.status == ExperienceStatus.loading ? null : i18n.save_button,
text: state.status == ExperienceStatus.loading
? null
: i18n.save_button,
child: state.status == ExperienceStatus.loading
? SizedBox(
? const SizedBox(
height: 20.0,
width: 20.0,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(UiColors.white), // UiColors.primaryForeground is white mostly
valueColor: AlwaysStoppedAnimation<Color>(
UiColors.white,
), // UiColors.primaryForeground is white mostly
),
)
: null,

View File

@@ -1,4 +1,3 @@
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
@@ -56,13 +55,16 @@ class PersonalInfoPage extends StatelessWidget {
backgroundColor: UiColors.bgPopup,
elevation: 0,
leading: IconButton(
icon: const Icon(UiIcons.chevronLeft, color: UiColors.textSecondary),
icon: const Icon(
UiIcons.chevronLeft,
color: UiColors.textSecondary,
),
onPressed: () => Modular.to.pop(),
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
),
title: Text(
i18n.title,
style: UiTypography.title1m.copyWith(color: UiColors.textPrimary),
style: UiTypography.title1m.textPrimary,
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0),
@@ -86,9 +88,7 @@ class PersonalInfoPage extends StatelessWidget {
return Center(
child: Text(
'Failed to load personal information',
style: UiTypography.body1r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.body1r.textSecondary,
),
);
}

View File

@@ -93,7 +93,7 @@ class _FieldLabel extends StatelessWidget {
Widget build(BuildContext context) {
return Text(
text,
style: UiTypography.body2m.copyWith(color: UiColors.textPrimary),
style: UiTypography.body2m.textPrimary,
);
}
}
@@ -101,7 +101,6 @@ class _FieldLabel extends StatelessWidget {
/// A read-only field widget for displaying non-editable information.
/// A read-only field widget for displaying non-editable information.
class _ReadOnlyField extends StatelessWidget {
const _ReadOnlyField({required this.value});
final String value;
@@ -120,7 +119,7 @@ class _ReadOnlyField extends StatelessWidget {
),
child: Text(
value,
style: UiTypography.body2r.copyWith(color: UiColors.textPrimary),
style: UiTypography.body2r.textPrimary,
),
);
}
@@ -129,7 +128,6 @@ class _ReadOnlyField extends StatelessWidget {
/// An editable text field widget.
/// An editable text field widget.
class _EditableField extends StatelessWidget {
const _EditableField({
required this.controller,
required this.hint,
@@ -150,10 +148,10 @@ class _EditableField extends StatelessWidget {
enabled: enabled,
keyboardType: keyboardType,
autofillHints: autofillHints,
style: UiTypography.body2r.copyWith(color: UiColors.textPrimary),
style: UiTypography.body2r.textPrimary,
decoration: InputDecoration(
hintText: hint,
hintStyle: UiTypography.body2r.copyWith(color: UiColors.textSecondary),
hintStyle: UiTypography.body2r.textSecondary,
contentPadding: const EdgeInsets.symmetric(
horizontal: UiConstants.space3,
vertical: UiConstants.space3,

View File

@@ -28,7 +28,8 @@ class ProfilePhotoWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final TranslationsStaffOnboardingPersonalInfoEn i18n = t.staff.onboarding.personal_info;
final TranslationsStaffOnboardingPersonalInfoEn i18n =
t.staff.onboarding.personal_info;
return Column(
children: <Widget>[
@@ -41,7 +42,7 @@ class ProfilePhotoWidget extends StatelessWidget {
height: 96,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: UiColors.primary.withOpacity(0.1),
color: UiColors.primary.withValues(alpha: 0.1),
),
child: photoUrl != null
? ClipOval(
@@ -53,9 +54,7 @@ class ProfilePhotoWidget extends StatelessWidget {
: Center(
child: Text(
fullName.isNotEmpty ? fullName[0].toUpperCase() : '?',
style: UiTypography.displayL.copyWith(
color: UiColors.primary,
),
style: UiTypography.displayL.primary,
),
),
),
@@ -71,7 +70,7 @@ class ProfilePhotoWidget extends StatelessWidget {
border: Border.all(color: UiColors.border),
boxShadow: <BoxShadow>[
BoxShadow(
color: UiColors.textPrimary.withOpacity(0.1),
color: UiColors.textPrimary.withValues(alpha: 0.1),
blurRadius: UiConstants.space1,
offset: const Offset(0, 2),
),
@@ -92,7 +91,7 @@ class ProfilePhotoWidget extends StatelessWidget {
const SizedBox(height: UiConstants.space3),
Text(
i18n.change_photo_hint,
style: UiTypography.body2r.copyWith(color: UiColors.textSecondary),
style: UiTypography.body2r.textSecondary,
),
],
);