
Introduces dedicated pages and routes for Delivery and Inventory operations. Refactors the application drawer to include navigation links for these new operations, ensuring proper route transitions and closing the drawer on tap. Enhances the drawer's active state styling for list and expansion tiles, providing better visual feedback. Extracts common app bar logic into a reusable `MainAppbarComponent` and integrates it into operation pages, starting with Reception, to standardize the application's header. Standardizes routing paths for operation pages and ensures consistent page transitions across them.
304 lines
9.6 KiB
Dart
304 lines
9.6 KiB
Dart
import 'package:barcode_scanner/backend/schema/user/user_struct.dart';
|
|
import 'package:barcode_scanner/pages/login/login_page_model.dart';
|
|
import 'package:barcode_scanner/pages/operation/reception/reception_page_model.dart';
|
|
import 'package:barcode_scanner/router/go_router_builder.dart';
|
|
import 'package:barcode_scanner/router/go_secure_router_builder.dart';
|
|
import 'package:barcode_scanner/themes/app_theme.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
class DrawerComponent extends ConsumerStatefulWidget {
|
|
const DrawerComponent({
|
|
super.key,
|
|
this.isOperationExpanded = true,
|
|
this.isSettingsExpanded = false,
|
|
this.selectedState = const SelectedDrawerState(
|
|
menuIndex: 0,
|
|
subMenuIndex: 0,
|
|
),
|
|
});
|
|
|
|
final bool isOperationExpanded;
|
|
final bool isSettingsExpanded;
|
|
final SelectedDrawerState selectedState;
|
|
|
|
@override
|
|
ConsumerState<DrawerComponent> createState() => _DrawerComponentState();
|
|
}
|
|
|
|
class _DrawerComponentState extends ConsumerState<DrawerComponent> {
|
|
late bool _isOperationExpanded;
|
|
late bool _isSettingsExpanded;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_isOperationExpanded = widget.isOperationExpanded;
|
|
_isSettingsExpanded = widget.isSettingsExpanded;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final selected = widget.selectedState;
|
|
|
|
return Drawer(
|
|
backgroundColor: AppTheme.of(context).primaryBackground,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
|
|
child: Column(
|
|
children: [
|
|
const SizedBox(height: 50),
|
|
|
|
// === Profil utilisateur
|
|
Consumer(
|
|
builder: (context, ref, child) {
|
|
final state = ref.watch(receptionPageModelProvider);
|
|
return ListTile(
|
|
onTap: () => ProfileRoute().push(context),
|
|
contentPadding: EdgeInsets.zero,
|
|
leading: Container(
|
|
width: 60,
|
|
height: 60,
|
|
decoration: BoxDecoration(
|
|
color: AppTheme.of(context).secondaryBackground,
|
|
borderRadius: BorderRadius.circular(60),
|
|
),
|
|
),
|
|
title: Text(
|
|
state.user?.fullName ?? '',
|
|
style: AppTheme.of(context).titleMedium,
|
|
),
|
|
subtitle: Text(
|
|
state.user?.email ?? '',
|
|
style: AppTheme.of(context).bodyMedium,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
|
|
const Divider(),
|
|
|
|
// === Opérations
|
|
_ExpansionTileComponent(
|
|
leadingIcon: Icons.inbox,
|
|
isExpanded: _isOperationExpanded,
|
|
onExpansionChanged: (expanded) =>
|
|
setState(() => _isOperationExpanded = expanded),
|
|
title: 'Opérations',
|
|
isActive: selected.isSelected(menu: 0),
|
|
children: [
|
|
_ListTile(
|
|
onTap: () {
|
|
Navigator.of(context).pop();
|
|
ReceptionRoute().go(context);
|
|
},
|
|
title: 'Réceptions',
|
|
isActive: selected.isSelected(menu: 0, sub: 0),
|
|
leadingIcon: Icons.move_to_inbox,
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 40),
|
|
),
|
|
_ListTile(
|
|
onTap: () {
|
|
Navigator.of(context).pop();
|
|
DeliveryRoute().go(context);
|
|
},
|
|
title: 'Livraisons',
|
|
isActive: selected.isSelected(menu: 0, sub: 1),
|
|
leadingIcon: Icons.local_shipping,
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 40),
|
|
),
|
|
_ListTile(
|
|
onTap: () {
|
|
Navigator.of(context).pop();
|
|
InventoryRoute().go(context);
|
|
},
|
|
title: 'Inventaires',
|
|
isActive: selected.isSelected(menu: 0, sub: 2),
|
|
leadingIcon: Icons.inventory,
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 40),
|
|
),
|
|
],
|
|
),
|
|
|
|
// === Produits
|
|
_ListTile(
|
|
title: "Produits",
|
|
leadingIcon: Icons.insert_chart,
|
|
trailing: const Icon(Icons.keyboard_arrow_right),
|
|
isActive: selected.isSelected(menu: 1),
|
|
contentPadding: const EdgeInsetsDirectional.symmetric(
|
|
horizontal: 10,
|
|
),
|
|
),
|
|
|
|
// === Historique
|
|
_ListTile(
|
|
title: "Historiques",
|
|
leadingIcon: Icons.history,
|
|
trailing: const Icon(Icons.keyboard_arrow_right),
|
|
isActive: selected.isSelected(menu: 2),
|
|
contentPadding: const EdgeInsetsDirectional.symmetric(
|
|
horizontal: 10,
|
|
),
|
|
),
|
|
|
|
// === Paramètres
|
|
_ExpansionTileComponent(
|
|
title: "Paramètres",
|
|
isExpanded: _isSettingsExpanded,
|
|
onExpansionChanged: (expanded) =>
|
|
setState(() => _isSettingsExpanded = expanded),
|
|
isActive: selected.isSelected(menu: 3),
|
|
leadingIcon: Icons.settings,
|
|
children: [
|
|
_ListTile(
|
|
title: 'Connexion Odoo',
|
|
isActive: selected.isSelected(menu: 3, sub: 3),
|
|
leadingIcon: Icons.lock_open,
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 40),
|
|
),
|
|
_ListTile(
|
|
title: 'Gestion des utilisateurs',
|
|
isActive: selected.isSelected(menu: 3, sub: 4),
|
|
leadingIcon: Icons.group,
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 40),
|
|
),
|
|
],
|
|
),
|
|
|
|
const Spacer(),
|
|
|
|
// === Déconnexion
|
|
SafeArea(
|
|
child: ListTile(
|
|
onTap: () async {
|
|
await ref.read(loginPageModelProvider.notifier).logOut();
|
|
await UserStruct(id: '1').deleteLocalStorage();
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
SplashRoute().go(context);
|
|
});
|
|
},
|
|
leading: const Icon(Icons.logout),
|
|
title: Text(
|
|
'Se déconnecter',
|
|
style: AppTheme.of(context).bodyLarge,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class SelectedDrawerState {
|
|
const SelectedDrawerState({
|
|
required this.menuIndex,
|
|
required this.subMenuIndex,
|
|
});
|
|
final int menuIndex;
|
|
final int subMenuIndex;
|
|
|
|
bool isSelected({required int menu, int? sub}) {
|
|
if (sub == null) return menuIndex == menu;
|
|
return menuIndex == menu && subMenuIndex == sub;
|
|
}
|
|
}
|
|
|
|
class _ListTile extends StatelessWidget {
|
|
const _ListTile({
|
|
Key? key,
|
|
required this.title,
|
|
this.isActive = false,
|
|
this.onTap,
|
|
this.leadingIcon,
|
|
this.contentPadding,
|
|
this.trailing,
|
|
}) : super(key: key);
|
|
final bool isActive;
|
|
final String title;
|
|
final VoidCallback? onTap;
|
|
final IconData? leadingIcon;
|
|
final EdgeInsetsGeometry? contentPadding;
|
|
final Widget? trailing;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ListTile(
|
|
selected: isActive,
|
|
selectedTileColor: AppTheme.of(context).primary.withValues(alpha: 0.1),
|
|
onTap: onTap,
|
|
contentPadding: contentPadding,
|
|
leading: Icon(
|
|
leadingIcon,
|
|
color: isActive
|
|
? AppTheme.of(context).primary
|
|
: AppTheme.of(context).primaryText,
|
|
),
|
|
title: Text(
|
|
title,
|
|
style: AppTheme.of(context).bodyLarge.copyWith(
|
|
color: isActive ? AppTheme.of(context).primary : null,
|
|
),
|
|
),
|
|
trailing: trailing,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ExpansionTileComponent extends StatelessWidget {
|
|
const _ExpansionTileComponent({
|
|
required this.title,
|
|
this.isExpanded = false,
|
|
this.onExpansionChanged,
|
|
this.isActive = false,
|
|
this.children = const <Widget>[],
|
|
this.leadingIcon,
|
|
});
|
|
final bool isExpanded;
|
|
final void Function(bool)? onExpansionChanged;
|
|
final bool isActive;
|
|
final List<Widget> children;
|
|
final String title;
|
|
final IconData? leadingIcon;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
color: isActive
|
|
? AppTheme.of(context).primary.withValues(alpha: 0.05)
|
|
: null,
|
|
child: ExpansionTile(
|
|
initiallyExpanded: isExpanded,
|
|
onExpansionChanged: onExpansionChanged,
|
|
tilePadding: const EdgeInsetsDirectional.symmetric(horizontal: 10),
|
|
shape: LinearBorder.none,
|
|
title: Row(
|
|
children: [
|
|
Icon(
|
|
leadingIcon,
|
|
color: isActive
|
|
? AppTheme.of(context).primary
|
|
: AppTheme.of(context).primaryText,
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Text(
|
|
title,
|
|
style: AppTheme.of(context).bodyLarge.copyWith(
|
|
color: isActive ? AppTheme.of(context).primary : null,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
trailing: Icon(
|
|
isExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_right,
|
|
),
|
|
children: children,
|
|
),
|
|
);
|
|
}
|
|
}
|