srs/lib/features/shops/tickets_screen.dart
Aswin Biju 749d18192a
All checks were successful
deploy / deploy (push) Successful in 4m58s
refactor: updated UI and other trivial fixes
2026-11-05 10:13:32 +05:30

196 lines
5.5 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../core/format.dart';
import '../../core/models.dart';
import '../../core/providers.dart';
import '../../core/theme.dart';
import '../../widgets/ui.dart';
import '../orders/order_card.dart';
class TicketsScreen extends ConsumerStatefulWidget {
const TicketsScreen({super.key, required this.shopId});
final int shopId;
@override
ConsumerState<TicketsScreen> createState() => _TicketsScreenState();
}
class _TicketsScreenState extends ConsumerState<TicketsScreen> {
bool _byEnding = true;
String _q = '';
void _mode(bool byEnding) => setState(() {
_byEnding = byEnding;
_q = '';
});
@override
Widget build(BuildContext context) {
final date = ref.watch(selectedDateProvider);
final detail = ref.watch(shopDetailProvider((widget.shopId, date)));
return Scaffold(
body: SafeArea(
child: asyncView(
detail,
(d) => Column(
children: [
ScreenHeader(
title: '${d.shop.name} tickets',
subtitle: '${plural(d.tickets, 'ticket')} · ${dateShort(date)}',
back: true,
),
PagePad(
top: S.s4,
bottom: S.s10,
child: Container(
constraints: const BoxConstraints(maxWidth: 420),
padding: const EdgeInsets.all(S.s4),
decoration: BoxDecoration(
color: C.chip,
borderRadius: BorderRadius.circular(R.input),
),
child: Row(
children: [
_seg('By ending', _byEnding, () => _mode(true)),
_seg('By order', !_byEnding, () => _mode(false)),
],
),
),
),
PagePad(
bottom: S.s8,
child: SearchField(
key: ValueKey(_byEnding),
hint: _byEnding
? 'Search endings (e.g. 0987)'
: 'Search message or ending',
onChanged: (v) => setState(() => _q = v.trim()),
),
),
Expanded(child: _byEnding ? _endings(d) : _orders(d)),
],
),
retry: () =>
ref.invalidate(shopDetailProvider((widget.shopId, date))),
),
),
);
}
Widget _seg(String t, bool on, VoidCallback tap) => Expanded(
child: Pressable(
onTap: tap,
radius: R.md,
builder: (_, hover) => AnimatedContainer(
duration: const Duration(milliseconds: 160),
height: 38,
alignment: Alignment.center,
decoration: BoxDecoration(
color: on ? Colors.white : (hover ? C.hover : Colors.transparent),
borderRadius: BorderRadius.circular(R.md),
),
child: Text(
t,
style: ts(13, w: on ? w7 : w5, c: on ? C.navy : C.muted),
),
),
),
);
Widget _endings(ShopDetail d) {
final list = d.endings
.where((e) => _q.isEmpty || e.ending.contains(_q))
.toList();
if (list.isEmpty) {
return ListView(
children: const [
EmptyState(
icon: Icons.search_off_rounded,
title: 'No matching endings',
),
],
);
}
return PageList(
children: [
LayoutBuilder(
builder: (context, c) {
const gap = 8.0, min = 104.0;
final cols = ((c.maxWidth + gap) / (min + gap)).floor().clamp(
1,
12,
);
final w = (c.maxWidth - gap * (cols - 1)) / cols;
return Wrap(
alignment: WrapAlignment.center,
spacing: gap,
runSpacing: gap,
children: [for (final e in list) _chip(e, w)],
);
},
),
],
);
}
Widget _chip(EndingQty e, double width) {
final win = e.wins;
return Container(
width: width,
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: S.s8, vertical: S.s10),
decoration: BoxDecoration(
color: win ? C.greenTint : Colors.white,
borderRadius: BorderRadius.circular(R.md),
border: Border.all(color: win ? C.green : C.border),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
e.ending,
style: ts(14, w: w7, c: win ? C.green : C.ink, ls: 0.4),
),
const SizedBox(width: S.s6),
Text(
'×${inr(e.qty)}',
style: ts(12, w: w6, c: win ? C.green : C.muted),
),
],
),
);
}
Widget _orders(ShopDetail d) {
final q = _q.toLowerCase();
final orders = [
for (final o in d.orders)
if (o.matches(q)) o,
];
if (orders.isEmpty) {
return ListView(
children: [
EmptyState(
icon: q.isEmpty
? Icons.receipt_long_outlined
: Icons.search_off_rounded,
title: q.isEmpty ? 'No orders' : 'No matching orders',
),
],
);
}
return PageList(
children: [
CardGrid(
children: [
for (final o in orders)
OrderCard(order: o, onTap: () => context.push('/orders/${o.id}')),
],
),
],
);
}
}