feat: Implement Hubs feature with dedicated navigation, a home page action card, a settings quick link, and localization.

This commit is contained in:
Achintha Isuru
2026-01-21 20:11:09 -05:00
parent 9d9d2aa456
commit 61d7c08c95
15 changed files with 65 additions and 7 deletions

View File

@@ -8,6 +8,7 @@ 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; import 'package:client_settings/client_settings.dart' as client_settings;
import 'package:client_hubs/client_hubs.dart' as client_hubs;
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
@@ -32,6 +33,7 @@ class AppModule extends Module {
'/client-settings', '/client-settings',
module: client_settings.ClientSettingsModule(), module: client_settings.ClientSettingsModule(),
); );
r.module('/client/hubs', module: client_hubs.ClientHubsModule());
} }
} }

View File

@@ -21,6 +21,8 @@ dependencies:
path: ../../packages/features/client/settings path: ../../packages/features/client/settings
core_localization: core_localization:
path: ../../packages/core_localization path: ../../packages/core_localization
client_hubs:
path: ../../packages/features/client/hubs
flutter_modular: ^6.3.2 flutter_modular: ^6.3.2
flutter_bloc: ^8.1.3 flutter_bloc: ^8.1.3
flutter_localizations: flutter_localizations:

View File

@@ -160,7 +160,9 @@
"rapid": "RAPID", "rapid": "RAPID",
"rapid_subtitle": "Urgent same-day", "rapid_subtitle": "Urgent same-day",
"create_order": "Create Order", "create_order": "Create Order",
"create_order_subtitle": "Schedule shifts" "create_order_subtitle": "Schedule shifts",
"hubs": "Hubs",
"hubs_subtitle": "Clock-in points"
}, },
"reorder": { "reorder": {
"title": "REORDER", "title": "REORDER",

View File

@@ -160,7 +160,9 @@
"rapid": "RÁPIDO", "rapid": "RÁPIDO",
"rapid_subtitle": "Urgente mismo día", "rapid_subtitle": "Urgente mismo día",
"create_order": "Crear Orden", "create_order": "Crear Orden",
"create_order_subtitle": "Programar turnos" "create_order_subtitle": "Programar turnos",
"hubs": "Hubs",
"hubs_subtitle": "Puntos marcaje"
}, },
"reorder": { "reorder": {
"title": "REORDENAR", "title": "REORDENAR",

View File

@@ -4,9 +4,9 @@
/// To regenerate, run: `dart run slang` /// To regenerate, run: `dart run slang`
/// ///
/// Locales: 2 /// Locales: 2
/// Strings: 332 (166 per locale) /// Strings: 336 (168 per locale)
/// ///
/// Built on 2026-01-22 at 00:48 UTC /// Built on 2026-01-22 at 01:00 UTC
// coverage:ignore-file // coverage:ignore-file
// ignore_for_file: type=lint, unused_import // ignore_for_file: type=lint, unused_import

View File

@@ -496,6 +496,12 @@ class TranslationsClientHomeActionsEn {
/// en: 'Schedule shifts' /// en: 'Schedule shifts'
String get create_order_subtitle => 'Schedule shifts'; String get create_order_subtitle => 'Schedule shifts';
/// en: 'Hubs'
String get hubs => 'Hubs';
/// en: 'Clock-in points'
String get hubs_subtitle => 'Clock-in points';
} }
// Path: client_home.reorder // Path: client_home.reorder
@@ -999,6 +1005,8 @@ extension on Translations {
'client_home.actions.rapid_subtitle' => 'Urgent same-day', 'client_home.actions.rapid_subtitle' => 'Urgent same-day',
'client_home.actions.create_order' => 'Create Order', 'client_home.actions.create_order' => 'Create Order',
'client_home.actions.create_order_subtitle' => 'Schedule shifts', 'client_home.actions.create_order_subtitle' => 'Schedule shifts',
'client_home.actions.hubs' => 'Hubs',
'client_home.actions.hubs_subtitle' => 'Clock-in points',
'client_home.reorder.title' => 'REORDER', 'client_home.reorder.title' => 'REORDER',
'client_home.reorder.reorder_button' => 'Reorder', 'client_home.reorder.reorder_button' => 'Reorder',
'client_home.reorder.per_hr' => ({required Object amount}) => '${amount}/hr', 'client_home.reorder.per_hr' => ({required Object amount}) => '${amount}/hr',

View File

@@ -323,6 +323,8 @@ class _TranslationsClientHomeActionsEs implements TranslationsClientHomeActionsE
@override String get rapid_subtitle => 'Urgente mismo día'; @override String get rapid_subtitle => 'Urgente mismo día';
@override String get create_order => 'Crear Orden'; @override String get create_order => 'Crear Orden';
@override String get create_order_subtitle => 'Programar turnos'; @override String get create_order_subtitle => 'Programar turnos';
@override String get hubs => 'Hubs';
@override String get hubs_subtitle => 'Puntos marcaje';
} }
// Path: client_home.reorder // Path: client_home.reorder
@@ -661,6 +663,8 @@ extension on TranslationsEs {
'client_home.actions.rapid_subtitle' => 'Urgente mismo día', 'client_home.actions.rapid_subtitle' => 'Urgente mismo día',
'client_home.actions.create_order' => 'Crear Orden', 'client_home.actions.create_order' => 'Crear Orden',
'client_home.actions.create_order_subtitle' => 'Programar turnos', 'client_home.actions.create_order_subtitle' => 'Programar turnos',
'client_home.actions.hubs' => 'Hubs',
'client_home.actions.hubs_subtitle' => 'Puntos marcaje',
'client_home.reorder.title' => 'REORDENAR', 'client_home.reorder.title' => 'REORDENAR',
'client_home.reorder.reorder_button' => 'Reordenar', 'client_home.reorder.reorder_button' => 'Reordenar',
'client_home.reorder.per_hr' => ({required Object amount}) => '${amount}/hr', 'client_home.reorder.per_hr' => ({required Object amount}) => '${amount}/hr',

View File

@@ -1,13 +1,15 @@
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'mocks/auth_repository_mock.dart'; import 'mocks/auth_repository_mock.dart';
import 'mocks/business_repository_mock.dart';
import 'mocks/home_repository_mock.dart'; import 'mocks/home_repository_mock.dart';
/// A module that provides Data Connect dependencies, including mocks. /// A module that provides Data Connect dependencies, including mocks.
class DataConnectModule extends Module { class DataConnectModule extends Module {
@override @override
void exportedBinds(Injector i) { void exportedBinds(Injector i) {
// Make the AuthRepositoryMock available to any module that imports this one. // Make these mocks available to any module that imports this one.
i.addLazySingleton(AuthRepositoryMock.new); i.addLazySingleton(AuthRepositoryMock.new);
i.addLazySingleton(HomeRepositoryMock.new); i.addLazySingleton(HomeRepositoryMock.new);
i.addLazySingleton(BusinessRepositoryMock.new);
} }
} }

View File

@@ -12,4 +12,9 @@ extension ClientHomeNavigator on IModularNavigator {
void pushSettings() { void pushSettings() {
pushNamed('/client-settings/'); pushNamed('/client-settings/');
} }
/// Navigates to the hubs page.
void pushHubs() {
pushNamed('/client/hubs');
}
} }

View File

@@ -324,6 +324,7 @@ class ClientHomePage extends StatelessWidget {
return ActionsWidget( return ActionsWidget(
onRapidPressed: () {}, onRapidPressed: () {},
onCreateOrderPressed: () => _openOrderFormSheet(context, null), onCreateOrderPressed: () => _openOrderFormSheet(context, null),
onHubsPressed: () => Modular.to.pushHubs(),
); );
case 'reorder': case 'reorder':
return ReorderWidget( return ReorderWidget(

View File

@@ -10,11 +10,15 @@ class ActionsWidget extends StatelessWidget {
/// Callback when Create Order is pressed. /// Callback when Create Order is pressed.
final VoidCallback onCreateOrderPressed; final VoidCallback onCreateOrderPressed;
/// Callback when Hubs is pressed.
final VoidCallback onHubsPressed;
/// Creates an [ActionsWidget]. /// Creates an [ActionsWidget].
const ActionsWidget({ const ActionsWidget({
super.key, super.key,
required this.onRapidPressed, required this.onRapidPressed,
required this.onCreateOrderPressed, required this.onCreateOrderPressed,
required this.onHubsPressed,
}); });
@override @override
@@ -53,6 +57,21 @@ class ActionsWidget extends StatelessWidget {
onTap: onCreateOrderPressed, onTap: onCreateOrderPressed,
), ),
), ),
const SizedBox(width: UiConstants.space2),
Expanded(
child: _ActionCard(
title: i18n.hubs,
subtitle: i18n.hubs_subtitle,
icon: UiIcons.nfc,
color: const Color(0xFFF0FDF4),
borderColor: const Color(0xFFBBF7D0),
iconBgColor: const Color(0xFFDCFCE7),
iconColor: const Color(0xFF16A34A),
textColor: const Color(0xFF064E3B),
subtitleColor: const Color(0xFF15803D),
onTap: onHubsPressed,
),
),
], ],
); );
} }

View File

@@ -4,6 +4,6 @@ import 'package:flutter_modular/flutter_modular.dart';
extension ClientHubsNavigator on IModularNavigator { extension ClientHubsNavigator on IModularNavigator {
/// Navigates to the client hubs page. /// Navigates to the client hubs page.
Future<void> pushClientHubs() async { Future<void> pushClientHubs() async {
await pushNamed('/client/hubs/'); await pushNamed('/client/hubs');
} }
} }

View File

@@ -196,6 +196,10 @@ class ClientHubsPage extends StatelessWidget {
).add(const ClientHubsAddDialogToggled(visible: true)), ).add(const ClientHubsAddDialogToggled(visible: true)),
text: t.client_hubs.add_hub, text: t.client_hubs.add_hub,
leadingIcon: LucideIcons.plus, leadingIcon: LucideIcons.plus,
style: ElevatedButton.styleFrom(
minimumSize: Size(0, 40),
maximumSize: Size.fromHeight(40),
),
), ),
], ],
), ),

View File

@@ -7,4 +7,9 @@ extension ClientSettingsNavigator on IModularNavigator {
void pushClientSettings() { void pushClientSettings() {
pushNamed('/client/settings/'); pushNamed('/client/settings/');
} }
/// Navigates to the hubs page.
void pushHubs() {
pushNamed('/client/hubs');
}
} }

View File

@@ -1,6 +1,8 @@
import 'package:core_localization/core_localization.dart'; import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';
import '../../navigation/client_settings_navigator.dart';
/// A widget that displays a list of quick links in a card. /// A widget that displays a list of quick links in a card.
class SettingsQuickLinks extends StatelessWidget { class SettingsQuickLinks extends StatelessWidget {
@@ -34,7 +36,7 @@ class SettingsQuickLinks extends StatelessWidget {
_QuickLinkItem( _QuickLinkItem(
icon: UiIcons.nfc, icon: UiIcons.nfc,
title: labels.clock_in_hubs, title: labels.clock_in_hubs,
onTap: () {}, onTap: () => Modular.to.pushHubs(),
), ),
_QuickLinkItem( _QuickLinkItem(
icon: UiIcons.building, icon: UiIcons.building,