mesa_screen.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. import '/models/mesa_model.dart';
  2. import '/themes/themes.dart';
  3. import '/viewmodels/mesa_view_model.dart';
  4. import 'mesa_form.dart';
  5. import '/widgets/widgets.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:provider/provider.dart';
  8. import '../../widgets/widgets_components.dart' as clase;
  9. class MesasScreen extends StatefulWidget {
  10. @override
  11. _MesasScreenState createState() => _MesasScreenState();
  12. }
  13. class _MesasScreenState extends State<MesasScreen> {
  14. final _busqueda = TextEditingController(text: '');
  15. ScrollController horizontalScrollController = ScrollController();
  16. @override
  17. void initState() {
  18. super.initState();
  19. Provider.of<MesaViewModel>(context, listen: false).fetchLocalAll();
  20. }
  21. void go(Mesa mesa) {
  22. showDialog(
  23. context: context,
  24. builder: (context) {
  25. return TotpCuadroConfirmacion(
  26. title: "Editar Mesa",
  27. content:
  28. "Por favor, ingresa el código de autenticación para continuar.",
  29. onSuccess: () {
  30. Navigator.push(
  31. context,
  32. MaterialPageRoute(
  33. builder: (context) => MesaForm(mesa: mesa),
  34. ),
  35. ).then((_) {
  36. Provider.of<MesaViewModel>(context, listen: false)
  37. .fetchLocalAll();
  38. });
  39. },
  40. );
  41. },
  42. );
  43. }
  44. void clearSearchAndReset() {
  45. setState(() {
  46. _busqueda.clear();
  47. Provider.of<MesaViewModel>(context, listen: false).fetchLocalAll();
  48. });
  49. }
  50. @override
  51. Widget build(BuildContext context) {
  52. final model = Provider.of<MesaViewModel>(context);
  53. double screenWidth = MediaQuery.of(context).size.width;
  54. final isMobile = screenWidth < 1250;
  55. final double? columnSpacing = isMobile ? null : 0;
  56. TextStyle estilo = const TextStyle(fontWeight: FontWeight.bold);
  57. List<DataRow> registros = [];
  58. for (Mesa item in model.mesas) {
  59. registros.add(DataRow(cells: [
  60. DataCell(
  61. Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
  62. PopupMenuButton(
  63. itemBuilder: (context) => [
  64. PopupMenuItem(
  65. child: const Text('Editar'),
  66. onTap: () => go(item),
  67. ),
  68. PopupMenuItem(
  69. child: const Text('Eliminar'),
  70. onTap: () async {
  71. Future.delayed(Duration.zero, () {
  72. showDialog(
  73. context: context,
  74. builder: (context) {
  75. return TotpCuadroConfirmacion(
  76. title: "Eliminar Mesa",
  77. content:
  78. "Por favor, ingresa el código de autenticación para continuar.",
  79. onSuccess: () async {
  80. await model.deleteMesa(item.id!);
  81. model.fetchLocalAll();
  82. },
  83. );
  84. },
  85. );
  86. });
  87. },
  88. ),
  89. ],
  90. icon: const Icon(Icons.more_vert),
  91. ),
  92. ])),
  93. DataCell(
  94. Text(item.id.toString()),
  95. onTap: () {
  96. Provider.of<MesaViewModel>(context, listen: false).selectMesa(item);
  97. go(item);
  98. },
  99. ),
  100. DataCell(
  101. Text(item.nombre.toString()),
  102. onTap: () {
  103. Provider.of<MesaViewModel>(context, listen: false).selectMesa(item);
  104. go(item);
  105. },
  106. ),
  107. ]));
  108. }
  109. return Scaffold(
  110. appBar: AppBar(
  111. title: Text(
  112. 'Mesas',
  113. style: TextStyle(color: AppTheme.secondary),
  114. ),
  115. iconTheme: IconThemeData(color: AppTheme.secondary),
  116. ),
  117. body: Column(
  118. children: [
  119. Expanded(
  120. child: ListView(
  121. padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
  122. children: [
  123. const SizedBox(height: 8),
  124. clase.tarjeta(
  125. Padding(
  126. padding: const EdgeInsets.all(8.0),
  127. child: LayoutBuilder(
  128. builder: (context, constraints) {
  129. if (screenWidth > 1000) {
  130. return Row(
  131. children: [
  132. Expanded(
  133. flex: 7,
  134. child: busquedaTextField(),
  135. ),
  136. SizedBox(width: 5),
  137. botonBuscar()
  138. ],
  139. );
  140. } else {
  141. return Column(
  142. children: [
  143. Row(
  144. children: [busquedaTextField()],
  145. ),
  146. SizedBox(height: 15),
  147. Row(
  148. children: [botonBuscar()],
  149. ),
  150. ],
  151. );
  152. }
  153. },
  154. ),
  155. ),
  156. ),
  157. const SizedBox(height: 8),
  158. model.isLoading
  159. ? const Center(child: CircularProgressIndicator())
  160. : Container(),
  161. clase.tarjeta(
  162. Column(
  163. children: [
  164. LayoutBuilder(builder: (context, constraints) {
  165. return SingleChildScrollView(
  166. scrollDirection: Axis.vertical,
  167. child: Scrollbar(
  168. controller: horizontalScrollController,
  169. interactive: true,
  170. thumbVisibility: true,
  171. thickness: 10.0,
  172. child: SingleChildScrollView(
  173. controller: horizontalScrollController,
  174. scrollDirection: Axis.horizontal,
  175. child: ConstrainedBox(
  176. constraints: BoxConstraints(
  177. minWidth: isMobile
  178. ? constraints.maxWidth
  179. : screenWidth),
  180. child: DataTable(
  181. columnSpacing: columnSpacing,
  182. sortAscending: true,
  183. sortColumnIndex: 1,
  184. columns: [
  185. DataColumn(label: Text(" ", style: estilo)),
  186. DataColumn(
  187. label: Text("ID", style: estilo)),
  188. DataColumn(
  189. label: Text("NOMBRE", style: estilo)),
  190. ],
  191. rows: registros,
  192. ),
  193. ),
  194. ),
  195. ),
  196. );
  197. }),
  198. ],
  199. ),
  200. ),
  201. const SizedBox(
  202. height: 15,
  203. ),
  204. if (!model.isLoading) ...[
  205. Row(
  206. mainAxisAlignment: MainAxisAlignment.center,
  207. children: [
  208. TextButton(
  209. onPressed:
  210. model.currentPage > 1 ? model.previousPage : null,
  211. child: Text('Anterior'),
  212. style: ButtonStyle(
  213. backgroundColor:
  214. MaterialStateProperty.resolveWith<Color?>(
  215. (Set<MaterialState> states) {
  216. if (states.contains(MaterialState.disabled)) {
  217. return Colors.grey;
  218. }
  219. return AppTheme.tertiary;
  220. },
  221. ),
  222. foregroundColor:
  223. MaterialStateProperty.resolveWith<Color?>(
  224. (Set<MaterialState> states) {
  225. if (states.contains(MaterialState.disabled)) {
  226. return Colors.black;
  227. }
  228. return Colors.white;
  229. },
  230. ),
  231. ),
  232. ),
  233. SizedBox(width: 15),
  234. Text(
  235. 'Página ${model.currentPage} de ${model.totalPages}'),
  236. SizedBox(width: 15),
  237. TextButton(
  238. onPressed: model.currentPage < model.totalPages
  239. ? model.nextPage
  240. : null,
  241. child: Text('Siguiente'),
  242. style: ButtonStyle(
  243. backgroundColor:
  244. MaterialStateProperty.resolveWith<Color?>(
  245. (Set<MaterialState> states) {
  246. if (states.contains(MaterialState.disabled)) {
  247. return Colors.grey;
  248. }
  249. return AppTheme.tertiary;
  250. },
  251. ),
  252. foregroundColor:
  253. MaterialStateProperty.resolveWith<Color?>(
  254. (Set<MaterialState> states) {
  255. if (states.contains(MaterialState.disabled)) {
  256. return Colors.black;
  257. }
  258. return Colors.white;
  259. },
  260. ),
  261. ),
  262. ),
  263. ],
  264. ),
  265. ],
  266. const SizedBox(
  267. height: 15,
  268. ),
  269. ],
  270. ),
  271. ),
  272. ],
  273. ),
  274. floatingActionButton: FloatingActionButton.extended(
  275. onPressed: () async {
  276. showDialog(
  277. context: context,
  278. builder: (context) {
  279. return TotpCuadroConfirmacion(
  280. title: "Agregar Mesa",
  281. content:
  282. "Por favor, ingresa el código de autenticación para continuar.",
  283. onSuccess: () {
  284. Mesa nuevaMesa = Mesa();
  285. Navigator.push(
  286. context,
  287. MaterialPageRoute(
  288. builder: (context) => MesaForm(mesa: nuevaMesa),
  289. ),
  290. ).then((_) =>
  291. Provider.of<MesaViewModel>(context, listen: false)
  292. .fetchLocalAll());
  293. },
  294. );
  295. },
  296. );
  297. },
  298. icon: Icon(Icons.add, size: 30, color: AppTheme.quaternary),
  299. label: Text(
  300. "Agregar Mesa",
  301. style: TextStyle(fontSize: 18, color: AppTheme.quaternary),
  302. ),
  303. shape: RoundedRectangleBorder(
  304. borderRadius: BorderRadius.circular(8),
  305. ),
  306. materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
  307. backgroundColor: AppTheme.tertiary,
  308. ),
  309. );
  310. }
  311. Widget busquedaTextField() {
  312. return Row(
  313. children: [
  314. Expanded(
  315. flex: 3,
  316. child: AppTextField(
  317. prefixIcon: const Icon(Icons.search),
  318. etiqueta: 'Búsqueda por nombre...',
  319. controller: _busqueda,
  320. hintText: 'Búsqueda por nombre...',
  321. ),
  322. ),
  323. const SizedBox(width: 5),
  324. ],
  325. );
  326. }
  327. Widget botonBuscar() {
  328. return Expanded(
  329. flex: 2,
  330. child: Row(
  331. children: [
  332. Expanded(
  333. flex: 2,
  334. child: Padding(
  335. padding: const EdgeInsets.only(top: 30),
  336. child: ElevatedButton(
  337. onPressed: clearSearchAndReset,
  338. style: ElevatedButton.styleFrom(
  339. shape: RoundedRectangleBorder(
  340. borderRadius: BorderRadius.circular(20.0),
  341. ),
  342. backgroundColor: AppTheme.tertiary,
  343. padding: const EdgeInsets.symmetric(vertical: 25),
  344. ),
  345. child: Text('Limpiar',
  346. style: TextStyle(color: AppTheme.quaternary)),
  347. ),
  348. ),
  349. ),
  350. const SizedBox(width: 8),
  351. Expanded(
  352. flex: 2,
  353. child: Padding(
  354. padding: const EdgeInsets.only(top: 30),
  355. child: ElevatedButton(
  356. onPressed: () async {
  357. if (_busqueda.text.isNotEmpty) {
  358. await Provider.of<MesaViewModel>(context, listen: false)
  359. .fetchLocalByName(nombre: _busqueda.text.trim());
  360. } else {
  361. ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
  362. content: Text('Introduce un nombre para buscar.')));
  363. }
  364. },
  365. style: ElevatedButton.styleFrom(
  366. shape: RoundedRectangleBorder(
  367. borderRadius: BorderRadius.circular(20.0),
  368. ),
  369. backgroundColor: AppTheme.tertiary,
  370. padding: const EdgeInsets.symmetric(vertical: 25),
  371. ),
  372. child: Text('Buscar',
  373. style: TextStyle(color: AppTheme.quaternary)),
  374. ),
  375. ),
  376. ),
  377. ],
  378. ));
  379. }
  380. }