selezione_cliente_page.dart 6.9 KB

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