feat: Implement language selection feature in staff profile onboarding
This commit is contained in:
@@ -16,7 +16,7 @@ class StaffPaths {
|
||||
/// Generate child route based on the given route and parent route
|
||||
///
|
||||
/// This is useful for creating nested routes within modules.
|
||||
static String childRoute(String parent, String child) {
|
||||
static String childRoute(String parent, String child) {
|
||||
final String childPath = child.replaceFirst(parent, '');
|
||||
|
||||
// check if the child path is empty
|
||||
@@ -107,8 +107,7 @@ class StaffPaths {
|
||||
/// Path format: `/worker-main/shift-details/{shiftId}`
|
||||
///
|
||||
/// Example: `/worker-main/shift-details/shift123`
|
||||
static String shiftDetails(String shiftId) =>
|
||||
'$shiftDetailsRoute/$shiftId';
|
||||
static String shiftDetails(String shiftId) => '$shiftDetailsRoute/$shiftId';
|
||||
|
||||
// ==========================================================================
|
||||
// ONBOARDING & PROFILE SECTIONS
|
||||
@@ -117,8 +116,17 @@ class StaffPaths {
|
||||
/// Personal information onboarding.
|
||||
///
|
||||
/// Collect basic personal information during staff onboarding.
|
||||
static const String onboardingPersonalInfo =
|
||||
'/worker-main/onboarding/personal-info/';
|
||||
static const String onboardingPersonalInfo = '/worker-main/personal-info/';
|
||||
|
||||
// ==========================================================================
|
||||
// PERSONAL INFORMATION & PREFERENCES
|
||||
// ==========================================================================
|
||||
|
||||
/// Language selection page.
|
||||
///
|
||||
/// Allows staff to select their preferred language for the app interface.
|
||||
static const String languageSelection =
|
||||
'/worker-main/personal-info/language-selection/';
|
||||
|
||||
/// Emergency contact information.
|
||||
///
|
||||
|
||||
@@ -130,9 +130,6 @@ class StaffProfilePage extends StatelessWidget {
|
||||
// Support section
|
||||
const SupportSection(),
|
||||
|
||||
// Settings section
|
||||
const SettingsSection(),
|
||||
|
||||
// Logout button at the bottom
|
||||
const LogoutButton(),
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export 'compliance_section.dart';
|
||||
export 'finance_section.dart';
|
||||
export 'onboarding_section.dart';
|
||||
export 'settings_section.dart';
|
||||
export 'support_section.dart';
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
import 'package:core_localization/core_localization.dart';
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
|
||||
/// Language selection page for staff profile.
|
||||
///
|
||||
/// Displays available languages and allows the user to select their preferred
|
||||
/// language. Changes are applied immediately via [LocaleBloc] and persisted.
|
||||
/// Shows a snackbar when the language is successfully changed.
|
||||
class LanguageSelectionPage extends StatefulWidget {
|
||||
/// Creates a [LanguageSelectionPage].
|
||||
const LanguageSelectionPage({super.key});
|
||||
|
||||
@override
|
||||
State<LanguageSelectionPage> createState() => _LanguageSelectionPageState();
|
||||
}
|
||||
|
||||
class _LanguageSelectionPageState extends State<LanguageSelectionPage> {
|
||||
void _showLanguageChangedSnackbar(String languageName) {
|
||||
UiSnackbar.show(
|
||||
context,
|
||||
message: 'Language changed to $languageName',
|
||||
type: UiSnackbarType.success,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: UiAppBar(
|
||||
title: 'Select Language',
|
||||
showBackButton: true,
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(1.0),
|
||||
child: Container(color: UiColors.border, height: 1.0),
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: BlocBuilder<LocaleBloc, LocaleState>(
|
||||
builder: (BuildContext context, LocaleState state) {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(UiConstants.space5),
|
||||
children: <Widget>[
|
||||
_buildLanguageOption(
|
||||
context,
|
||||
label: 'English',
|
||||
locale: AppLocale.en,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
_buildLanguageOption(
|
||||
context,
|
||||
label: 'Español',
|
||||
locale: AppLocale.es,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLanguageOption(
|
||||
BuildContext context, {
|
||||
required String label,
|
||||
required AppLocale locale,
|
||||
}) {
|
||||
// Check if this option is currently selected.
|
||||
final AppLocale currentLocale = LocaleSettings.currentLocale;
|
||||
final bool isSelected = currentLocale == locale;
|
||||
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
// Only proceed if selecting a different language
|
||||
if (currentLocale != locale) {
|
||||
Modular.get<LocaleBloc>().add(ChangeLocale(locale.flutterLocale));
|
||||
_showLanguageChangedSnackbar(label);
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: UiConstants.space4,
|
||||
horizontal: UiConstants.space4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? UiColors.primary.withValues(alpha: 0.1)
|
||||
: UiColors.background,
|
||||
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
||||
border: Border.all(
|
||||
color: isSelected ? UiColors.primary : UiColors.border,
|
||||
width: isSelected ? 2 : 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
label,
|
||||
style: isSelected
|
||||
? UiTypography.body1b.copyWith(color: UiColors.primary)
|
||||
: UiTypography.body1r,
|
||||
),
|
||||
if (isSelected) const Icon(UiIcons.check, color: UiColors.primary),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:core_localization/core_localization.dart';
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:krow_core/core.dart';
|
||||
|
||||
|
||||
/// A form widget containing all personal information fields.
|
||||
@@ -77,11 +79,73 @@ class PersonalInfoForm extends StatelessWidget {
|
||||
hint: i18n.locations_hint,
|
||||
enabled: enabled,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
|
||||
_FieldLabel(text: 'Language'),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
_LanguageSelector(
|
||||
enabled: enabled,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A language selector widget that displays the current language and navigates to language selection page.
|
||||
class _LanguageSelector extends StatelessWidget {
|
||||
const _LanguageSelector({
|
||||
this.enabled = true,
|
||||
});
|
||||
|
||||
final bool enabled;
|
||||
|
||||
String _getLanguageLabel(AppLocale locale) {
|
||||
switch (locale) {
|
||||
case AppLocale.en:
|
||||
return 'English';
|
||||
case AppLocale.es:
|
||||
return 'Español';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final AppLocale currentLocale = LocaleSettings.currentLocale;
|
||||
final String currentLanguage = _getLanguageLabel(currentLocale);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: enabled
|
||||
? () => Modular.to.pushNamed(StaffPaths.languageSelection)
|
||||
: null,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space3,
|
||||
vertical: UiConstants.space3,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgPopup,
|
||||
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
||||
border: Border.all(color: UiColors.border),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
currentLanguage,
|
||||
style: UiTypography.body2r.textPrimary,
|
||||
),
|
||||
Icon(
|
||||
UiIcons.chevronRight,
|
||||
color: UiColors.textSecondary,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A label widget for form fields.
|
||||
/// A label widget for form fields.
|
||||
class _FieldLabel extends StatelessWidget {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:krow_core/core.dart';
|
||||
|
||||
import 'data/repositories/personal_info_repository_impl.dart';
|
||||
import 'domain/repositories/personal_info_repository_interface.dart';
|
||||
@@ -7,6 +8,7 @@ import 'domain/usecases/get_personal_info_usecase.dart';
|
||||
import 'domain/usecases/update_personal_info_usecase.dart';
|
||||
import 'presentation/blocs/personal_info_bloc.dart';
|
||||
import 'presentation/pages/personal_info_page.dart';
|
||||
import 'presentation/pages/language_selection_page.dart';
|
||||
|
||||
/// The entry module for the Staff Profile Info feature.
|
||||
///
|
||||
@@ -23,7 +25,8 @@ class StaffProfileInfoModule extends Module {
|
||||
void binds(Injector i) {
|
||||
// Repository
|
||||
i.addLazySingleton<PersonalInfoRepositoryInterface>(
|
||||
PersonalInfoRepositoryImpl.new);
|
||||
PersonalInfoRepositoryImpl.new,
|
||||
);
|
||||
|
||||
// Use Cases - delegate business logic to repository
|
||||
i.addLazySingleton<GetPersonalInfoUseCase>(
|
||||
@@ -45,13 +48,18 @@ class StaffProfileInfoModule extends Module {
|
||||
@override
|
||||
void routes(RouteManager r) {
|
||||
r.child(
|
||||
'/personal-info',
|
||||
StaffPaths.childRoute(
|
||||
StaffPaths.onboardingPersonalInfo,
|
||||
StaffPaths.onboardingPersonalInfo,
|
||||
),
|
||||
child: (BuildContext context) => const PersonalInfoPage(),
|
||||
);
|
||||
// Alias with trailing slash to be tolerant of external deep links
|
||||
r.child(
|
||||
'/personal-info/',
|
||||
child: (BuildContext context) => const PersonalInfoPage(),
|
||||
StaffPaths.childRoute(
|
||||
StaffPaths.onboardingPersonalInfo,
|
||||
StaffPaths.languageSelection,
|
||||
),
|
||||
child: (BuildContext context) => const LanguageSelectionPage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ class StaffMainModule extends Module {
|
||||
],
|
||||
);
|
||||
r.module(
|
||||
StaffPaths.childRoute(StaffPaths.main, StaffPaths.onboardingPersonalInfo).replaceFirst('/personal-info', ''),
|
||||
StaffPaths.childRoute(StaffPaths.main, StaffPaths.onboardingPersonalInfo),
|
||||
module: StaffProfileInfoModule(),
|
||||
);
|
||||
r.module(
|
||||
|
||||
Reference in New Issue
Block a user