selezione_cliente_page.dart 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_riverpod/flutter_riverpod.dart';
  3. import 'package:ristoris/core/credit_score_color.dart';
  4. import 'package:ristoris/database/database.dart';
  5. import 'package:ristoris/providers/database_provider.dart';
  6. import '../../widgets/search_bar.dart' as sw;
  7. import '../../widgets/empty_state.dart';
  8. class SelezioneClientePage extends ConsumerStatefulWidget {
  9. final int idAgente;
  10. const SelezioneClientePage({super.key, required this.idAgente});
  11. @override
  12. ConsumerState<SelezioneClientePage> createState() =>
  13. _SelezioneClientePageState();
  14. }
  15. class _SelezioneClientePageState extends ConsumerState<SelezioneClientePage> {
  16. String _filter = '';
  17. final _searchCtrl = TextEditingController();
  18. @override
  19. void dispose() {
  20. _searchCtrl.dispose();
  21. super.dispose();
  22. }
  23. @override
  24. Widget build(BuildContext context) {
  25. final clientiAsync = ref.watch(
  26. _filter.isEmpty
  27. ? customerListForAgentProvider(widget.idAgente)
  28. : customerSearchForAgentProvider((
  29. idAgente: widget.idAgente,
  30. filter: _filter,
  31. )),
  32. );
  33. return Scaffold(
  34. appBar: AppBar(title: const Text('Seleziona Cliente')),
  35. body: SafeArea(
  36. child: Column(
  37. children: [
  38. sw.SearchBarWidget(
  39. controller: _searchCtrl,
  40. hint: 'Cerca per codice, ragione sociale o partita IVA...',
  41. onChanged: (v) => setState(() => _filter = v),
  42. onClear: () {
  43. _searchCtrl.clear();
  44. setState(() => _filter = '');
  45. },
  46. ),
  47. Expanded(
  48. child: clientiAsync.when(
  49. data: (clienti) {
  50. if (clienti.isEmpty) {
  51. return const EmptyState(
  52. icon: Icons.people_outline,
  53. title: 'Nessun cliente trovato',
  54. );
  55. }
  56. return ListView.builder(
  57. padding:
  58. const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
  59. itemCount: clienti.length,
  60. itemBuilder: (_, i) => _ClienteTile(
  61. cliente: clienti[i],
  62. onTap: () => _onClienteTap(clienti[i]),
  63. ),
  64. );
  65. },
  66. loading: () => const Center(child: CircularProgressIndicator()),
  67. error: (e, _) => Center(child: Text('Errore: $e')),
  68. ),
  69. ),
  70. ],
  71. ),
  72. ),
  73. );
  74. }
  75. void _onClienteTap(Cliente cliente) {
  76. if (cliente.codBlocco == '001') {
  77. ScaffoldMessenger.of(context).showSnackBar(
  78. const SnackBar(
  79. content: Text('Cliente bloccato: impossibile selezionare')),
  80. );
  81. return;
  82. }
  83. Navigator.pop(context, cliente);
  84. }
  85. }
  86. class _ClienteTile extends StatelessWidget {
  87. final Cliente cliente;
  88. final VoidCallback onTap;
  89. const _ClienteTile({required this.cliente, required this.onTap});
  90. @override
  91. Widget build(BuildContext context) {
  92. final theme = Theme.of(context);
  93. final cs = theme.colorScheme;
  94. final bgColor = creditScoreBg(cliente.creditScoreColor);
  95. return Card(
  96. margin: const EdgeInsets.only(bottom: 8),
  97. elevation: 0,
  98. color: Colors.white,
  99. shape: RoundedRectangleBorder(
  100. borderRadius: BorderRadius.circular(12),
  101. side: BorderSide(color: cs.outlineVariant),
  102. ),
  103. child: InkWell(
  104. borderRadius: BorderRadius.circular(12),
  105. onTap: onTap,
  106. child: Padding(
  107. padding: const EdgeInsets.all(12),
  108. child: Row(
  109. children: [
  110. Expanded(
  111. child: Column(
  112. crossAxisAlignment: CrossAxisAlignment.start,
  113. children: [
  114. Row(
  115. children: [
  116. if ((cliente.codCliente ?? '').isNotEmpty)
  117. Container(
  118. padding: const EdgeInsets.symmetric(
  119. horizontal: 8, vertical: 4),
  120. decoration: BoxDecoration(
  121. color: bgColor ?? cs.surfaceContainerHighest,
  122. borderRadius: BorderRadius.circular(4),
  123. ),
  124. child: Text(
  125. cliente.codCliente ?? '',
  126. style: TextStyle(
  127. fontSize: 12,
  128. height: 16 / 12,
  129. fontWeight: FontWeight.w500,
  130. color: bgColor != null
  131. ? Colors.white
  132. : cs.onSurfaceVariant,
  133. ),
  134. ),
  135. ),
  136. const SizedBox(width: 8),
  137. Expanded(
  138. child: Text(
  139. cliente.ragioneSociale ?? '',
  140. style: theme.textTheme.bodyMedium
  141. ?.copyWith(fontWeight: FontWeight.w600),
  142. maxLines: 1,
  143. overflow: TextOverflow.ellipsis,
  144. ),
  145. ),
  146. ],
  147. ),
  148. const SizedBox(height: 4),
  149. Row(
  150. children: [
  151. if ((cliente.telefono ?? '').isNotEmpty) ...[
  152. Icon(Icons.phone,
  153. size: 14, color: cs.onSurfaceVariant),
  154. const SizedBox(width: 4),
  155. Text(cliente.telefono ?? '',
  156. style: theme.textTheme.bodySmall
  157. ?.copyWith(color: cs.onSurfaceVariant)),
  158. const SizedBox(width: 12),
  159. ],
  160. if ((cliente.cellulare ?? '').isNotEmpty) ...[
  161. Icon(Icons.smartphone,
  162. size: 14, color: cs.onSurfaceVariant),
  163. const SizedBox(width: 4),
  164. Text(cliente.cellulare ?? '',
  165. style: theme.textTheme.bodySmall
  166. ?.copyWith(color: cs.onSurfaceVariant)),
  167. ],
  168. ],
  169. ),
  170. ],
  171. ),
  172. ),
  173. if (cliente.codBlocco == '001')
  174. Icon(Icons.block, color: Colors.red, size: 24)
  175. else if (cliente.codBlocco != null &&
  176. cliente.codBlocco!.isNotEmpty)
  177. Icon(Icons.warning_amber_rounded,
  178. color: Colors.amber, size: 24),
  179. ],
  180. ),
  181. ),
  182. ),
  183. );
  184. }
  185. }
  186. final customerListForAgentProvider =
  187. FutureProvider.family<List<Cliente>, int>((ref, idAgente) {
  188. return ref.read(clientiDaoProvider).getActiveClients(idAgente);
  189. });
  190. final customerSearchForAgentProvider =
  191. FutureProvider.family<List<Cliente>, ({int idAgente, String filter})>(
  192. (ref, params) {
  193. return ref
  194. .read(clientiDaoProvider)
  195. .getClienti(params.idAgente, params.filter);
  196. },
  197. );