import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/models.dart'; import '../../core/providers.dart'; import '../../core/theme.dart'; import '../../widgets/ui.dart'; Future showShopSheet( BuildContext context, { Shop? shop, VoidCallback? onDeactivated, }) { return showSheet( context, (ctx) => _ShopForm(shop: shop, onDeactivated: onDeactivated), ); } class _ShopForm extends ConsumerStatefulWidget { const _ShopForm({this.shop, this.onDeactivated}); final Shop? shop; final VoidCallback? onDeactivated; @override ConsumerState<_ShopForm> createState() => _ShopFormState(); } class _ShopFormState extends ConsumerState<_ShopForm> { late final _name = TextEditingController(text: widget.shop?.name ?? ''); final _code = TextEditingController(); late bool _active = widget.shop?.active ?? true; bool _codeTouched = false; bool _busy = false; String? _error; bool get _edit => widget.shop != null; @override void dispose() { _name.dispose(); _code.dispose(); super.dispose(); } void _nameChanged(String v) { if (!_edit && !_codeTouched) { final letters = v.toUpperCase().replaceAll(RegExp(r'[^A-Z0-9]'), ''); _code.text = letters.length > 3 ? letters.substring(0, 3) : letters; } setState(() => _error = null); } Future _save() async { setState(() { _busy = true; _error = null; }); final api = ref.read(apiProvider); try { await mutate( ref, () => _edit ? api.renameShop(widget.shop!.id, _name.text) : api.createShop(_name.text, _code.text), ); if (!mounted) return; final msg = _edit ? 'Shop updated' : 'Shop added'; Navigator.pop(context); toast(context, msg); } catch (e) { if (mounted) setState(() => _error = errText(e)); } finally { if (mounted) setState(() => _busy = false); } } Future _toggle(bool on) async { final shop = widget.shop!; final ok = await confirm( context, title: on ? 'Activate ${shop.name}?' : 'Deactivate ${shop.name}?', body: on ? 'This shop will be available again when saving orders.' : 'This shop will no longer be available when saving orders. Existing orders are kept.', confirmLabel: on ? 'Activate' : 'Deactivate', danger: !on, ); if (!ok || !mounted) return; setState(() { _busy = true; _error = null; }); try { await mutate(ref, () => ref.read(apiProvider).setShopActive(shop.id, on)); if (!mounted) return; setState(() => _active = on); if (!on) { final cb = widget.onDeactivated; Navigator.pop(context); toast(context, 'Shop deactivated'); cb?.call(); } else { toast(context, 'Shop activated'); } } catch (e) { if (mounted) setState(() => _error = errText(e)); } finally { if (mounted) setState(() => _busy = false); } } @override Widget build(BuildContext context) { final code = _edit ? widget.shop!.code : _code.text.trim().toUpperCase(); final name = _name.text.trim(); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(_edit ? 'Edit shop' : 'Add shop', style: ts(20, w: w7)), const SizedBox(height: S.s4), Text( _edit ? 'You can rename a shop. The short code cannot be changed.' : 'Create a shop to group and track its orders.', style: ts(13, c: C.muted), ), const SizedBox(height: S.s20), SrsField( controller: _name, label: 'Shop name', hint: 'e.g. Kazhakkoottam', autofocus: true, textCapitalization: TextCapitalization.words, onChanged: _nameChanged, ), const SizedBox(height: S.s16), SrsField( controller: _edit ? TextEditingController(text: widget.shop!.code) : _code, label: 'Short code', hint: 'e.g. KZK', enabled: !_edit, textCapitalization: TextCapitalization.characters, helper: _edit ? 'The short code is permanent.' : 'Up to 20 characters, no spaces.', onChanged: (_) => setState(() { _codeTouched = true; _error = null; }), ), const SizedBox(height: S.s16), Container( padding: const EdgeInsets.all(S.s14), decoration: BoxDecoration( color: C.bg, borderRadius: BorderRadius.circular(R.input), ), child: Row( children: [ CodeTile(code.isEmpty ? '···' : code, size: 38), const SizedBox(width: S.s12), Expanded( child: Text( '${code.isEmpty ? 'CODE' : code} · ${name.isEmpty ? 'Shop name' : name}', maxLines: 1, overflow: TextOverflow.ellipsis, style: ts( 14, w: w6, c: code.isEmpty && name.isEmpty ? C.placeholder : C.ink, ), ), ), ], ), ), if (_error != null) ...[ const SizedBox(height: S.s12), ErrorLine(_error!), ], const SizedBox(height: S.s20), BigButton( _edit ? 'Save changes' : 'Add shop', loading: _busy, onPressed: name.isEmpty || (!_edit && code.isEmpty) ? null : _save, ), if (_edit) ...[ const SizedBox(height: S.s14), Container( padding: const EdgeInsets.fromLTRB(S.s16, S.s10, S.s10, S.s10), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(R.input), border: Border.all(color: C.border), ), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _active ? 'Active' : 'Inactive', style: ts(14, w: w6), ), const SizedBox(height: S.s2), Text( _active ? 'Available when saving orders.' : 'Unavailable when saving orders. Existing orders are kept.', style: ts(12, c: C.muted), ), ], ), ), Switch( value: _active, onChanged: _busy ? null : _toggle, activeTrackColor: C.green, activeThumbColor: Colors.white, ), ], ), ), ], ], ); } }