This commit is contained in:
2026-08-07 15:31:20 +05:30
parent 09e5e29df2
commit ad44402232
17 changed files with 598 additions and 1187 deletions

View File

@@ -183,6 +183,13 @@ class _PosDashboardScreenState extends ConsumerState<PosDashboardScreen> {
Expanded(
child: AnimatedSwitcher(
duration: AppMotion.fast,
// Top, not the default centre. The switcher stacks its
// children with loose constraints, so a module page
// shorter than the viewport — Promotions with two
// campaigns, Product Import before anything is pulled —
// sized itself to its content and then floated in the
// middle of the screen with dead space above it.
child: KeyedSubtree(
key: ValueKey(module),
child: _body(module, layout),

View File

@@ -78,36 +78,13 @@ class AppSidebar extends ConsumerWidget {
}
}
/// Store name beside the mark.
///
/// Used to read "Nearle / POS", which told the person standing at the till
/// nothing they did not already know — they can see which app is open. What is
/// worth the space is which shop and which branch this terminal is signed into,
/// because one person works several and a bill rung against the wrong outlet is
/// found the next morning. Both come from the sign-in response.
class _Brand extends ConsumerWidget {
class _Brand extends StatelessWidget {
const _Brand({required this.expanded});
final bool expanded;
@override
Widget build(BuildContext context, WidgetRef ref) {
final store = ref.watch(storeDisplayNameProvider);
final location = ref.watch(locationDisplayNameProvider);
// Before the back office has answered, fall back to whatever the store
// record on this terminal says rather than to the product's own name.
final title = store.isNotEmpty
? store
: (ref.watch(currentStoreProvider)?.name ?? 'Store');
// A single-outlet shop usually names the tenant and the branch the same
// thing; repeating it twice would look like a bug.
final subtitle =
(location.isEmpty || location.toLowerCase() == title.toLowerCase())
? 'POS'
: location;
Widget build(BuildContext context) {
return Container(
height: AppSizes.headerHeight,
padding: EdgeInsets.symmetric(
@@ -125,11 +102,9 @@ class _Brand extends ConsumerWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
const Text(
'Nearle',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w700,
letterSpacing: -0.3,
@@ -138,9 +113,7 @@ class _Brand extends ConsumerWidget {
),
),
Text(
subtitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
'POS',
style: AppTypography.sectionLabel()
.copyWith(color: AppColors.primary),
),

View File

@@ -352,41 +352,47 @@ class _Row extends StatelessWidget {
return Padding(
padding: const EdgeInsets.symmetric(vertical: AppSpacing.xs + 2),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
// Start, not space-between. With space-between *and* a Spacer, the
// free width was shared out between every child — which pushed the
// slab hint away from the label it belongs to, so "GST" and "(5%, 12%)"
// read as two unrelated columns. The Spacer alone puts all the slack in
// one place, between the label group and the amount.
children: [
Flexible(
child: Text(
label,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 14,
color: AppColors.textSecondary,
Flexible(
child: Text(
label,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 14,
color: AppColors.textSecondary,
),
),
),
),
if (hint != null) ...[
const SizedBox(width: AppSpacing.xs),
Text('($hint)',
if (hint != null) ...[
const SizedBox(width: AppSpacing.xs),
Text(
'($hint)',
style: const TextStyle(
fontSize: 11.5,
color: AppColors.textTertiary,
),),
],
const SizedBox(width: AppSpacing.sm),
const Spacer(),
Text(
value,
style: AppTypography.money(
14.5,
weight: FontWeight.w600,
color: valueColor ?? AppColors.textPrimary,
),
),
],
const Spacer(),
Text(
value,
style: AppTypography.money(
14.5,
weight: FontWeight.w600,
color: valueColor ?? AppColors.textPrimary,
),
),
),
if (trailingIcon != null) ...[
Icon(trailingIcon, size: 14, color: AppColors.textTertiary),
if (trailingIcon != null) ...[
const SizedBox(width: AppSpacing.xs),
Icon(trailingIcon, size: 14, color: AppColors.textTertiary),
],
],
],),
),
);
}
}

View File

@@ -404,15 +404,8 @@ class _CashierBrand extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final store = ref.watch(currentStoreProvider);
final user = ref.watch(currentUserProvider);
final store = ref.watch(storeDisplayNameProvider);
// Which shop, not which product. See `_Brand` in the sidebar — a cashier
// knows what app is open; what they cannot see is which outlet this till
// is signed into.
final title = store.isNotEmpty
? store
: (ref.watch(currentStoreProvider)?.name ?? 'Store');
return Row(
mainAxisSize: MainAxisSize.min,
@@ -426,8 +419,7 @@ class _CashierBrand extends ConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
maxLines: 1,
store?.name ?? 'Nearle POS',
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 14,