80 lines
2.8 KiB
Dart
80 lines
2.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../../auth/providers/auth_controller.dart';
|
|
|
|
/// The modules a cashier needs. Deliberately excludes analytics — this
|
|
/// terminal is for billing, not back-office reporting.
|
|
enum PosModule {
|
|
pos('Point of Sale', 'POS', Icons.point_of_sale_rounded, NavSection.billing),
|
|
customers('Customers', 'Customers', Icons.people_alt_rounded,
|
|
NavSection.billing,),
|
|
|
|
productImport('Product Import', 'Product Import',
|
|
Icons.cloud_download_rounded, NavSection.catalogue,),
|
|
promos('Promotions', 'Promo', Icons.sell_rounded, NavSection.catalogue),
|
|
|
|
events('Events', 'Events', Icons.sync_rounded, NavSection.session),
|
|
settings('Settings', 'Settings', Icons.settings_rounded, NavSection.session);
|
|
|
|
const PosModule(this.title, this.label, this.icon, this.section);
|
|
|
|
/// Long form, shown in the page header.
|
|
final String title;
|
|
|
|
/// Short form, shown in the sidebar.
|
|
final String label;
|
|
|
|
final IconData icon;
|
|
final NavSection section;
|
|
}
|
|
|
|
/// Groups the navigation into labelled blocks.
|
|
enum NavSection {
|
|
billing('Billing'),
|
|
catalogue('Catalogue'),
|
|
session('Session');
|
|
|
|
const NavSection(this.label);
|
|
|
|
final String label;
|
|
|
|
List<PosModule> get modules =>
|
|
PosModule.values.where((m) => m.section == this).toList();
|
|
}
|
|
|
|
/// What a cashier session may open.
|
|
///
|
|
/// Billing, and nothing else — not the catalogue, the promos, the sync log or
|
|
/// the terminal's configuration. The sidebar is hidden in cashier mode anyway,
|
|
/// so this is the belt to that braces: a module reached some other way (a scan
|
|
/// handler, a deep link, a stale value left in [activeModuleProvider] from the
|
|
/// admin's session) still cannot render.
|
|
const cashierModules = <PosModule>[PosModule.pos];
|
|
|
|
final activeModuleProvider = StateProvider<PosModule>((ref) => PosModule.pos);
|
|
|
|
/// The modules the current session is allowed to reach.
|
|
final visibleModulesProvider = Provider<List<PosModule>>((ref) {
|
|
return ref.watch(isCashierModeProvider) ? cashierModules : PosModule.values;
|
|
});
|
|
|
|
/// [activeModuleProvider], clamped to what this session may open.
|
|
///
|
|
/// Read this rather than the raw value anywhere a module decides what gets
|
|
/// built. An admin who leaves the shell on Settings and hands the till to a
|
|
/// cashier would otherwise reopen it on Settings.
|
|
final resolvedModuleProvider = Provider<PosModule>((ref) {
|
|
final active = ref.watch(activeModuleProvider);
|
|
final visible = ref.watch(visibleModulesProvider);
|
|
return visible.contains(active) ? active : PosModule.pos;
|
|
});
|
|
|
|
/// Sections that still have at least one module this session may open.
|
|
final visibleSectionsProvider = Provider<List<NavSection>>((ref) {
|
|
final visible = ref.watch(visibleModulesProvider);
|
|
return NavSection.values
|
|
.where((s) => s.modules.any(visible.contains))
|
|
.toList();
|
|
});
|