175 lines
5.1 KiB
Dart
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),
|
|
],
|
|
),
|
|
);
|
|
}
|