feat: Implement client settings and profile management feature with sign-out functionality.
This commit is contained in:
@@ -7,6 +7,7 @@ import 'package:flutter_modular/flutter_modular.dart';
|
|||||||
import 'package:client_authentication/client_authentication.dart'
|
import 'package:client_authentication/client_authentication.dart'
|
||||||
as client_authentication;
|
as client_authentication;
|
||||||
import 'package:client_home/client_home.dart' as client_home;
|
import 'package:client_home/client_home.dart' as client_home;
|
||||||
|
import 'package:client_settings/client_settings.dart' as client_settings;
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
@@ -25,6 +26,12 @@ class AppModule extends Module {
|
|||||||
|
|
||||||
// Client home route
|
// Client home route
|
||||||
r.module('/client-home', module: client_home.ClientHomeModule());
|
r.module('/client-home', module: client_home.ClientHomeModule());
|
||||||
|
|
||||||
|
// Client settings route
|
||||||
|
r.module(
|
||||||
|
'/client-settings',
|
||||||
|
module: client_settings.ClientSettingsModule(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ dependencies:
|
|||||||
path: ../../packages/features/client/authentication
|
path: ../../packages/features/client/authentication
|
||||||
client_home:
|
client_home:
|
||||||
path: ../../packages/features/client/home
|
path: ../../packages/features/client/home
|
||||||
|
client_settings:
|
||||||
|
path: ../../packages/features/client/settings
|
||||||
core_localization:
|
core_localization:
|
||||||
path: ../../packages/core_localization
|
path: ../../packages/core_localization
|
||||||
flutter_modular: ^6.3.2
|
flutter_modular: ^6.3.2
|
||||||
|
|||||||
@@ -185,6 +185,17 @@
|
|||||||
"hourly_rate": "Hourly Rate (\\$) *",
|
"hourly_rate": "Hourly Rate (\\$) *",
|
||||||
"post_shift": "Post Shift"
|
"post_shift": "Post Shift"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"client_settings": {
|
||||||
|
"profile": {
|
||||||
|
"title": "Profile",
|
||||||
|
"edit_profile": "Edit Profile",
|
||||||
|
"hubs": "Hubs",
|
||||||
|
"log_out": "Log Out",
|
||||||
|
"quick_links": "Quick Links",
|
||||||
|
"clock_in_hubs": "Clock-In Hubs",
|
||||||
|
"billing_payments": "Billing & Payments"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -185,5 +185,16 @@
|
|||||||
"hourly_rate": "Tarifa por hora (\\$) *",
|
"hourly_rate": "Tarifa por hora (\\$) *",
|
||||||
"post_shift": "Publicar Turno"
|
"post_shift": "Publicar Turno"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"client_settings": {
|
||||||
|
"profile": {
|
||||||
|
"title": "Perfil",
|
||||||
|
"edit_profile": "Editar Perfil",
|
||||||
|
"hubs": "Hubs",
|
||||||
|
"log_out": "Cerrar sesión",
|
||||||
|
"quick_links": "Enlaces rápidos",
|
||||||
|
"clock_in_hubs": "Hubs de Marcaje",
|
||||||
|
"billing_payments": "Facturación y Pagos"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
/// To regenerate, run: `dart run slang`
|
/// To regenerate, run: `dart run slang`
|
||||||
///
|
///
|
||||||
/// Locales: 2
|
/// Locales: 2
|
||||||
/// Strings: 276 (138 per locale)
|
/// Strings: 288 (144 per locale)
|
||||||
///
|
///
|
||||||
/// Built on 2026-01-21 at 18:21 UTC
|
/// Built on 2026-01-22 at 00:33 UTC
|
||||||
|
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// ignore_for_file: type=lint, unused_import
|
// ignore_for_file: type=lint, unused_import
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ class Translations with BaseTranslations<AppLocale, Translations> {
|
|||||||
late final TranslationsStaffAuthenticationEn staff_authentication = TranslationsStaffAuthenticationEn._(_root);
|
late final TranslationsStaffAuthenticationEn staff_authentication = TranslationsStaffAuthenticationEn._(_root);
|
||||||
late final TranslationsClientAuthenticationEn client_authentication = TranslationsClientAuthenticationEn._(_root);
|
late final TranslationsClientAuthenticationEn client_authentication = TranslationsClientAuthenticationEn._(_root);
|
||||||
late final TranslationsClientHomeEn client_home = TranslationsClientHomeEn._(_root);
|
late final TranslationsClientHomeEn client_home = TranslationsClientHomeEn._(_root);
|
||||||
|
late final TranslationsClientSettingsEn client_settings = TranslationsClientSettingsEn._(_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path: common
|
// Path: common
|
||||||
@@ -120,10 +121,6 @@ class TranslationsClientHomeEn {
|
|||||||
final Translations _root; // ignore: unused_field
|
final Translations _root; // ignore: unused_field
|
||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
|
|
||||||
/// en: 'Shift order submitted successfully'
|
|
||||||
String get shift_created_success => 'Shift order submitted successfully';
|
|
||||||
|
|
||||||
late final TranslationsClientHomeDashboardEn dashboard = TranslationsClientHomeDashboardEn._(_root);
|
late final TranslationsClientHomeDashboardEn dashboard = TranslationsClientHomeDashboardEn._(_root);
|
||||||
late final TranslationsClientHomeWidgetsEn widgets = TranslationsClientHomeWidgetsEn._(_root);
|
late final TranslationsClientHomeWidgetsEn widgets = TranslationsClientHomeWidgetsEn._(_root);
|
||||||
late final TranslationsClientHomeActionsEn actions = TranslationsClientHomeActionsEn._(_root);
|
late final TranslationsClientHomeActionsEn actions = TranslationsClientHomeActionsEn._(_root);
|
||||||
@@ -131,6 +128,16 @@ class TranslationsClientHomeEn {
|
|||||||
late final TranslationsClientHomeFormEn form = TranslationsClientHomeFormEn._(_root);
|
late final TranslationsClientHomeFormEn form = TranslationsClientHomeFormEn._(_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Path: client_settings
|
||||||
|
class TranslationsClientSettingsEn {
|
||||||
|
TranslationsClientSettingsEn._(this._root);
|
||||||
|
|
||||||
|
final Translations _root; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
late final TranslationsClientSettingsProfileEn profile = TranslationsClientSettingsProfileEn._(_root);
|
||||||
|
}
|
||||||
|
|
||||||
// Path: staff_authentication.get_started_page
|
// Path: staff_authentication.get_started_page
|
||||||
class TranslationsStaffAuthenticationGetStartedPageEn {
|
class TranslationsStaffAuthenticationGetStartedPageEn {
|
||||||
TranslationsStaffAuthenticationGetStartedPageEn._(this._root);
|
TranslationsStaffAuthenticationGetStartedPageEn._(this._root);
|
||||||
@@ -541,6 +548,36 @@ class TranslationsClientHomeFormEn {
|
|||||||
String get post_shift => 'Post Shift';
|
String get post_shift => 'Post Shift';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Path: client_settings.profile
|
||||||
|
class TranslationsClientSettingsProfileEn {
|
||||||
|
TranslationsClientSettingsProfileEn._(this._root);
|
||||||
|
|
||||||
|
final Translations _root; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
|
||||||
|
/// en: 'Profile'
|
||||||
|
String get title => 'Profile';
|
||||||
|
|
||||||
|
/// en: 'Edit Profile'
|
||||||
|
String get edit_profile => 'Edit Profile';
|
||||||
|
|
||||||
|
/// en: 'Hubs'
|
||||||
|
String get hubs => 'Hubs';
|
||||||
|
|
||||||
|
/// en: 'Log Out'
|
||||||
|
String get log_out => 'Log Out';
|
||||||
|
|
||||||
|
/// en: 'Quick Links'
|
||||||
|
String get quick_links => 'Quick Links';
|
||||||
|
|
||||||
|
/// en: 'Clock-In Hubs'
|
||||||
|
String get clock_in_hubs => 'Clock-In Hubs';
|
||||||
|
|
||||||
|
/// en: 'Billing & Payments'
|
||||||
|
String get billing_payments => 'Billing & Payments';
|
||||||
|
}
|
||||||
|
|
||||||
// Path: staff_authentication.profile_setup_page.steps
|
// Path: staff_authentication.profile_setup_page.steps
|
||||||
class TranslationsStaffAuthenticationProfileSetupPageStepsEn {
|
class TranslationsStaffAuthenticationProfileSetupPageStepsEn {
|
||||||
TranslationsStaffAuthenticationProfileSetupPageStepsEn._(this._root);
|
TranslationsStaffAuthenticationProfileSetupPageStepsEn._(this._root);
|
||||||
@@ -816,7 +853,6 @@ extension on Translations {
|
|||||||
'client_authentication.sign_up_page.social_google' => 'Sign Up with Google',
|
'client_authentication.sign_up_page.social_google' => 'Sign Up with Google',
|
||||||
'client_authentication.sign_up_page.has_account' => 'Already have an account? ',
|
'client_authentication.sign_up_page.has_account' => 'Already have an account? ',
|
||||||
'client_authentication.sign_up_page.sign_in_link' => 'Sign In',
|
'client_authentication.sign_up_page.sign_in_link' => 'Sign In',
|
||||||
'client_home.shift_created_success' => 'Shift order submitted successfully',
|
|
||||||
'client_home.dashboard.welcome_back' => 'Welcome back',
|
'client_home.dashboard.welcome_back' => 'Welcome back',
|
||||||
'client_home.dashboard.edit_mode_active' => 'Edit Mode Active',
|
'client_home.dashboard.edit_mode_active' => 'Edit Mode Active',
|
||||||
'client_home.dashboard.drag_instruction' => 'Drag to reorder, toggle visibility',
|
'client_home.dashboard.drag_instruction' => 'Drag to reorder, toggle visibility',
|
||||||
@@ -855,6 +891,13 @@ extension on Translations {
|
|||||||
'client_home.form.workers_needed' => 'Workers Needed *',
|
'client_home.form.workers_needed' => 'Workers Needed *',
|
||||||
'client_home.form.hourly_rate' => 'Hourly Rate (\$) *',
|
'client_home.form.hourly_rate' => 'Hourly Rate (\$) *',
|
||||||
'client_home.form.post_shift' => 'Post Shift',
|
'client_home.form.post_shift' => 'Post Shift',
|
||||||
|
'client_settings.profile.title' => 'Profile',
|
||||||
|
'client_settings.profile.edit_profile' => 'Edit Profile',
|
||||||
|
'client_settings.profile.hubs' => 'Hubs',
|
||||||
|
'client_settings.profile.log_out' => 'Log Out',
|
||||||
|
'client_settings.profile.quick_links' => 'Quick Links',
|
||||||
|
'client_settings.profile.clock_in_hubs' => 'Clock-In Hubs',
|
||||||
|
'client_settings.profile.billing_payments' => 'Billing & Payments',
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class TranslationsEs with BaseTranslations<AppLocale, Translations> implements T
|
|||||||
@override late final _TranslationsStaffAuthenticationEs staff_authentication = _TranslationsStaffAuthenticationEs._(_root);
|
@override late final _TranslationsStaffAuthenticationEs staff_authentication = _TranslationsStaffAuthenticationEs._(_root);
|
||||||
@override late final _TranslationsClientAuthenticationEs client_authentication = _TranslationsClientAuthenticationEs._(_root);
|
@override late final _TranslationsClientAuthenticationEs client_authentication = _TranslationsClientAuthenticationEs._(_root);
|
||||||
@override late final _TranslationsClientHomeEs client_home = _TranslationsClientHomeEs._(_root);
|
@override late final _TranslationsClientHomeEs client_home = _TranslationsClientHomeEs._(_root);
|
||||||
|
@override late final _TranslationsClientSettingsEs client_settings = _TranslationsClientSettingsEs._(_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path: common
|
// Path: common
|
||||||
@@ -103,7 +104,6 @@ class _TranslationsClientHomeEs implements TranslationsClientHomeEn {
|
|||||||
final TranslationsEs _root; // ignore: unused_field
|
final TranslationsEs _root; // ignore: unused_field
|
||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get shift_created_success => 'Orden de turno enviada con éxito';
|
|
||||||
@override late final _TranslationsClientHomeDashboardEs dashboard = _TranslationsClientHomeDashboardEs._(_root);
|
@override late final _TranslationsClientHomeDashboardEs dashboard = _TranslationsClientHomeDashboardEs._(_root);
|
||||||
@override late final _TranslationsClientHomeWidgetsEs widgets = _TranslationsClientHomeWidgetsEs._(_root);
|
@override late final _TranslationsClientHomeWidgetsEs widgets = _TranslationsClientHomeWidgetsEs._(_root);
|
||||||
@override late final _TranslationsClientHomeActionsEs actions = _TranslationsClientHomeActionsEs._(_root);
|
@override late final _TranslationsClientHomeActionsEs actions = _TranslationsClientHomeActionsEs._(_root);
|
||||||
@@ -111,6 +111,16 @@ class _TranslationsClientHomeEs implements TranslationsClientHomeEn {
|
|||||||
@override late final _TranslationsClientHomeFormEs form = _TranslationsClientHomeFormEs._(_root);
|
@override late final _TranslationsClientHomeFormEs form = _TranslationsClientHomeFormEs._(_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Path: client_settings
|
||||||
|
class _TranslationsClientSettingsEs implements TranslationsClientSettingsEn {
|
||||||
|
_TranslationsClientSettingsEs._(this._root);
|
||||||
|
|
||||||
|
final TranslationsEs _root; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
@override late final _TranslationsClientSettingsProfileEs profile = _TranslationsClientSettingsProfileEs._(_root);
|
||||||
|
}
|
||||||
|
|
||||||
// Path: staff_authentication.get_started_page
|
// Path: staff_authentication.get_started_page
|
||||||
class _TranslationsStaffAuthenticationGetStartedPageEs implements TranslationsStaffAuthenticationGetStartedPageEn {
|
class _TranslationsStaffAuthenticationGetStartedPageEs implements TranslationsStaffAuthenticationGetStartedPageEn {
|
||||||
_TranslationsStaffAuthenticationGetStartedPageEs._(this._root);
|
_TranslationsStaffAuthenticationGetStartedPageEs._(this._root);
|
||||||
@@ -334,6 +344,22 @@ class _TranslationsClientHomeFormEs implements TranslationsClientHomeFormEn {
|
|||||||
@override String get post_shift => 'Publicar Turno';
|
@override String get post_shift => 'Publicar Turno';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Path: client_settings.profile
|
||||||
|
class _TranslationsClientSettingsProfileEs implements TranslationsClientSettingsProfileEn {
|
||||||
|
_TranslationsClientSettingsProfileEs._(this._root);
|
||||||
|
|
||||||
|
final TranslationsEs _root; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
@override String get title => 'Perfil';
|
||||||
|
@override String get edit_profile => 'Editar Perfil';
|
||||||
|
@override String get hubs => 'Hubs';
|
||||||
|
@override String get log_out => 'Cerrar sesión';
|
||||||
|
@override String get quick_links => 'Enlaces rápidos';
|
||||||
|
@override String get clock_in_hubs => 'Hubs de Marcaje';
|
||||||
|
@override String get billing_payments => 'Facturación y Pagos';
|
||||||
|
}
|
||||||
|
|
||||||
// Path: staff_authentication.profile_setup_page.steps
|
// Path: staff_authentication.profile_setup_page.steps
|
||||||
class _TranslationsStaffAuthenticationProfileSetupPageStepsEs implements TranslationsStaffAuthenticationProfileSetupPageStepsEn {
|
class _TranslationsStaffAuthenticationProfileSetupPageStepsEs implements TranslationsStaffAuthenticationProfileSetupPageStepsEn {
|
||||||
_TranslationsStaffAuthenticationProfileSetupPageStepsEs._(this._root);
|
_TranslationsStaffAuthenticationProfileSetupPageStepsEs._(this._root);
|
||||||
@@ -534,7 +560,6 @@ extension on TranslationsEs {
|
|||||||
'client_authentication.sign_up_page.social_google' => 'Regístrate con Google',
|
'client_authentication.sign_up_page.social_google' => 'Regístrate con Google',
|
||||||
'client_authentication.sign_up_page.has_account' => '¿Ya tienes una cuenta? ',
|
'client_authentication.sign_up_page.has_account' => '¿Ya tienes una cuenta? ',
|
||||||
'client_authentication.sign_up_page.sign_in_link' => 'Iniciar sesión',
|
'client_authentication.sign_up_page.sign_in_link' => 'Iniciar sesión',
|
||||||
'client_home.shift_created_success' => 'Orden de turno enviada con éxito',
|
|
||||||
'client_home.dashboard.welcome_back' => 'Bienvenido de nuevo',
|
'client_home.dashboard.welcome_back' => 'Bienvenido de nuevo',
|
||||||
'client_home.dashboard.edit_mode_active' => 'Modo Edición Activo',
|
'client_home.dashboard.edit_mode_active' => 'Modo Edición Activo',
|
||||||
'client_home.dashboard.drag_instruction' => 'Arrastra para reordenar, cambia la visibilidad',
|
'client_home.dashboard.drag_instruction' => 'Arrastra para reordenar, cambia la visibilidad',
|
||||||
@@ -573,6 +598,13 @@ extension on TranslationsEs {
|
|||||||
'client_home.form.workers_needed' => 'Trabajadores Necesarios *',
|
'client_home.form.workers_needed' => 'Trabajadores Necesarios *',
|
||||||
'client_home.form.hourly_rate' => 'Tarifa por hora (\$) *',
|
'client_home.form.hourly_rate' => 'Tarifa por hora (\$) *',
|
||||||
'client_home.form.post_shift' => 'Publicar Turno',
|
'client_home.form.post_shift' => 'Publicar Turno',
|
||||||
|
'client_settings.profile.title' => 'Perfil',
|
||||||
|
'client_settings.profile.edit_profile' => 'Editar Perfil',
|
||||||
|
'client_settings.profile.hubs' => 'Hubs',
|
||||||
|
'client_settings.profile.log_out' => 'Cerrar sesión',
|
||||||
|
'client_settings.profile.quick_links' => 'Enlaces rápidos',
|
||||||
|
'client_settings.profile.clock_in_hubs' => 'Hubs de Marcaje',
|
||||||
|
'client_settings.profile.billing_payments' => 'Facturación y Pagos',
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,4 +171,7 @@ class UiIcons {
|
|||||||
|
|
||||||
/// Google icon
|
/// Google icon
|
||||||
static const IconData google = _IconLib2.google;
|
static const IconData google = _IconLib2.google;
|
||||||
|
|
||||||
|
/// NFC icon
|
||||||
|
static const IconData nfc = _IconLib.nfc;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
|
|
||||||
/// Navigation extension for the Client Home feature.
|
/// Extension on [IModularNavigator] to provide strongly-typed navigation
|
||||||
|
/// for the client home feature.
|
||||||
extension ClientHomeNavigator on IModularNavigator {
|
extension ClientHomeNavigator on IModularNavigator {
|
||||||
/// Navigates to the Client Home page.
|
/// Navigates to the client home page.
|
||||||
void navigateClientHome() => navigate('/client-home/');
|
void pushClientHome() {
|
||||||
|
pushNamed('/client/home/');
|
||||||
|
}
|
||||||
|
|
||||||
/// Pushes the Client Home page.
|
/// Navigates to the settings page.
|
||||||
Future<void> pushClientHome() => pushNamed('/client-home/');
|
void pushSettings() {
|
||||||
|
pushNamed('/client-settings/');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:flutter_modular/flutter_modular.dart';
|
|||||||
import '../blocs/client_home_bloc.dart';
|
import '../blocs/client_home_bloc.dart';
|
||||||
import '../blocs/client_home_event.dart';
|
import '../blocs/client_home_event.dart';
|
||||||
import '../blocs/client_home_state.dart';
|
import '../blocs/client_home_state.dart';
|
||||||
|
import '../navigation/client_home_navigator.dart';
|
||||||
import '../widgets/actions_widget.dart';
|
import '../widgets/actions_widget.dart';
|
||||||
import '../widgets/coverage_widget.dart';
|
import '../widgets/coverage_widget.dart';
|
||||||
import '../widgets/live_activity_widget.dart';
|
import '../widgets/live_activity_widget.dart';
|
||||||
@@ -176,7 +177,10 @@ class ClientHomePage extends StatelessWidget {
|
|||||||
onTap: () {},
|
onTap: () {},
|
||||||
),
|
),
|
||||||
const SizedBox(width: UiConstants.space2),
|
const SizedBox(width: UiConstants.space2),
|
||||||
_HeaderIconButton(icon: UiIcons.settings, onTap: () {}),
|
_HeaderIconButton(
|
||||||
|
icon: UiIcons.settings,
|
||||||
|
onTap: () => Modular.to.pushSettings(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
|
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||||
|
import 'src/data/repositories_impl/settings_repository_impl.dart';
|
||||||
|
import 'src/domain/repositories/settings_repository_interface.dart';
|
||||||
|
import 'src/domain/usecases/sign_out_usecase.dart';
|
||||||
|
import 'src/presentation/blocs/client_settings_bloc.dart';
|
||||||
|
import 'src/presentation/pages/client_settings_page.dart';
|
||||||
|
|
||||||
|
/// A [Module] for the client settings feature.
|
||||||
|
class ClientSettingsModule extends Module {
|
||||||
|
@override
|
||||||
|
List<Module> get imports => [DataConnectModule()];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void binds(Injector i) {
|
||||||
|
// Repositories
|
||||||
|
i.addLazySingleton<SettingsRepositoryInterface>(
|
||||||
|
() => SettingsRepositoryImpl(i.get<AuthRepositoryMock>()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// UseCases
|
||||||
|
i.addLazySingleton(SignOutUseCase.new);
|
||||||
|
|
||||||
|
// BLoCs
|
||||||
|
i.add<ClientSettingsBloc>(
|
||||||
|
() => ClientSettingsBloc(signOutUseCase: i.get<SignOutUseCase>()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void routes(r) {
|
||||||
|
r.child('/', child: (_) => const ClientSettingsPage());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||||
|
import '../../domain/repositories/settings_repository_interface.dart';
|
||||||
|
|
||||||
|
/// Implementation of [SettingsRepositoryInterface] that delegates to [AuthRepositoryMock].
|
||||||
|
class SettingsRepositoryImpl implements SettingsRepositoryInterface {
|
||||||
|
final AuthRepositoryMock _authMock;
|
||||||
|
|
||||||
|
/// Creates a [SettingsRepositoryImpl].
|
||||||
|
SettingsRepositoryImpl(this._authMock);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> signOut() {
|
||||||
|
return _authMock.signOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
/// Interface for the Client Settings Repository.
|
||||||
|
abstract interface class SettingsRepositoryInterface {
|
||||||
|
/// Signs out the current user.
|
||||||
|
Future<void> signOut();
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import 'package:krow_core/core.dart';
|
||||||
|
import '../repositories/settings_repository_interface.dart';
|
||||||
|
|
||||||
|
/// Use case handles the user sign out process.
|
||||||
|
class SignOutUseCase implements NoInputUseCase<void> {
|
||||||
|
final SettingsRepositoryInterface _repository;
|
||||||
|
|
||||||
|
/// Creates a [SignOutUseCase].
|
||||||
|
SignOutUseCase(this._repository);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> call() {
|
||||||
|
return _repository.signOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import '../../domain/usecases/sign_out_usecase.dart';
|
||||||
|
|
||||||
|
part 'client_settings_event.dart';
|
||||||
|
part 'client_settings_state.dart';
|
||||||
|
|
||||||
|
/// BLoC to manage client settings and profile state.
|
||||||
|
class ClientSettingsBloc
|
||||||
|
extends Bloc<ClientSettingsEvent, ClientSettingsState> {
|
||||||
|
final SignOutUseCase _signOutUseCase;
|
||||||
|
|
||||||
|
ClientSettingsBloc({required SignOutUseCase signOutUseCase})
|
||||||
|
: _signOutUseCase = signOutUseCase,
|
||||||
|
super(const ClientSettingsInitial()) {
|
||||||
|
on<ClientSettingsSignOutRequested>(_onSignOutRequested);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onSignOutRequested(
|
||||||
|
ClientSettingsSignOutRequested event,
|
||||||
|
Emitter<ClientSettingsState> emit,
|
||||||
|
) async {
|
||||||
|
emit(const ClientSettingsLoading());
|
||||||
|
try {
|
||||||
|
await _signOutUseCase();
|
||||||
|
emit(const ClientSettingsSignOutSuccess());
|
||||||
|
} catch (e) {
|
||||||
|
emit(ClientSettingsError(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
part of 'client_settings_bloc.dart';
|
||||||
|
|
||||||
|
abstract class ClientSettingsEvent extends Equatable {
|
||||||
|
const ClientSettingsEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientSettingsSignOutRequested extends ClientSettingsEvent {
|
||||||
|
const ClientSettingsSignOutRequested();
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
part of 'client_settings_bloc.dart';
|
||||||
|
|
||||||
|
abstract class ClientSettingsState extends Equatable {
|
||||||
|
const ClientSettingsState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientSettingsInitial extends ClientSettingsState {
|
||||||
|
const ClientSettingsInitial();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientSettingsLoading extends ClientSettingsState {
|
||||||
|
const ClientSettingsLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientSettingsSignOutSuccess extends ClientSettingsState {
|
||||||
|
const ClientSettingsSignOutSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientSettingsError extends ClientSettingsState {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
const ClientSettingsError(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [message];
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
|
|
||||||
|
/// Extension on [IModularNavigator] to provide strongly-typed navigation
|
||||||
|
/// for the client settings feature.
|
||||||
|
extension ClientSettingsNavigator on IModularNavigator {
|
||||||
|
/// Navigates to the client settings page.
|
||||||
|
void pushClientSettings() {
|
||||||
|
pushNamed('/client/settings/');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
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';
|
||||||
|
import '../blocs/client_settings_bloc.dart';
|
||||||
|
|
||||||
|
/// Page for client settings and profile management.
|
||||||
|
class ClientSettingsPage extends StatelessWidget {
|
||||||
|
/// Creates a [ClientSettingsPage].
|
||||||
|
const ClientSettingsPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final labels = t.client_settings.profile;
|
||||||
|
|
||||||
|
return BlocProvider<ClientSettingsBloc>(
|
||||||
|
create: (context) => Modular.get<ClientSettingsBloc>(),
|
||||||
|
child: BlocListener<ClientSettingsBloc, ClientSettingsState>(
|
||||||
|
listener: (context, state) {
|
||||||
|
if (state is ClientSettingsSignOutSuccess) {
|
||||||
|
Modular.to.navigate('/');
|
||||||
|
}
|
||||||
|
if (state is ClientSettingsError) {
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context,
|
||||||
|
).showSnackBar(SnackBar(content: Text(state.message)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Scaffold(
|
||||||
|
backgroundColor: UiColors.background,
|
||||||
|
body: CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
SliverAppBar(
|
||||||
|
backgroundColor: UiColors.primary,
|
||||||
|
expandedHeight: 200,
|
||||||
|
pinned: true,
|
||||||
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
|
background: Container(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [UiColors.primary, Color(0xFF0047FF)],
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: UiConstants.space5 * 2,
|
||||||
|
), // Adjust for SafeArea
|
||||||
|
Container(
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
color: UiColors.white.withValues(alpha: 0.24),
|
||||||
|
width: 4,
|
||||||
|
),
|
||||||
|
color: UiColors.white,
|
||||||
|
),
|
||||||
|
child: const Center(
|
||||||
|
child: Text(
|
||||||
|
'C',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: UiColors.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space3),
|
||||||
|
Text(
|
||||||
|
'Your Company',
|
||||||
|
style: UiTypography.body1b.copyWith(
|
||||||
|
color: UiColors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space1),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
UiIcons.mail,
|
||||||
|
size: 14,
|
||||||
|
color: UiColors.white.withValues(alpha: 0.7),
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space2),
|
||||||
|
Text(
|
||||||
|
'client@example.com',
|
||||||
|
style: UiTypography.footnote1r.copyWith(
|
||||||
|
color: UiColors.white.withValues(alpha: 0.7),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(UiIcons.arrowLeft, color: UiColors.white),
|
||||||
|
onPressed: () => Modular.to.pop(),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
labels.title,
|
||||||
|
style: UiTypography.body1b.copyWith(color: UiColors.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SliverPadding(
|
||||||
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
|
sliver: SliverList(
|
||||||
|
delegate: SliverChildListDelegate([
|
||||||
|
UiButton.primary(
|
||||||
|
text: labels.edit_profile,
|
||||||
|
onPressed: () {},
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
UiButton.primary(text: labels.hubs, onPressed: () {}),
|
||||||
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
BlocBuilder<ClientSettingsBloc, ClientSettingsState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return UiButton.secondary(
|
||||||
|
text: labels.log_out,
|
||||||
|
onPressed: state is ClientSettingsLoading
|
||||||
|
? null
|
||||||
|
: () => BlocProvider.of<ClientSettingsBloc>(
|
||||||
|
context,
|
||||||
|
).add(const ClientSettingsSignOutRequested()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space5),
|
||||||
|
Card(
|
||||||
|
elevation: 0,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: UiConstants.radiusLg,
|
||||||
|
side: const BorderSide(color: UiColors.border),
|
||||||
|
),
|
||||||
|
color: UiColors.white,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
labels.quick_links,
|
||||||
|
style: UiTypography.footnote1b.textPrimary,
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space3),
|
||||||
|
_buildQuickLink(
|
||||||
|
context,
|
||||||
|
icon: UiIcons.nfc,
|
||||||
|
title: labels.clock_in_hubs,
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
_buildQuickLink(
|
||||||
|
context,
|
||||||
|
icon: UiIcons.building,
|
||||||
|
title: labels.billing_payments,
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildQuickLink(
|
||||||
|
BuildContext context, {
|
||||||
|
required IconData icon,
|
||||||
|
required String title,
|
||||||
|
required VoidCallback onTap,
|
||||||
|
}) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
borderRadius: UiConstants.radiusMd,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space3,
|
||||||
|
horizontal: UiConstants.space2,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(icon, size: 20, color: UiColors.iconSecondary),
|
||||||
|
const SizedBox(width: UiConstants.space3),
|
||||||
|
Text(title, style: UiTypography.footnote1m.textPrimary),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Icon(
|
||||||
|
UiIcons.chevronRight,
|
||||||
|
size: 20,
|
||||||
|
color: UiColors.iconThird,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
apps/packages/features/client/settings/pubspec.yaml
Normal file
37
apps/packages/features/client/settings/pubspec.yaml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
name: client_settings
|
||||||
|
description: Settings and profile screen for the client application.
|
||||||
|
version: 0.0.1
|
||||||
|
publish_to: none
|
||||||
|
resolution: workspace
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=3.10.0 <4.0.0'
|
||||||
|
flutter: ">=3.0.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_bloc: ^8.1.0
|
||||||
|
flutter_modular: ^6.3.0
|
||||||
|
equatable: ^2.0.5
|
||||||
|
lucide_icons: ^0.257.0
|
||||||
|
|
||||||
|
design_system:
|
||||||
|
path: ../../../design_system
|
||||||
|
core_localization:
|
||||||
|
path: ../../../core_localization
|
||||||
|
krow_core:
|
||||||
|
path: ../../../core
|
||||||
|
krow_domain:
|
||||||
|
path: ../../../domain
|
||||||
|
krow_data_connect:
|
||||||
|
path: ../../../data_connect
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
bloc_test: ^9.1.0
|
||||||
|
mocktail: ^1.0.0
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
||||||
@@ -12,6 +12,7 @@ workspace:
|
|||||||
- packages/features/staff/authentication
|
- packages/features/staff/authentication
|
||||||
- packages/features/client/authentication
|
- packages/features/client/authentication
|
||||||
- packages/features/client/home
|
- packages/features/client/home
|
||||||
|
- packages/features/client/settings
|
||||||
- apps/staff
|
- apps/staff
|
||||||
- apps/client
|
- apps/client
|
||||||
- apps/design_system_viewer
|
- apps/design_system_viewer
|
||||||
|
|||||||
Reference in New Issue
Block a user