feat: add rows count (broken), fix: bug with low stock items

This commit is contained in:
2026-08-02 23:23:28 +02:00
parent f5b2041612
commit 6060ca2713
19 changed files with 373 additions and 41 deletions
+75 -18
View File
@@ -12,8 +12,10 @@ import 'package:flutter_slidable/flutter_slidable.dart';
import '../models/bar_tab.dart';
import '../models/product.dart';
import '../models/tab_item.dart';
import '../services/product_service.dart';
import '../utils/app_updater.dart';
import '../viewmodels/bar_screen_view_model.dart';
import '../viewmodels/settings_view_model.dart';
import '../l10n/app_localizations.dart';
import '../l10n/app_localizations_helpers.dart';
@@ -38,6 +40,11 @@ class _BarScreenViewState extends State<BarScreenView> {
final l10n = AppLocalizations.of(context);
final viewModel = context.watch<BarScreenViewModel>();
final productsViewModel = context.watch<ProductListViewModel>();
final settings = context.watch<SettingsViewModel>().settings;
final stockByProductId = {
for (final product in productsViewModel.products)
product.id: product.stockQuantity,
};
return Scaffold(
appBar: AppBar(
@@ -118,6 +125,7 @@ class _BarScreenViewState extends State<BarScreenView> {
flex: 2,
child: _ProductGrid(
products: productsViewModel.products,
rows: settings.barGridRows,
hasSelectedTab: viewModel.selectedTab != null,
onProductTap: (product) async {
if (viewModel.selectedTab == null) {
@@ -130,10 +138,18 @@ class _BarScreenViewState extends State<BarScreenView> {
try {
await viewModel.addProductToSelectedTab(product);
} catch (e, stack) {
Sentry.captureException(e, stackTrace: stack);
if (e is! InsufficientStockException) {
Sentry.captureException(e, stackTrace: stack);
}
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(l10n.couldNotAddProductToTab)),
SnackBar(
content: Text(
e is InsufficientStockException
? l10n.productOutOfStock
: l10n.couldNotAddProductToTab,
),
),
);
}
},
@@ -146,6 +162,7 @@ class _BarScreenViewState extends State<BarScreenView> {
tabs: viewModel.tabs,
selectedTab: viewModel.selectedTab,
selectedTabId: viewModel.selectedTabId,
stockByProductId: stockByProductId,
onNewTabPressed: () => showNewTabDialog(context),
onTabSelected: viewModel.selectTab,
onItemQuantityChanged: viewModel.changeItemQuantity,
@@ -166,11 +183,13 @@ class _BarScreenViewState extends State<BarScreenView> {
// ---------------------------------------------------------------------------
class _ProductGrid extends StatelessWidget {
final List<Product> products;
final int rows;
final bool hasSelectedTab;
final ValueChanged<Product> onProductTap;
const _ProductGrid({
required this.products,
required this.rows,
required this.hasSelectedTab,
required this.onProductTap,
});
@@ -267,11 +286,26 @@ class _ProductGrid extends StatelessWidget {
Expanded(
child: LayoutBuilder(
builder: (context, constraints) {
final columns = ((constraints.maxWidth / 170).floor())
.clamp(3, 5)
.toInt();
const horizontalPadding = 32.0;
const verticalPadding = 28.0;
const spacing = 12.0;
final rowCount = rows.clamp(2, 6).toInt();
final rowHeight =
((constraints.maxHeight -
verticalPadding -
(rowCount - 1) * spacing) /
rowCount)
.clamp(96.0, 320.0)
.toDouble();
final columns =
(((constraints.maxWidth - horizontalPadding + spacing) /
(rowHeight + spacing))
.ceil())
.clamp(2, 8)
.toInt();
return GridView.builder(
key: ValueKey('product-grid-$rows'),
padding: const EdgeInsets.fromLTRB(16, 12, 16, 16),
itemCount: products.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
@@ -306,6 +340,7 @@ class _TabPanel extends StatefulWidget {
final List<BarTab> tabs;
final BarTab? selectedTab;
final String? selectedTabId;
final Map<String, int> stockByProductId;
final VoidCallback onNewTabPressed;
final ValueChanged<String> onTabSelected;
final Future<void> Function(TabItem item, int quantity) onItemQuantityChanged;
@@ -316,6 +351,7 @@ class _TabPanel extends StatefulWidget {
required this.tabs,
required this.selectedTab,
required this.selectedTabId,
required this.stockByProductId,
required this.onNewTabPressed,
required this.onTabSelected,
required this.onItemQuantityChanged,
@@ -452,6 +488,7 @@ class _TabPanelState extends State<_TabPanel> {
)
: _SelectedTabDetails(
tab: widget.selectedTab!,
stockByProductId: widget.stockByProductId,
onItemQuantityChanged: widget.onItemQuantityChanged,
onCloseTabPressed: widget.onCloseTabPressed,
),
@@ -596,11 +633,13 @@ class _OpenTabsList extends StatelessWidget {
class _SelectedTabDetails extends StatelessWidget {
final BarTab tab;
final Map<String, int> stockByProductId;
final Future<void> Function(TabItem item, int quantity) onItemQuantityChanged;
final VoidCallback onCloseTabPressed;
const _SelectedTabDetails({
required this.tab,
required this.stockByProductId,
required this.onItemQuantityChanged,
required this.onCloseTabPressed,
});
@@ -654,6 +693,7 @@ class _SelectedTabDetails extends StatelessWidget {
return _TabItemRow(
item: item,
stockByProductId: stockByProductId,
onQuantityChanged: onItemQuantityChanged,
);
},
@@ -706,14 +746,21 @@ class _SelectedTabDetails extends StatelessWidget {
class _TabItemRow extends StatelessWidget {
final TabItem item;
final Map<String, int> stockByProductId;
final Future<void> Function(TabItem item, int quantity) onQuantityChanged;
const _TabItemRow({required this.item, required this.onQuantityChanged});
const _TabItemRow({
required this.item,
required this.stockByProductId,
required this.onQuantityChanged,
});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final l10n = AppLocalizations.of(context);
final availableStock = stockByProductId[item.productId];
final canIncrease = availableStock == null || availableStock > 0;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
@@ -771,18 +818,28 @@ class _TabItemRow extends StatelessWidget {
),
IconButton(
visualDensity: VisualDensity.compact,
onPressed: () async {
try {
await onQuantityChanged(item, item.quantity + 1);
} catch (e, stack) {
Sentry.captureException(e, stackTrace: stack);
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(l10n.couldNotUpdateQuantity)),
);
}
}
},
onPressed: canIncrease
? () async {
try {
await onQuantityChanged(item, item.quantity + 1);
} catch (e, stack) {
if (e is! InsufficientStockException) {
Sentry.captureException(e, stackTrace: stack);
}
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
e is InsufficientStockException
? l10n.productOutOfStock
: l10n.couldNotUpdateQuantity,
),
),
);
}
}
}
: null,
icon: const Icon(Icons.add_rounded, size: 18),
),
],