feat: add alot
This commit is contained in:
@@ -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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user