diff --git a/lib/views/bar_screen_view.dart b/lib/views/bar_screen_view.dart index 750b56f..fee7847 100644 --- a/lib/views/bar_screen_view.dart +++ b/lib/views/bar_screen_view.dart @@ -71,8 +71,8 @@ class _BarScreenViewState extends State { }, icon: const Icon(Icons.logout), ), - const SizedBox(width: 8), ], + actionsPadding: const EdgeInsets.symmetric(horizontal: 8), ), resizeToAvoidBottomInset: false, body: Builder( diff --git a/lib/views/history_screen_view.dart b/lib/views/history_screen_view.dart index dbe3f89..0133ebd 100644 --- a/lib/views/history_screen_view.dart +++ b/lib/views/history_screen_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; +import 'package:kooltab2/views/widgets/closed_tab_card.dart'; import 'package:provider/provider.dart'; import '../app/router.dart'; @@ -21,7 +22,7 @@ class _HistoryScreenViewState extends State with RouteAware { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { - context.read().ensureLoaded(); + context.read().load(); }); } @@ -58,6 +59,7 @@ class _HistoryScreenViewState extends State with RouteAware { const Text('Tab History'), ], ), + actionsPadding: const EdgeInsets.symmetric(horizontal: 8), actions: [ IconButton( tooltip: 'Refresh', @@ -122,7 +124,7 @@ class _HistoryScreenViewState extends State with RouteAware { itemBuilder: (context, index) { final closedTab = viewModel.closedTabs[index]; - return _ClosedTabCard(closedTab: closedTab); + return ClosedTabCard(closedTab: closedTab); }, ), ), @@ -170,157 +172,3 @@ class _EmptyState extends StatelessWidget { ); } } - -class _ClosedTabCard extends StatefulWidget { - final ClosedTab closedTab; - - const _ClosedTabCard({required this.closedTab}); - - @override - State<_ClosedTabCard> createState() => _ClosedTabCardState(); -} - -class _ClosedTabCardState extends State<_ClosedTabCard> { - bool _expanded = false; - - @override - Widget build(BuildContext context) { - final closedTab = widget.closedTab; - final dateFormat = DateFormat('MMM d, y · h:mm a'); - final scheme = Theme.of(context).colorScheme; - - return Container( - decoration: BoxDecoration( - color: scheme.onSurface.withValues(alpha: 0.04), - borderRadius: BorderRadius.circular(14), - border: Border.all(color: scheme.onSurface.withValues(alpha: 0.06)), - ), - clipBehavior: Clip.antiAlias, - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: () => setState(() => _expanded = !_expanded), - enableFeedback: true, - splashFactory: NoSplash.splashFactory, - - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 14, - ), - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - closedTab.customerName, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w800, - color: scheme.onSurface, - ), - ), - const SizedBox(height: 2), - Text( - '${dateFormat.format(closedTab.closedAt)} · ${closedTab.itemCount} items', - style: Theme.of(context).textTheme.bodySmall, - ), - ], - ), - ), - const SizedBox(width: 8), - Text( - closedTab.formattedTotal, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w800, - color: scheme.onSurface, - ), - ), - const SizedBox(width: 4), - Icon( - _expanded - ? Icons.expand_less_rounded - : Icons.expand_more_rounded, - color: scheme.onSurface.withValues(alpha: 0.5), - ), - ], - ), - ), - AnimatedCrossFade( - duration: const Duration(milliseconds: 150), - crossFadeState: _expanded - ? CrossFadeState.showFirst - : CrossFadeState.showSecond, - firstChild: Padding( - padding: const EdgeInsets.fromLTRB(16, 0, 16, 14), - child: Column( - children: [ - const Divider(height: 1), - const SizedBox(height: 8), - ...closedTab.items.map( - (item) => _ClosedTabItemRow(item: item), - ), - ], - ), - ), - secondChild: const SizedBox(width: double.infinity), - ), - ], - ), - ), - ), - ); - } -} - -class _ClosedTabItemRow extends StatelessWidget { - final ClosedTabItem item; - - const _ClosedTabItemRow({required this.item}); - - @override - Widget build(BuildContext context) { - final unitPrice = - NumberFormat.simpleCurrency().format(item.unitPriceInCents / 100); - final lineTotal = - NumberFormat.simpleCurrency().format(item.lineTotalInCents / 100); - final scheme = Theme.of(context).colorScheme; - - return Padding( - padding: const EdgeInsets.symmetric(vertical: 6), - child: Row( - children: [ - Expanded( - child: Text( - item.productName, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle(fontWeight: FontWeight.w600, color: scheme.onSurface), - ), - ), - Text( - '${item.quantity} × $unitPrice', - style: Theme.of(context).textTheme.bodySmall, - ), - const SizedBox(width: 12), - SizedBox( - width: 64, - child: Text( - lineTotal, - textAlign: TextAlign.end, - style: TextStyle(fontWeight: FontWeight.w700, color: scheme.onSurface), - ), - ), - ], - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/product_list_view.dart b/lib/views/product_list_view.dart index c177318..43aa664 100644 --- a/lib/views/product_list_view.dart +++ b/lib/views/product_list_view.dart @@ -14,7 +14,6 @@ class ProductListView extends StatefulWidget { } class _ProductListViewState extends State { - @override void initState() { super.initState(); @@ -30,14 +29,16 @@ class _ProductListViewState extends State { return Scaffold( appBar: AppBar( - title: const Text('Products'), - actions: [ - IconButton( - tooltip: 'Back to bar', - onPressed: () => context.go('/bar'), - icon: const Icon(Icons.point_of_sale), - ), - ], + title: Row( + children: [ + IconButton( + onPressed: () => context.go('/bar'), + icon: const Icon(Icons.arrow_back), + ), + const SizedBox(width: 8), + const Text('Products'), + ], + ), ), resizeToAvoidBottomInset: false, floatingActionButton: FloatingActionButton.extended( @@ -48,21 +49,15 @@ class _ProductListViewState extends State { body: Builder( builder: (context) { if (viewModel.isLoading) { - return const Center( - child: CircularProgressIndicator(), - ); + return const Center(child: CircularProgressIndicator()); } if (viewModel.errorMessage != null) { - return Center( - child: Text(viewModel.errorMessage!), - ); + return Center(child: Text(viewModel.errorMessage!)); } if (viewModel.products.isEmpty) { - return const Center( - child: Text('No products yet.'), - ); + return const Center(child: Text('No products yet.')); } return ListView.separated( @@ -86,8 +81,7 @@ class _ProductListViewState extends State { ), title: Text(product.name), subtitle: Text( - '${product.category} • ${product - .formattedPrice} • Stock: ${product.stockQuantity}', + '${product.category} • ${product.formattedPrice} • Stock: ${product.stockQuantity}', ), trailing: const Icon(Icons.chevron_right), onTap: () => context.go('/products/${product.id}/edit'), @@ -99,4 +93,4 @@ class _ProductListViewState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/views/settings_view.dart b/lib/views/settings_view.dart index 5d802fa..ba8d790 100644 --- a/lib/views/settings_view.dart +++ b/lib/views/settings_view.dart @@ -50,6 +50,7 @@ class _SettingsScreenViewState extends State { child: const Text('Confirm'), ), ], + actionsPadding: const EdgeInsets.symmetric(horizontal: 8), ), ); } diff --git a/lib/views/widgets/closed_tab_card.dart b/lib/views/widgets/closed_tab_card.dart new file mode 100644 index 0000000..4c61e96 --- /dev/null +++ b/lib/views/widgets/closed_tab_card.dart @@ -0,0 +1,158 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:kooltab2/models/closed_tab.dart'; +import 'package:kooltab2/models/closed_tab_item.dart'; + +class ClosedTabCard extends StatefulWidget { + final ClosedTab closedTab; + + const ClosedTabCard({required this.closedTab}); + + @override + State createState() => _ClosedTabCardState(); +} + +class _ClosedTabCardState extends State { + bool _expanded = false; + + @override + Widget build(BuildContext context) { + final closedTab = widget.closedTab; + final dateFormat = DateFormat('MMM d, y · h:mm a'); + final scheme = Theme.of(context).colorScheme; + + return Container( + decoration: BoxDecoration( + color: scheme.onSurface.withValues(alpha: 0.04), + borderRadius: BorderRadius.circular(14), + border: Border.all(color: scheme.onSurface.withValues(alpha: 0.06)), + ), + clipBehavior: Clip.antiAlias, + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () => setState(() => _expanded = !_expanded), + enableFeedback: true, + splashFactory: NoSplash.splashFactory, + + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 14, + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + closedTab.customerName, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w800, + color: scheme.onSurface, + ), + ), + const SizedBox(height: 2), + Text( + '${dateFormat.format(closedTab.closedAt)} · ${closedTab.itemCount} items', + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ), + ), + const SizedBox(width: 8), + Text( + closedTab.formattedTotal, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w800, + color: scheme.onSurface, + ), + ), + const SizedBox(width: 4), + Icon( + _expanded + ? Icons.expand_less_rounded + : Icons.expand_more_rounded, + color: scheme.onSurface.withValues(alpha: 0.5), + ), + ], + ), + ), + AnimatedCrossFade( + duration: const Duration(milliseconds: 150), + crossFadeState: _expanded + ? CrossFadeState.showFirst + : CrossFadeState.showSecond, + firstChild: Padding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 14), + child: Column( + children: [ + const Divider(height: 1), + const SizedBox(height: 8), + ...closedTab.items.map( + (item) => _ClosedTabItemRow(item: item), + ), + ], + ), + ), + secondChild: const SizedBox(width: double.infinity), + ), + ], + ), + ), + ), + ); + } +} + +class _ClosedTabItemRow extends StatelessWidget { + final ClosedTabItem item; + + const _ClosedTabItemRow({required this.item}); + + @override + Widget build(BuildContext context) { + final unitPrice = + NumberFormat.simpleCurrency().format(item.unitPriceInCents / 100); + final lineTotal = + NumberFormat.simpleCurrency().format(item.lineTotalInCents / 100); + final scheme = Theme.of(context).colorScheme; + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 6), + child: Row( + children: [ + Expanded( + child: Text( + item.productName, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontWeight: FontWeight.w600, color: scheme.onSurface), + ), + ), + Text( + '${item.quantity} × $unitPrice', + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(width: 12), + SizedBox( + width: 64, + child: Text( + lineTotal, + textAlign: TextAlign.end, + style: TextStyle(fontWeight: FontWeight.w700, color: scheme.onSurface), + ), + ), + ], + ), + ); + } +} \ No newline at end of file