feat: New Open Tab dialog with search and optional customer email. Drinks are now grouped per order. Payments trigger cha-ching and money rain.

This commit is contained in:
2026-10-28 02:11:32 +01:00
parent 1aee0541c4
commit 29ceb51177
41 changed files with 2771 additions and 812 deletions
+41 -4
View File
@@ -45,6 +45,7 @@ void main() {
WidgetTester tester, {
bool showSearch = false,
BarTab? selectedTab,
List<BarTab>? panelTabs,
}) async {
tester.view.physicalSize = const Size(500, 800);
tester.view.devicePixelRatio = 1;
@@ -56,12 +57,13 @@ void main() {
supportedLocales: AppLocalizations.supportedLocales,
home: Scaffold(
body: TabPanel(
tabs: tabs,
tabs: panelTabs ?? tabs,
selectedTab: selectedTab,
selectedTabId: selectedTab?.id,
showSearch: showSearch,
onTabSelected: (_) {},
onItemQuantityChanged: (_, _) async {},
onTabDeselected: () {},
onItemQuantityChanged: (_, _, _) async {},
onCloseTabPressed: () {},
onTabEdited: (_) async {},
onTabDeleted: (_) async {},
@@ -92,7 +94,7 @@ void main() {
await tester.tap(
find.descendant(
of: find.byWidgetPredicate(
(widget) => widget.runtimeType.toString() == '_LetterIndexBar',
(widget) => widget.runtimeType.toString() == 'LetterIndexBar',
),
matching: find.text(letter),
),
@@ -190,6 +192,40 @@ void main() {
);
});
for (final (description, name, count) in [
('the first name', 'anna', null),
('a name in a short list', 'Bram', 3),
]) {
testWidgets('lines up $description on top of its items', (
tester,
) async {
final panelTabs = count == null
? tabs
: (tabs.toList()..sort(
(a, b) => compareByIndex(a.customerName, b.customerName),
))
.take(count)
.toList();
final tab = panelTabs.firstWhere((tab) => tab.customerName == name);
// Selected from outside, as the Open Tab dialog does
await pumpPanel(tester, panelTabs: panelTabs);
await pumpPanel(tester, panelTabs: panelTabs, selectedTab: tab);
await tester.pumpAndSettle();
final list = tester.getRect(find.byType(OpenTabsList));
final details = tester.getRect(find.byType(SelectedTabDetails));
final tile = tester.getRect(
find
.ancestor(of: find.text(name), matching: find.byType(ClipRRect))
.first,
);
expect(tile.bottom, closeTo(list.bottom, 1));
expect(details.top - tile.bottom, inInclusiveRange(0, 20));
});
}
testWidgets('browsing the names shows the divider again', (tester) async {
final marc = tabs.firstWhere((tab) => tab.customerName == 'Marc');
await pumpPanel(tester, selectedTab: marc);
@@ -221,7 +257,8 @@ void main() {
});
testWidgets('a letter without names goes to the next one', (tester) async {
await pumpPanel(tester);
// A selected tab keeps the list short enough to scroll Rik to the top
await pumpPanel(tester, selectedTab: tabs.first);
await tapLetter(tester, 'Q');
expect(firstVisibleName(tester), 'Rik');