added local db
This commit is contained in:
@@ -7,7 +7,6 @@ import '../../../core/theme/app_dimens.dart';
|
||||
import '../../../core/theme/app_layout.dart';
|
||||
import '../../../core/theme/app_typography.dart';
|
||||
import '../../../core/utils/formatters.dart';
|
||||
import '../../../domain/entities/sync_event.dart';
|
||||
import '../../auth/providers/auth_controller.dart';
|
||||
import '../../sync/widgets/sign_out_dialog.dart';
|
||||
import '../providers/cart_controller.dart';
|
||||
@@ -232,10 +231,7 @@ class _Section extends ConsumerWidget {
|
||||
final active = ref.watch(activeModuleProvider);
|
||||
final cartCount = ref.watch(cartItemCountProvider);
|
||||
final ready = ref.watch(catalogueReadyProvider);
|
||||
final outstanding = ref
|
||||
.watch(syncEventsProvider)
|
||||
.where((e) => e.status != SyncStatus.synced)
|
||||
.length;
|
||||
final outstanding = ref.watch(unsyncedCountProvider).value ?? 0;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
@@ -82,61 +82,104 @@ class _Header extends ConsumerWidget {
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
AppSpacing.xl,
|
||||
AppSpacing.lg,
|
||||
AppSpacing.md,
|
||||
AppSpacing.lg,
|
||||
AppSpacing.sm,
|
||||
AppSpacing.md,
|
||||
),
|
||||
child: Row(children: [
|
||||
Text('Cart', style: context.text.headlineSmall),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
if (cart.isNotEmpty)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primarySurface,
|
||||
borderRadius: AppRadius.brPill,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
'${cart.lineCount}',
|
||||
style: const TextStyle(
|
||||
color: AppColors.primary,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 13,
|
||||
'Cart',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.text.titleLarge,
|
||||
),
|
||||
),
|
||||
if (cart.isNotEmpty) ...[
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primarySurface,
|
||||
borderRadius: AppRadius.brPill,
|
||||
),
|
||||
child: Text(
|
||||
'${cart.lineCount}',
|
||||
style: const TextStyle(
|
||||
color: AppColors.primary,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 12.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
if (controller.canUndo)
|
||||
IconButton(
|
||||
tooltip: 'Undo (F8)',
|
||||
onPressed: controller.undo,
|
||||
icon: const Icon(Icons.undo_rounded, size: 19),
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
if (cart.isNotEmpty) ...[
|
||||
TextButton.icon(
|
||||
onPressed: () async {
|
||||
await controller.park();
|
||||
ref.invalidate(parkedBillsProvider);
|
||||
if (context.mounted) context.showSnack('Bill parked');
|
||||
},
|
||||
icon: const Icon(Icons.pause_circle_outline_rounded, size: 17),
|
||||
label: const Text('Park'),
|
||||
style: TextButton.styleFrom(foregroundColor: AppColors.warning),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: controller.clear,
|
||||
style: TextButton.styleFrom(foregroundColor: AppColors.danger),
|
||||
child: const Text('Clear'),
|
||||
),
|
||||
],
|
||||
const Spacer(),
|
||||
// Icon-only actions: labelled buttons overflowed the 380px panel.
|
||||
if (controller.canUndo)
|
||||
_IconAction(
|
||||
icon: Icons.undo_rounded,
|
||||
tooltip: 'Undo (F8)',
|
||||
color: AppColors.textSecondary,
|
||||
onTap: controller.undo,
|
||||
),
|
||||
if (cart.isNotEmpty) ...[
|
||||
_IconAction(
|
||||
icon: Icons.pause_circle_outline_rounded,
|
||||
tooltip: 'Park bill',
|
||||
color: AppColors.warning,
|
||||
onTap: () async {
|
||||
await controller.park();
|
||||
ref.invalidate(parkedBillsProvider);
|
||||
if (context.mounted) context.showSnack('Bill parked');
|
||||
},
|
||||
),
|
||||
_IconAction(
|
||||
icon: Icons.delete_outline_rounded,
|
||||
tooltip: 'Clear bill',
|
||||
color: AppColors.danger,
|
||||
onTap: controller.clear,
|
||||
),
|
||||
],
|
||||
if (inSheet)
|
||||
_IconAction(
|
||||
icon: Icons.close_rounded,
|
||||
tooltip: 'Close',
|
||||
color: AppColors.textSecondary,
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
),
|
||||
],
|
||||
if (inSheet)
|
||||
IconButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
icon: const Icon(Icons.close_rounded),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Compact square action used in the bill header.
|
||||
class _IconAction extends StatelessWidget {
|
||||
const _IconAction({
|
||||
required this.icon,
|
||||
required this.tooltip,
|
||||
required this.color,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final IconData icon;
|
||||
final String tooltip;
|
||||
final Color color;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Tooltip(
|
||||
message: tooltip,
|
||||
child: IconButton(
|
||||
onPressed: onTap,
|
||||
icon: Icon(icon, size: 19),
|
||||
color: color,
|
||||
visualDensity: VisualDensity.compact,
|
||||
constraints: const BoxConstraints(minWidth: 36, minHeight: 36),
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -305,11 +348,16 @@ class _Row extends StatelessWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: AppSpacing.xs + 2),
|
||||
child: Row(children: [
|
||||
Text(label,
|
||||
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)',
|
||||
@@ -318,6 +366,7 @@ class _Row extends StatelessWidget {
|
||||
color: AppColors.textTertiary,
|
||||
)),
|
||||
],
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
const Spacer(),
|
||||
Text(
|
||||
value,
|
||||
|
||||
@@ -22,7 +22,11 @@ class CartFab extends ConsumerWidget {
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(AppSpacing.lg),
|
||||
child: Material(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.sizeOf(context).width - AppSpacing.xxxl,
|
||||
),
|
||||
child: Material(
|
||||
color: AppColors.primary,
|
||||
borderRadius: AppRadius.brLg,
|
||||
elevation: 0,
|
||||
@@ -57,15 +61,18 @@ class CartFab extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(width: AppSpacing.md),
|
||||
const Text(
|
||||
'View bill',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
const Flexible(
|
||||
child: Text(
|
||||
'View bill',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: AppSpacing.xl),
|
||||
const SizedBox(width: AppSpacing.lg),
|
||||
Text(
|
||||
Formatters.money(cart.grandTotal),
|
||||
style: AppTypography.money(19, color: Colors.white),
|
||||
@@ -77,6 +84,7 @@ class CartFab extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
).animate().fadeIn(duration: 180.ms).slideY(begin: 0.3, end: 0);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../core/router/app_router.dart';
|
||||
import '../../../core/theme/app_colors.dart';
|
||||
import '../../../core/theme/app_dimens.dart';
|
||||
import '../../../core/utils/extensions.dart';
|
||||
import '../../../core/utils/formatters.dart';
|
||||
import '../../../core/widgets/status_pill.dart';
|
||||
import '../../customer/widgets/customer_capture_sheet.dart';
|
||||
import '../providers/cart_controller.dart';
|
||||
|
||||
/// Strip above the product grid showing who the sale belongs to.
|
||||
@@ -85,7 +83,7 @@ class CustomerBar extends ConsumerWidget {
|
||||
),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () => context.push(AppRoutes.existingCustomer),
|
||||
onPressed: () => showCustomerCaptureSheet(context),
|
||||
icon: const Icon(Icons.sync_alt_rounded, size: 17),
|
||||
label: Text(customer == null ? 'Add Customer' : 'Change'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
|
||||
@@ -137,12 +137,12 @@ class _DiscountSheetState extends State<_DiscountSheet> {
|
||||
segments: const [
|
||||
ButtonSegment(
|
||||
value: DiscountType.percentage,
|
||||
label: Text('Percentage'),
|
||||
label: Text('Percent'),
|
||||
icon: Icon(Icons.percent_rounded, size: 17),
|
||||
),
|
||||
ButtonSegment(
|
||||
value: DiscountType.flat,
|
||||
label: Text('Flat amount'),
|
||||
label: Text('Flat'),
|
||||
icon: Icon(Icons.currency_rupee_rounded, size: 17),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../app/providers.dart';
|
||||
import '../../../core/router/app_router.dart';
|
||||
import '../../../core/theme/app_colors.dart';
|
||||
import '../../../core/theme/app_dimens.dart';
|
||||
import '../../../core/theme/app_layout.dart';
|
||||
@@ -29,6 +27,8 @@ class PageHeader extends ConsumerWidget {
|
||||
final module = ref.watch(activeModuleProvider);
|
||||
final now = ref.watch(clockProvider).value ?? DateTime.now();
|
||||
final compact = layout.sidebarIsDrawer;
|
||||
// Status chrome is the first thing dropped when width gets tight.
|
||||
final showStatus = MediaQuery.sizeOf(context).width >= 1180;
|
||||
|
||||
return Container(
|
||||
constraints: const BoxConstraints(minHeight: AppSizes.headerHeight),
|
||||
@@ -75,7 +75,7 @@ class PageHeader extends ConsumerWidget {
|
||||
|
||||
const Spacer(),
|
||||
|
||||
if (!compact) ...[
|
||||
if (showStatus) ...[
|
||||
const _LivePill(),
|
||||
const SizedBox(width: AppSpacing.lg),
|
||||
Text(
|
||||
@@ -250,7 +250,7 @@ class _ParkedBillsButton extends ConsumerWidget {
|
||||
builder: (_) => AlertDialog(
|
||||
title: const Text('Parked bills'),
|
||||
content: SizedBox(
|
||||
width: 380,
|
||||
width: (MediaQuery.sizeOf(context).width - 96).clamp(280.0, 380.0),
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: parked.length,
|
||||
@@ -297,7 +297,10 @@ class _NewSaleButton extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
void start() {
|
||||
ref.read(cartControllerProvider.notifier).reset();
|
||||
context.go(AppRoutes.welcome);
|
||||
ref.read(activeModuleProvider.notifier).state = PosModule.pos;
|
||||
ScaffoldMessenger.of(context)
|
||||
..hideCurrentSnackBar()
|
||||
..showSnackBar(const SnackBar(content: Text('Started a new sale.')));
|
||||
}
|
||||
|
||||
if (compact) {
|
||||
|
||||
@@ -88,10 +88,13 @@ class _ProductCardState extends State<ProductCard> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.xs + 2),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
// Scales down rather than overflowing on small tiles.
|
||||
FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
Formatters.money(p.price),
|
||||
style: AppTypography.money(17,
|
||||
@@ -112,8 +115,9 @@ class _ProductCardState extends State<ProductCard> {
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
Text(
|
||||
|
||||
Reference in New Issue
Block a user