import 'package:cbluna_crm_lu/helpers/globals.dart'; import 'package:cbluna_crm_lu/pages/content_manager/widget/edit_video_popup_neo.dart'; import 'package:cbluna_crm_lu/pages/widgets/pluto_grid/pluto_grid_header.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:pluto_grid/pluto_grid.dart'; import 'package:cbluna_crm_lu/pages/content_manager/widget/edit_video_popup.dart'; import 'package:cbluna_crm_lu/pages/content_manager/widget/popup_detalle_video.dart'; import 'package:cbluna_crm_lu/pages/content_manager/widget/popup_eliminar_video.dart'; import 'package:cbluna_crm_lu/pages/widgets/animated_hover_button.dart'; import '../../../providers/videos_provider.dart'; import '../../../theme/theme.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:toggle_switch/toggle_switch.dart'; class AllVideoTable extends StatelessWidget { const AllVideoTable({ Key? key, required this.providerAd, }) : super(key: key); final VideosProvider providerAd; @override Widget build(BuildContext context) { return Flexible( child: PlutoGrid( key: UniqueKey(), configuration: PlutoGridConfiguration( enableMoveDownAfterSelecting: true, enableMoveHorizontalInEditing: true, localeText: const PlutoGridLocaleText.spanish(), scrollbar: plutoGridScrollbarConfig(context), style: plutoGridStyleConfigContentManager(context, rowHeight: 150), columnFilter: const PlutoGridColumnFilterConfig( filters: [ ...FilterHelper.defaultFilters, ], ), ), columns: [ PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.numbers, color: AppTheme.of(context).primaryBackground, texto: 'ID', ), title: 'ID', field: 'video_id', titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, width: 100, type: PlutoColumnType.text(), cellPadding: const EdgeInsets.all(0), enableEditingMode: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, renderer: (rendererContext) { return rendererContext.cell.row.cells['categories']!.value .toString() .replaceAll(RegExp(r'^\[|\]$'), '') .replaceAll('", "', ', ') .replaceAll('null', '') .isEmpty ? Row( mainAxisSize: MainAxisSize.max, children: [ Container( color: Colors.yellowAccent[400], child: Center( child: //icono warning Icon( Icons.warning, color: AppTheme.of(context).tertiaryText, size: 24, ), ), ), Expanded( child: Container( color: Colors.yellowAccent[400], child: Center( child: Text( rendererContext.cell.value.toString(), textAlign: TextAlign.center, style: TextStyle( color: AppTheme.of(context).primaryText, fontSize: 18, fontWeight: FontWeight.w500, ), ), ), ), ) ], ) : Text( rendererContext.cell.value.toString(), textAlign: TextAlign.center, style: TextStyle( color: AppTheme.of(context).primaryText, fontSize: 18, fontWeight: FontWeight.w500, ), ); }, ), PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.play_arrow, color: AppTheme.of(context).primaryBackground, texto: 'Video', ), title: 'Video', field: 'video_url', width: 225, titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, type: PlutoColumnType.text(), enableEditingMode: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, renderer: (rendererContext) { try { return Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( height: 250, width: 100, clipBehavior: Clip.antiAlias, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20)), child: Image.network( rendererContext.row.cells['poster_path']!.value, fit: BoxFit.cover, )), const SizedBox(width: 10), Container( decoration: BoxDecoration( shape: BoxShape.circle, color: AppTheme.of(context).primaryBackground, ), child: AnimatedHoverButton( icon: Icons.play_arrow, tooltip: 'Iniciar', size: 48, primaryColor: AppTheme.of(context).primaryBackground, secondaryColor: AppTheme.of(context).secondaryColor, onTap: () async { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( backgroundColor: Colors.transparent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), content: PopupDetallesVideo( tituloEncabezado: "Video actual", url: rendererContext.cell.value.toString(), titulo: rendererContext.row.cells['title']!.value, video: rendererContext.row.cells, ), // Widget personalizado ); }, ); }, ), ), ], ); } catch (e) { return Container( color: Colors.transparent, child: const Text("--", textAlign: TextAlign.center)); } }, ), PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.calendar_month, color: AppTheme.of(context).primaryBackground, texto: 'Fecha de creación', ), title: 'Created at', field: 'created_at', titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, width: 150, type: PlutoColumnType.date(), enableEditingMode: false, enableAutoEditing: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, renderer: (rendererContext) { // Obtiene el valor de la fecha String? fecha = rendererContext.row.cells['created_at']?.value; // Uso de operador ternario para verificar si hay fecha y formatearla String fechaFormateada = (fecha != null && fecha.isNotEmpty) ? DateFormat("dd 'de' MMMM 'de' yyyy", 'es_ES') .format(DateTime.parse(fecha)) : '--'; return SizedBox( width: 250, child: Text( fechaFormateada, textAlign: TextAlign.center, maxLines: 3, overflow: TextOverflow.ellipsis, style: TextStyle( color: AppTheme.of(context).primaryText, fontSize: 16, fontWeight: FontWeight.w500, ), ), ); }, ), PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.check_circle, color: AppTheme.of(context).primaryBackground, texto: 'Estado', ), title: 'Estado', field: 'video_status', titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, width: 200, type: PlutoColumnType.text(), enableEditingMode: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, renderer: (rendererContext) { //usa el tooltip para mostrar el estado del video return MouseRegion( cursor: SystemMouseCursors.click, child: ToggleSwitch( centerText: true, minWidth: 90.0, cornerRadius: 20.0, fontSize: 14, activeBgColor: [AppTheme.of(context).primaryColor], activeFgColor: Colors.white, inactiveBgColor: AppTheme.of(context).primaryText.withOpacity(0.7), inactiveFgColor: Colors.white, activeBgColors: [ [AppTheme.of(context).primaryColor], const [Colors.red] ], borderWidth: 2.0, borderColor: [ AppTheme.of(context).tertiaryText.withOpacity(0.7) ], labels: const ['Activo', 'Inactivo'], initialLabelIndex: rendererContext.cell.value == true ? 0 : 1, onToggle: (index) { rendererContext.cell.row.cells['video_status']!.value = index == 0 ? true : false; //cambia el valor en la celda providerAd.cambiarEstadoVideo( rendererContext.cell.row.cells['video_id']!.value, index == 0 ? true : false); }, ), ); }), PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.star, color: AppTheme.of(context).primaryBackground, texto: 'Prioridad', ), title: 'Priority', field: 'priority', titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, width: 125, type: PlutoColumnType.number(), enableEditingMode: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, renderer: (rendererContext) { return Container( width: 100, padding: const EdgeInsets.all(5), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: rendererContext.row.cells['priority']!.value == 4 ? const Color(0xFFD90D56).withOpacity(0.5) : rendererContext.row.cells['priority']!.value == 3 ? const Color(0xFFFFC700).withOpacity(0.5) : rendererContext.row.cells['priority']!.value == 2 ? const Color(0xFF517EF2).withOpacity(0.5) : const Color(0x5A0E2152)), child: Text( rendererContext.row.cells['priority']!.value == 4 ? 'alta' : rendererContext.row.cells['priority']!.value == 3 ? 'media' : rendererContext.row.cells['priority']!.value == 2 ? 'baja' : 'neutra', textAlign: TextAlign.center, style: TextStyle( color: AppTheme.of(context).primaryText, fontSize: 18, fontWeight: FontWeight.w500, ), )); }, ), PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.title, color: AppTheme.of(context).primaryBackground, texto: 'Titulo', ), title: 'Title', field: 'title', titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, width: 325, type: PlutoColumnType.text(), enableEditingMode: false, enableAutoEditing: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, renderer: (rendererContext) { return SizedBox( width: 150, child: Text( rendererContext.row.cells['title']!.value ?? '--', textAlign: TextAlign.center, maxLines: 3, overflow: TextOverflow.ellipsis, style: TextStyle( color: AppTheme.of(context).primaryText, fontSize: 18, fontWeight: FontWeight.w500, ), ), ); }, ), PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.description, color: AppTheme.of(context).primaryBackground, texto: 'Descripción', ), title: 'Descripción', field: 'overview', titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, width: 350, type: PlutoColumnType.text(), enableEditingMode: false, enableAutoEditing: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, renderer: (rendererContext) { return providerAd.showWarningsOnTable == true ? Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: Container( color: rendererContext.cell.value.isEmpty ? Colors.yellowAccent[400] : Colors.transparent, child: Center( child: Text( rendererContext.cell.value.isEmpty ? 'NO DESCRIPTION' : rendererContext.cell.value, textAlign: TextAlign.center, style: TextStyle( color: rendererContext.cell.value.isEmpty ? AppTheme.of(context).tertiaryText : AppTheme.of(context).primaryText, fontSize: rendererContext.cell.value.isEmpty ? 18 : 14, fontWeight: rendererContext.cell.value.isEmpty ? FontWeight.w800 : FontWeight.w500, ), ), ), ), ), ], ) : SizedBox( width: 200, child: Text( rendererContext.row.cells['overview']!.value, textAlign: TextAlign.center, maxLines: 4, overflow: TextOverflow.ellipsis, style: TextStyle( color: AppTheme.of(context).primaryText, fontSize: 14, fontWeight: FontWeight.w500, ), ), ); }, ), PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.qr_code_2_rounded, color: AppTheme.of(context).primaryBackground, texto: 'QRs generados', ), title: 'QRs generados', field: 'qr_codes', titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, width: 150, type: PlutoColumnType.text(), enableEditingMode: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, renderer: (rendererContext) { final qrCodes = rendererContext.row.cells['qr_codes']?.value; // Verifica que qrCodes no esté vacío antes de mostrar el botón if (qrCodes != null && qrCodes.isNotEmpty) { return Center( child: ElevatedButton.icon( icon: Icon(Icons.list_alt_rounded, size: 18), label: Text( 'Ver QRs', style: TextStyle(fontSize: 13), ), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6), backgroundColor: AppTheme.of(context).primaryColor, foregroundColor: Colors.white, ), onPressed: () { showDialog( context: context, builder: (_) => AlertDialog( backgroundColor: AppTheme.of(context).primaryBackground, title: const Text('Lista de QRs'), content: SizedBox( width: 400, height: 300, child: ListView.builder( itemCount: qrCodes .split('\n') .length, // Divide los QR Codes concatenados itemBuilder: (context, index) { try { final qrData = qrCodes.split( '\n')[index]; // Obtiene el QR individual return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Text( qrData, style: const TextStyle(fontSize: 14), ), ); } catch (e) { return Text( 'Error al leer QR: $e', style: const TextStyle(color: Colors.red), ); } }, ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Cerrar'), ), ], ), ); }, ), ); } else { return Center( child: Text( 'GLOBAL', style: TextStyle( color: AppTheme.of(context).primaryText, fontSize: 18, fontWeight: FontWeight.w900, ), )); } }, ), PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.link, color: AppTheme.of(context).primaryBackground, texto: 'Enlace', ), title: 'OutLink', field: 'url_ad', titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, width: 100, type: PlutoColumnType.text(), enableEditingMode: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, renderer: (rendererContext) { String? url = rendererContext.row.cells['url_ad']?.value; bool isValidUrl = url != null && url != 'null' && url.isNotEmpty; return providerAd.showWarningsOnTable == true ? Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( color: isValidUrl ? Colors.transparent : Colors.yellowAccent[400], child: IconButton( onPressed: () async { if (isValidUrl) { String finalUrl = url!.startsWith(RegExp(r'http://|https://')) ? url : 'http://$url'; if (await canLaunchUrl(Uri.parse(finalUrl))) { await launchUrl(Uri.parse(finalUrl)); } else { print('No se puede abrir el enlace'); } } else { print('No se puede abrir el enlace'); } }, icon: Icon( isValidUrl ? Icons.link : Icons.link_off, color: isValidUrl ? AppTheme.of(context).primaryColor : AppTheme.of(context).tertiaryText, semanticLabel: isValidUrl ? 'Open link' : 'No link', ), ), ), if (!isValidUrl) const Text('No link added', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w800)), ], ) : IconButton( onPressed: () async { if (isValidUrl) { // Asegurarse de que 'url' comience con 'http://' o 'https://' String finalUrl = url!.startsWith(RegExp(r'http://|https://')) ? url : 'http://$url'; if (await canLaunchUrl(Uri.parse(finalUrl))) { await launchUrl(Uri.parse(finalUrl)); } else { print('No se puede abrir el enlace'); } } else { print('No se puede abrir el enlace'); } }, icon: Icon( // Usar 'isValidUrl' para determinar el ícono a mostrar. isValidUrl ? Icons.link : Icons.link_off, color: AppTheme.of(context).primaryColor, ), ); }, ), PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.category, color: AppTheme.of(context).primaryBackground, texto: 'Categoría de video', ), title: 'Categoría', field: 'categories', titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, width: 200, type: PlutoColumnType.text(), enableEditingMode: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, renderer: (rendererContext) { var textoLimpio = rendererContext.cell.value .toString() .replaceAll(RegExp(r'^\[|\]$'), '') .replaceAll('", "', ', ') .replaceAll('null', ''); return Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: Container( color: textoLimpio.isEmpty ? Colors.yellowAccent[400] : Colors.transparent, child: Center( child: Text( textoLimpio.isEmpty ? 'NO CATEGORIES' : textoLimpio, textAlign: TextAlign.center, style: TextStyle( color: textoLimpio.isEmpty ? AppTheme.of(context).tertiaryText : AppTheme.of(context).primaryText, fontSize: textoLimpio.isEmpty ? 18 : 16, fontWeight: textoLimpio.isEmpty ? FontWeight.w800 : FontWeight.w500, ), ), ), ), ), ], ); }, ), PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.person, color: AppTheme.of(context).primaryBackground, texto: 'Patrocinador', ), title: 'Partner', field: 'partner', titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, width: 150, type: PlutoColumnType.text(), enableEditingMode: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, renderer: (rendererContext) { return providerAd.showWarningsOnTable == true ? Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: Container( color: rendererContext.cell.value.isEmpty ? Colors.yellowAccent[400] : Colors.transparent, child: Center( child: Text( rendererContext.cell.value.isEmpty ? 'NO PARTNER' : rendererContext.cell.value, textAlign: TextAlign.center, style: TextStyle( color: rendererContext.cell.value.isEmpty ? AppTheme.of(context).tertiaryText : AppTheme.of(context).primaryText, fontSize: rendererContext.cell.value.isEmpty ? 18 : 16, fontWeight: rendererContext.cell.value.isEmpty ? FontWeight.w800 : FontWeight.w500, ), ), ), ), ), ], ) : SizedBox( width: 200, child: Text( rendererContext.row.cells['partner']!.value ?? '--', textAlign: TextAlign.center, maxLines: 4, overflow: TextOverflow.ellipsis, style: TextStyle( color: AppTheme.of(context).primaryText, fontSize: 18, fontWeight: FontWeight.w500, ), ), ); }, ), /* PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.calendar_today, color: AppTheme.of(context).primaryBackground, texto: 'Fecha expiración', ), title: 'Exp. date', field: 'expirationDate', titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, width: 180, type: PlutoColumnType.date(), enableEditingMode: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, ), */ PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.timelapse, color: AppTheme.of(context).primaryBackground, texto: 'Duración', ), title: 'Duration', field: 'duration_video', titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, width: 130, type: PlutoColumnType.number(), enableEditingMode: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, renderer: (rendererContext) { int durationInSeconds = rendererContext.cell.value; String minutes = (durationInSeconds ~/ 60).toString().padLeft(2, '0'); String seconds = (durationInSeconds % 60).toString().padLeft(2, '0'); return Text( '$minutes:$seconds', textAlign: TextAlign.center, style: TextStyle( color: AppTheme.of(context).primaryText, fontSize: 14, fontWeight: FontWeight.w500, ), ); }, ), PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.edit, color: AppTheme.of(context).primaryBackground, texto: 'Editar', ), title: 'Edit', field: 'editar', width: 100, titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, type: PlutoColumnType.number(), enableEditingMode: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, renderer: (rendererContext) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( alignment: Alignment.center, child: AnimatedHoverButton( icon: Icons.edit, tooltip: 'Edit', primaryColor: AppTheme.of(context).primaryBackground, secondaryColor: AppTheme.of(context).primaryColor, onTap: () async { providerAd.resetAllvideoData(); final List qrsAsociados = await providerAd.getQrsByVideoId(rendererContext .cell.row.cells['video_id']!.value); providerAd.listaQrsSeleccionados = qrsAsociados; showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { providerAd.selectedVideoId = rendererContext .cell.row.cells['video_id']!.value; providerAd.tituloVideo = rendererContext .cell.row.cells['title']!.value; providerAd.videoPoints = rendererContext .cell.row.cells['points']!.value; providerAd.videoName = rendererContext .cell.row.cells['title']!.value; providerAd.videoPatner = rendererContext .cell.row.cells['partner']!.value; providerAd.descripcionVideo = rendererContext .cell.row.cells['overview']!.value; providerAd.videoOutlink = rendererContext .cell.row.cells['url_ad']!.value; //video_poster_file providerAd.videoCoverFile = rendererContext .cell.row.cells['poster_file_name']!.value; providerAd.videoCoverFileNameNoModif = rendererContext.cell.row .cells['poster_file_name']!.value; providerAd.selectePriority = rendererContext .row.cells['priority']!.value == 4 ? 'alta' : rendererContext .row.cells['priority']!.value == 3 ? 'media' : rendererContext.row.cells['priority']! .value == 2 ? 'baja' : 'neutra'; providerAd.videoPath = rendererContext .cell.row.cells['video_url']!.value; providerAd.videoPosterPath = rendererContext .cell.row.cells['poster_path']!.value; return AlertDialog( backgroundColor: AppTheme.of(context).primaryBackground, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50), ), content: PopupEditVideoNeo( provider: providerAd, editMode: true, currentCategories: rendererContext .cell.row.cells['categories']!.value, )); }, ); }, ), ), ], ); }), PlutoColumn( titleSpan: plutoGridHeader( context: context, icono: Icons.delete, color: AppTheme.of(context).primaryBackground, texto: 'Eliminar', ), title: 'Delete', field: 'eliminar', width: 100, titleTextAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center, type: PlutoColumnType.number(), enableEditingMode: false, backgroundColor: AppTheme.of(context).primaryColor, enableContextMenu: false, enableDropToResize: false, renderer: (rendererContext) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( alignment: Alignment.center, child: AnimatedHoverButton( icon: Icons.delete, tooltip: 'Delete this video', primaryColor: AppTheme.of(context).primaryBackground, secondaryColor: Colors.redAccent, onTap: () async { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return EliminarVideoPopup( idVideo: rendererContext .cell.row.cells['video_id']!.value, nombreVideo: rendererContext .cell.row.cells['title']!.value, videoFileName: rendererContext .cell.row.cells['video_file_name']!.value, posterFileName: rendererContext .cell.row.cells['poster_file_name']!.value, videoUrl: rendererContext .cell.row.cells['video_url']!.value, ); }, ); }, ), ), ], ); }), ], rows: providerAd.allVideoRows, onLoaded: (event) async { providerAd.listStateManager.add(event.stateManager); }, createFooter: (stateManager) { stateManager.setPageSize(10, notify: false); // default 40 return PlutoPagination(stateManager); }, ), ); } }