srs/lib/features/shops/shops_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

175 lines
5.1 KiB
Dart

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 'shop_sheet.dart';
class ShopsScreen extends ConsumerStatefulWidget {
const ShopsScreen({super.key});
@override
ConsumerState<ShopsScreen> createState() => _ShopsScreenState();
}
class _ShopsScreenState extends ConsumerState<ShopsScreen> {
String _q = '';
@override
Widget build(BuildContext context) {
final date = ref.watch(selectedDateProvider);
final isAdmin = ref.watch(sessionProvider)?.isAdmin ?? false;
final shops = ref.watch(shopsProvider(date));
final count = shops.value?.where((s) => s.shop.active).length;
Future<void> refresh() => refreshData(context, ref, shopsProvider(date));
return Column(
children: [
ScreenHeader(
title: 'Shops',
subtitle: count == null
? null
: '${plural(count, 'shop')} · ${dateShort(date)}',
subtitleLoading: shops.isLoading,
trailing: isAdmin
? SmallButton('Add shop', onTap: () => showShopSheet(context))
: null,
),
PagePad(
top: S.s4,
bottom: S.s6,
child: _Toolbar(
search: SearchField(
hint: 'Search shops',
onChanged: (v) => setState(() => _q = v.trim().toLowerCase()),
),
),
),
Expanded(
child: RefreshableView(
value: shops,
onRefresh: refresh,
skeleton: const SkeletonList(
top: S.s8,
children: [TileGridBone(figure: false)],
),
builder: (list) {
final f = list
.where(
(s) =>
_q.isEmpty ||
s.shop.name.toLowerCase().contains(_q) ||
s.shop.code.toLowerCase().contains(_q),
)
.toList();
if (f.isEmpty) {
return ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: [
EmptyState(
icon: Icons.storefront_outlined,
title: list.isEmpty
? 'No shops yet'
: 'No results for “$_q”',
body: list.isEmpty
? (isAdmin
? 'Add a shop to get started.'
: 'No shops are assigned to your account. Contact your administrator.')
: null,
),
],
);
}
return PageList(
top: S.s8,
children: [
CardGrid(children: [for (final s in f) _ShopTile(s: s)]),
],
);
},
),
),
],
);
}
}
class _Toolbar extends StatelessWidget {
const _Toolbar({required this.search});
final Widget search;
@override
Widget build(BuildContext context) => LayoutBuilder(
builder: (context, c) {
if (c.maxWidth < 760) {
return Column(
children: [
const DateStrip(),
const SizedBox(height: S.s12),
search,
],
);
}
return Row(
children: [
const SizedBox(width: 380, child: DateStrip()),
const SizedBox(width: S.s16),
Expanded(child: search),
],
);
},
);
}
class _ShopTile extends StatelessWidget {
const _ShopTile({required this.s});
final ShopDay s;
@override
Widget build(BuildContext context) =>
Opacity(opacity: s.shop.active ? 1 : 0.62, child: _card(context));
Widget _card(BuildContext context) => SrsCard(
onTap: () => s.shop.active
? context.push('/shops/${s.shop.id}')
: showShopSheet(context, shop: s.shop),
padding: const EdgeInsets.all(S.s14),
child: Row(
children: [
CodeTile(s.shop.code),
const SizedBox(width: S.s14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
s.shop.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: ts(15, w: w6),
),
const SizedBox(height: S.s4),
Text(
!s.shop.active
? 'Inactive'
: s.orders == 0
? 'No orders'
: '${plural(s.tickets, 'ticket')} · ${plural(s.orders, 'order')}',
style: ts(12, c: C.muted),
),
],
),
),
if (s.shop.active)
const Icon(Icons.chevron_right_rounded, color: C.placeholder)
else
const Pill('Inactive', bg: C.redTint, fg: C.red),
],
),
);
}