feat: add alot

This commit is contained in:
2026-10-08 04:12:36 +02:00
parent ccf5165417
commit fc607cb7bd
99 changed files with 4981 additions and 3655 deletions
+225
View File
@@ -0,0 +1,225 @@
import 'package:flutter/material.dart';
import '../../l10n/app_localizations.dart';
import '../../models/bar_tab.dart';
import '../../models/tab_item.dart';
import '../../services/product_service.dart';
import '../../utils/money.dart';
class SelectedTabDetails extends StatelessWidget {
final BarTab tab;
final Map<String, int> stockByProductId;
final Future<void> Function(TabItem item, int delta) onItemQuantityChanged;
final VoidCallback onCloseTabPressed;
const SelectedTabDetails({
super.key,
required this.tab,
required this.stockByProductId,
required this.onItemQuantityChanged,
required this.onCloseTabPressed,
});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final l10n = AppLocalizations.of(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
tab.customerName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.titleLarge,
),
),
Divider(),
],
),
const SizedBox(height: 16),
Expanded(
child: tab.items.isEmpty
? Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.local_bar_outlined,
size: 32,
color: scheme.onSurface.withValues(alpha: 0.25),
),
const SizedBox(height: 8),
Text(
l10n.tapProductsToAdd,
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
)
: ListView.separated(
itemCount: tab.items.length,
separatorBuilder: (_, _) => const Divider(height: 1),
itemBuilder: (context, index) {
final item = tab.items[index];
return TabItemRow(
item: item,
stockByProductId: stockByProductId,
onQuantityChanged: onItemQuantityChanged,
);
},
),
),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
color: Theme.of(
context,
).colorScheme.primary.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: Theme.of(
context,
).colorScheme.primary.withValues(alpha: 0.3),
),
),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
l10n.total,
style: Theme.of(context).textTheme.bodySmall,
),
Text(
tab.totalInCents.asEuro,
style: Theme.of(
context,
).textTheme.headlineMedium?.copyWith(fontSize: 24),
),
],
),
),
FilledButton(
onPressed: tab.items.isEmpty ? null : onCloseTabPressed,
child: Text(l10n.closeTab),
),
],
),
),
],
);
}
}
class TabItemRow extends StatelessWidget {
final TabItem item;
final Map<String, int> stockByProductId;
final Future<void> Function(TabItem item, int delta) onQuantityChanged;
const TabItemRow({
super.key,
required this.item,
required this.stockByProductId,
required this.onQuantityChanged,
});
Future<void> _changeQuantity(BuildContext context, int delta) async {
final l10n = AppLocalizations.of(context);
try {
await onQuantityChanged(item, delta);
} catch (e) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
e is InsufficientStockException
? l10n.productOutOfStock
: l10n.couldNotUpdateQuantity,
),
),
);
}
}
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final availableStock = stockByProductId[item.productId];
final canIncrease = availableStock == null || availableStock > 0;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.productName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w600),
),
const SizedBox(height: 2),
Text(
'${item.quantity} × ${item.unitPriceInCents.asEuro}',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
Container(
decoration: BoxDecoration(
color: scheme.onSurface.withValues(alpha: 0.05),
borderRadius: BorderRadius.circular(999),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
visualDensity: VisualDensity.compact,
onPressed: () => _changeQuantity(context, -1),
icon: const Icon(Icons.remove_rounded, size: 18),
),
SizedBox(
width: 22,
child: Text(
'${item.quantity}',
textAlign: TextAlign.center,
style: const TextStyle(fontWeight: FontWeight.w700),
),
),
IconButton(
visualDensity: VisualDensity.compact,
onPressed: canIncrease
? () => _changeQuantity(context, 1)
: null,
icon: const Icon(Icons.add_rounded, size: 18),
),
],
),
),
SizedBox(
width: 72,
child: Text(
item.lineTotalInCents.asEuro,
textAlign: TextAlign.end,
style: const TextStyle(fontWeight: FontWeight.w700),
),
),
],
),
);
}
}