@amsintegra/design-system 1.1.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,1125 +1,2939 @@
1
1
  'use strict';
2
2
 
3
- var antd = require('antd');
4
3
  var react = require('react');
5
4
  var jsxRuntime = require('react/jsx-runtime');
6
- var icons = require('@ant-design/icons');
5
+ var lucideReact = require('lucide-react');
6
+ var reactDom = require('react-dom');
7
7
 
8
- // src/theme/AmsConfigProvider.tsx
9
-
10
- // src/theme/tokens.ts
11
- var primitives = {
12
- brandRed: "#A72432",
13
- brandRedActive: "#8B1D28",
14
- // darken 10%
15
- brandRedLight: "#f1faff",
16
- green: "#50cd89",
17
- greenActive: "#47be7d",
18
- greenLight: "#e8fff3",
19
- purple: "#7239ea",
20
- purpleActive: "#5014d0",
21
- purpleLight: "#f8f5ff",
22
- pink: "#f1416c",
23
- pinkActive: "#d9214e",
24
- pinkLight: "#fff5f8",
25
- yellow: "#ffc700",
26
- yellowActive: "#f1bc00",
27
- yellowLight: "#fff8dd",
28
- white: "#ffffff",
29
- gray: {
30
- 100: "#f9f9f9",
31
- 200: "#f4f4f4",
32
- 300: "#e1e3ea",
33
- 400: "#b5b5c3",
34
- 500: "#a1a5b7",
35
- 600: "#7e8299",
36
- 700: "#5e6278",
37
- 800: "#3f4254",
38
- 900: "#181c32"
39
- },
40
- background: {
41
- app: "#f5f8fa",
42
- body: "#ffffff",
43
- appDark: "#151521",
44
- bodyDark: "#1e1e2d"
45
- },
46
- dark: {
47
- base: "#181c32",
48
- bg: "#151521",
49
- header: "#1e1e2d",
50
- active: "#2a2a3c",
51
- separator: "#393945",
52
- // Dark-mode surfaces (from mf-container's antdTheme).
53
- inputBg: "#1b1b29",
54
- cardBg: "#1e1e2d",
55
- // Dark-mode gray scale — DISTINCT from the light `gray` scale (Metronic).
56
- gray: {
57
- 100: "#2b2b40",
58
- 200: "#323248",
59
- 300: "#3b3b64",
60
- 400: "#474761",
61
- 500: "#565674",
62
- 600: "#6d6d80",
63
- 700: "#92929f",
64
- 800: "#cdcdde"
65
- }
66
- }
67
- };
68
- var radius = {
69
- sm: 2,
70
- base: 4,
71
- md: 6,
72
- lg: 8,
73
- xl: 12
8
+ // src/kit/lib/tema.ts
9
+ var NOMBRE_PRODUCTO_DEFAULT = "Backoffice";
10
+ var TEMA_DEFAULT = {
11
+ logo_url: null,
12
+ color_primario: "#A72432",
13
+ color_primario_oscuro: "#8B1D28",
14
+ color_acento: "#C9414D",
15
+ color_peligro: "#A72432",
16
+ color_fondo: "#ffffff",
17
+ color_texto: "#0b1f33",
18
+ color_borde: "#e2e8f0",
19
+ badge_exito: "#e6f4ea",
20
+ badge_advertencia: "#fdf1de",
21
+ badge_error: "#fbe9e9",
22
+ badge_neutro: "#ececec"
74
23
  };
75
- var typography = {
76
- fontFamily: "Inter, Helvetica, Arial, sans-serif",
77
- fontSizeBase: 13,
78
- weight: {
79
- light: 300,
80
- normal: 400,
81
- medium: 500,
82
- semibold: 600,
83
- bold: 700
24
+ function variablesDeTema(tema) {
25
+ return {
26
+ ...tema.color_acento ? { "--tema-acento": tema.color_acento } : {},
27
+ // El spinner de marca se pinta con el logo; sin logo rige el icono neutro de `globals.css`.
28
+ ...tema.logo_url ? { "--tema-logo-spinner": `url("${tema.logo_url}")` } : {},
29
+ "--tema-primario": tema.color_primario,
30
+ "--tema-primario-oscuro": tema.color_primario_oscuro,
31
+ "--tema-peligro": tema.color_peligro,
32
+ "--tema-fondo": tema.color_fondo,
33
+ "--tema-texto": tema.color_texto,
34
+ "--tema-borde": tema.color_borde,
35
+ "--tema-badge-exito": tema.badge_exito,
36
+ "--tema-badge-advertencia": tema.badge_advertencia,
37
+ "--tema-badge-error": tema.badge_error,
38
+ "--tema-badge-neutro": tema.badge_neutro
39
+ };
40
+ }
41
+ var LinkComponentContext = react.createContext(null);
42
+ var LinkComponentProvider = LinkComponentContext.Provider;
43
+ var LinkBase = react.forwardRef(function LinkBase2({ href, children, ...rest }, ref) {
44
+ const Injected = react.useContext(LinkComponentContext);
45
+ if (Injected) {
46
+ return react.createElement(Injected, { href, children, ...rest });
47
+ }
48
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { ref, href, ...rest, children });
49
+ });
50
+ var ListadoNavContext = react.createContext(null);
51
+ var ListadoNavProvider = ListadoNavContext.Provider;
52
+ function useNavegarListado() {
53
+ const ctx = react.useContext(ListadoNavContext);
54
+ if (!ctx) {
55
+ throw new Error(
56
+ "useNavegarListado must be used within a <ListadoNavProvider>. Wrap your listing screen and provide { ir, pendiente, params } connected to your router or local state."
57
+ );
84
58
  }
59
+ return ctx;
60
+ }
61
+ var TAMANO_BADGE = 16;
62
+ var TAMANO_BOTON = 20;
63
+ var ICONO_DEFAULT = lucideReact.Circle;
64
+ var ICONO_NAV_DASHBOARD = lucideReact.Activity;
65
+ var ICONO_NAV_SALUD = lucideReact.Gauge;
66
+ var ICONO_NAV_ANALITICA = lucideReact.BarChart3;
67
+ var ICONO_NAV_ENTIDADES = lucideReact.Users;
68
+ var ICONO_NAV_INVITACIONES = lucideReact.MailPlus;
69
+ var ICONO_NAV_ETIQUETAS = lucideReact.Tag;
70
+ var ICONO_NAV_JOBS = lucideReact.Briefcase;
71
+ var ICONO_NAV_TEMA = lucideReact.Palette;
72
+ var ICONO_NAV_CONFIGURACION = lucideReact.SlidersHorizontal;
73
+ var ICONO_NAV_API_KEYS = lucideReact.KeyRound;
74
+ var ICONO_NAV_BITACORA = lucideReact.History;
75
+ var ICONO_NAV_NOTIFICACIONES = lucideReact.BellRing;
76
+ var ICONO_NAV_MANUAL = lucideReact.BookOpen;
77
+ var ICONO_NAV_CLIENTE = lucideReact.KeyRound;
78
+ var ICONO_NAV_EXTRACCIONES = lucideReact.FileSpreadsheet;
79
+ var ICONO_CERRAR_SESION = lucideReact.LogOut;
80
+ var ICONO_INICIAR_SESION = lucideReact.LogIn;
81
+ var ICONO_NAV_COLAPSAR = lucideReact.PanelLeftClose;
82
+ var ICONO_NAV_EXPANDIR = lucideReact.PanelLeftOpen;
83
+ var ICONO_NAV_MENU_MOVIL = lucideReact.Menu;
84
+ var ICONO_CERRAR = lucideReact.X;
85
+ var ICONO_ALTA = lucideReact.PlusCircle;
86
+ var ICONO_GENERAR = lucideReact.Sparkles;
87
+ var ICONO_GUARDAR = lucideReact.Save;
88
+ var ICONO_EDITAR = lucideReact.Pencil;
89
+ var ICONO_BAJA = lucideReact.Trash2;
90
+ var ICONO_DESHABILITAR = lucideReact.UserX;
91
+ var ICONO_INACTIVAR = lucideReact.PauseCircle;
92
+ var ICONO_REACTIVAR_ETIQUETA = lucideReact.PlayCircle;
93
+ var ICONO_REACTIVAR = lucideReact.RotateCcw;
94
+ var ICONO_RESTABLECER = lucideReact.RotateCcw;
95
+ var ICONO_REINTENTAR = lucideReact.RefreshCw;
96
+ var ICONO_COPIAR = lucideReact.Copy;
97
+ var ICONO_LINK = lucideReact.Link2;
98
+ var ICONO_DESCARGAR = lucideReact.Download;
99
+ var ICONO_EXPORTAR = lucideReact.FileSpreadsheet;
100
+ var ICONO_EXPORTAR_CSV = lucideReact.Download;
101
+ var ICONO_CONFIRMAR = lucideReact.CheckCircle2;
102
+ var ICONO_VOLVER = lucideReact.ArrowLeft;
103
+ var ICONO_VER_TODOS = lucideReact.ArrowRight;
104
+ var ICONO_ROTAR_KEY = lucideReact.RotateCcw;
105
+ var ICONO_REVOCAR_KEY = lucideReact.Ban;
106
+ var ICONO_API_KEY = lucideReact.KeyRound;
107
+ var ICONO_EMAIL = lucideReact.Mail;
108
+ var ICONO_INVITACION_GENERICA = lucideReact.UserPlus;
109
+ var ICONO_SEGURIDAD = lucideReact.ShieldCheck;
110
+ var ICONO_CAMPANA = lucideReact.Bell;
111
+ var ICONO_SILENCIAR = lucideReact.BellOff;
112
+ var ICONO_FILTRO = lucideReact.Filter;
113
+ var ICONO_BUSCAR = lucideReact.Search;
114
+ var ICONO_LIMPIAR = lucideReact.X;
115
+ var ICONO_SELECCIONADO = lucideReact.Check;
116
+ var ICONO_ORDENAR_ASC = lucideReact.ArrowUp;
117
+ var ICONO_ORDENAR_DESC = lucideReact.ArrowDown;
118
+ var ICONO_ORDENAR_NEUTRO = lucideReact.ArrowUpDown;
119
+ var ICONO_PAGINA_ANTERIOR = lucideReact.ArrowLeft;
120
+ var ICONO_PAGINA_SIGUIENTE = lucideReact.ArrowRight;
121
+ var ICONO_VISTA_TABLA = lucideReact.Table2;
122
+ var ICONO_VISTA_TARJETAS = lucideReact.LayoutGrid;
123
+ var ICONO_ARRASTRAR_COLUMNA = lucideReact.GripVertical;
124
+ var ICONO_COLUMNAS = lucideReact.Columns3;
125
+ var ICONO_COLUMNA_VISIBLE = lucideReact.Eye;
126
+ var ICONO_COLUMNA_OCULTA = lucideReact.EyeOff;
127
+ var ICONO_ACCIONES_FILA = lucideReact.Ellipsis;
128
+ var ICONO_DENSIDAD_COMPACTA = lucideReact.Rows4;
129
+ var ICONO_DENSIDAD_COMODA = lucideReact.Rows2;
130
+ var ICONO_CARGANDO = lucideReact.Loader2;
131
+ var ICONO_INFO_EXPLICACION = lucideReact.Info;
132
+ var ICONO_COL_CODIGO = lucideReact.Hash;
133
+ var ICONO_COL_NOMBRE = lucideReact.Building2;
134
+ var ICONO_COL_ESTADO = lucideReact.Activity;
135
+ var ICONO_COL_MECANISMO = lucideReact.KeyRound;
136
+ var ICONO_COL_TOTAL = lucideReact.Wallet;
137
+ var ICONO_COL_FECHA = lucideReact.CalendarRange;
138
+ var ICONO_COL_FECHA_HORA = lucideReact.CalendarClock;
139
+ var ICONO_COL_ETIQUETA = lucideReact.Tag;
140
+ var ICONO_COL_USUARIO = lucideReact.User;
141
+ var ICONO_COL_DOCUMENTO = lucideReact.FileText;
142
+ var ICONO_PERIODO = lucideReact.CalendarRange;
143
+ var ICONO_VIGENCIA = lucideReact.Calendar;
144
+ var ICONO_DURACION = lucideReact.Timer;
145
+ var ICONO_UBICACION = lucideReact.FolderOpen;
146
+ var ICONOS_ESTATUS = {
147
+ pendiente: lucideReact.Circle,
148
+ procesando: lucideReact.Clock,
149
+ hecha: lucideReact.CheckCircle2,
150
+ gap: lucideReact.AlertTriangle,
151
+ fallida: lucideReact.XCircle
85
152
  };
86
- var sizing = {
87
- controlHeight: 32,
88
- buttonMinWidth: 110
153
+ function iconoDeEstatus(estado) {
154
+ return ICONOS_ESTATUS[estado] ?? ICONO_DEFAULT;
155
+ }
156
+ var ICONOS_SEMAFORO = {
157
+ verde: lucideReact.CheckCircle2,
158
+ ambar: lucideReact.AlertTriangle,
159
+ rojo: lucideReact.XCircle
89
160
  };
90
- var semantic = {
91
- colorPrimary: primitives.brandRed,
92
- colorPrimaryActive: primitives.brandRedActive,
93
- colorSuccess: primitives.green,
94
- colorInfo: primitives.purple,
95
- colorError: primitives.pink,
96
- colorWarning: primitives.yellow,
97
- // Links. Metronic inherits link color from the (red) primary, which reads as
98
- // "danger" in dense UIs. The AMS default uses a neutral dark gray so links
99
- // don't look like errors; consumers can override `colorLink`/`colorLinkHover`
100
- // via AmsConfigProvider's `theme` prop (e.g. back to the brand red or blue).
101
- colorLink: primitives.gray[800],
102
- colorLinkHover: primitives.brandRed,
103
- colorLinkActive: primitives.brandRedActive,
104
- colorBgLayout: primitives.background.app,
105
- colorBgContainer: primitives.background.body,
106
- borderRadius: radius.base,
107
- fontFamily: typography.fontFamily,
108
- fontSize: typography.fontSizeBase,
109
- controlHeight: sizing.controlHeight
161
+ var ETIQUETAS_SEMAFORO = {
162
+ verde: "verde (en linea)",
163
+ ambar: "ambar (advertencia)",
164
+ rojo: "rojo (critico)"
110
165
  };
166
+ function iconoDeSemaforo(color) {
167
+ return ICONOS_SEMAFORO[color] ?? lucideReact.Circle;
168
+ }
169
+ function etiquetaSemaforo(color) {
170
+ return ETIQUETAS_SEMAFORO[color] ?? color;
171
+ }
172
+ var ICONO_AVISO = lucideReact.AlertTriangle;
173
+ var ICONO_ERROR = lucideReact.XCircle;
174
+ var ICONO_OK = lucideReact.CheckCircle2;
175
+ var ICONO_KPI_RITMO = lucideReact.TrendingUp;
176
+ var ICONO_KPI_RENDIMIENTO = lucideReact.Gauge;
177
+ var ICONO_KPI_CONTEO = lucideReact.Boxes;
178
+ var ICONO_KPI_CREDENCIALES = lucideReact.KeyRound;
179
+ var ICONO_KPI_ALERTA = lucideReact.ShieldAlert;
180
+ var ICONO_KPI_CONSUMO = lucideReact.Coins;
181
+ var ICONO_KPI_BLOQUEO = lucideReact.Lock;
182
+ var ICONO_NAV_PLANES = lucideReact.Layers;
183
+ var ICONO_NAV_PAQUETES = lucideReact.Package;
184
+ var ICONO_NAV_ACCESOS = lucideReact.ShieldCheck;
185
+ var ICONO_NAV_USUARIOS = lucideReact.Users;
186
+ var ICONO_LOGIN_MICROSOFT = lucideReact.LogIn;
111
187
 
112
- // src/theme/theme.ts
113
- var sharedToken = {
114
- colorPrimary: semantic.colorPrimary,
115
- colorSuccess: semantic.colorSuccess,
116
- colorInfo: semantic.colorInfo,
117
- colorError: semantic.colorError,
118
- colorWarning: semantic.colorWarning,
119
- colorLink: semantic.colorLink,
120
- colorLinkHover: semantic.colorLinkHover,
121
- colorLinkActive: semantic.colorLinkActive,
122
- borderRadius: semantic.borderRadius,
123
- fontFamily: semantic.fontFamily,
124
- fontSize: semantic.fontSize,
125
- controlHeight: semantic.controlHeight
126
- };
127
- var lightToken = {
128
- ...sharedToken,
129
- colorBgLayout: semantic.colorBgLayout,
130
- colorBgContainer: semantic.colorBgContainer,
131
- colorBgElevated: primitives.background.body,
132
- colorText: primitives.gray[900],
133
- colorTextSecondary: primitives.gray[700],
134
- colorTextTertiary: primitives.gray[500],
135
- colorTextQuaternary: primitives.gray[400],
136
- colorBorder: primitives.gray[300],
137
- colorBorderSecondary: primitives.gray[200]
138
- };
139
- var darkToken = {
140
- ...sharedToken,
141
- colorBgLayout: primitives.background.appDark,
142
- colorBgContainer: primitives.background.bodyDark,
143
- colorBgElevated: primitives.background.bodyDark,
144
- colorText: "#ffffff",
145
- colorTextHeading: "#ffffff",
146
- colorTextSecondary: primitives.dark.gray[800],
147
- colorTextTertiary: primitives.dark.gray[700],
148
- colorTextQuaternary: primitives.dark.gray[600],
149
- colorBorder: primitives.dark.gray[300],
150
- colorBorderSecondary: primitives.dark.gray[200],
151
- boxShadow: "0 6px 16px 0 rgba(0, 0, 0, 0.48), 0 3px 6px -4px rgba(0, 0, 0, 0.36), 0 9px 28px 8px rgba(0, 0, 0, 0.15)"
152
- };
153
- var sharedComponents = {
154
- Button: { controlHeight: 32 },
155
- Tabs: {
156
- itemColor: primitives.gray[600],
157
- itemSelectedColor: semantic.colorPrimary,
158
- itemHoverColor: semantic.colorPrimary
159
- }
160
- };
161
- var lightComponents = {
162
- ...sharedComponents,
163
- Table: {
164
- headerBg: primitives.gray[100],
165
- headerColor: primitives.gray[800],
166
- borderColor: primitives.gray[300],
167
- cellPaddingBlock: 8,
168
- cellPaddingInline: 8
169
- }
170
- };
171
- var darkComponents = {
172
- ...sharedComponents,
173
- Table: {
174
- headerBg: primitives.dark.gray[200],
175
- headerColor: primitives.dark.gray[800],
176
- borderColor: primitives.dark.gray[400],
177
- rowHoverBg: primitives.dark.gray[200],
178
- cellPaddingBlock: 8,
179
- cellPaddingInline: 8
188
+ // src/kit/lib/explicaciones.ts
189
+ var EXPLICACIONES = {
190
+ // ───────────────────────────── plataforma.* (usados por los componentes del kit) ─────────────────────────────
191
+ "plataforma.rol": {
192
+ titulo: "Rol del usuario",
193
+ que_es: "Tu nivel de acceso en este sistema, por ejemplo administrador, operador o consulta.",
194
+ como_se_calcula: "Se resuelve al iniciar sesi\xF3n seg\xFAn tu asignaci\xF3n; no se calcula.",
195
+ de_donde_sale: "Asignaci\xF3n de rol del usuario en el directorio de acceso.",
196
+ como_leerlo: "Determina qu\xE9 m\xF3dulos ves y qu\xE9 acciones puedes ejecutar en cada tabla."
180
197
  },
181
- Input: {
182
- colorBgContainer: primitives.dark.inputBg,
183
- colorBorder: primitives.dark.gray[400],
184
- colorText: "#ffffff"
198
+ "plataforma.listado_resultados": {
199
+ titulo: "Resultados del listado",
200
+ que_es: "Cu\xE1ntas filas cumplen la b\xFAsqueda y los filtros actuales frente al total del listado.",
201
+ como_se_calcula: "Se cuentan las filas cuyo texto contiene lo buscado, sin distinguir may\xFAsculas ni acentos, y que cumplen cada filtro. La tabla muestra una p\xE1gina de 25, 50 o 100 de ellas.",
202
+ de_donde_sale: "Lista que ya trae la pantalla; la b\xFAsqueda, los filtros, el orden y la p\xE1gina viajan en la direcci\xF3n (URL).",
203
+ como_leerlo: "Si dice N en total no hay filtro activo. Copiar la direcci\xF3n comparte la misma vista."
185
204
  },
186
- Select: {
187
- colorBgContainer: primitives.dark.inputBg,
188
- colorBorder: primitives.dark.gray[400],
189
- colorText: "#ffffff"
205
+ "plataforma.tabla_columnas": {
206
+ titulo: "Columnas de la tabla",
207
+ que_es: "Lista para mostrar u ocultar las columnas de esta tabla que no est\xE1n fijas. Las columnas de identidad y la de acciones siempre se ven.",
208
+ como_se_calcula: "Cada casilla desmarcada oculta esa columna en pantalla; no cambia los datos, el orden, los filtros ni lo que se exporta.",
209
+ de_donde_sale: "Preferencia guardada en este navegador, por tabla y por usuario. Restablecer la borra y vuelve a mostrar todas las columnas con la densidad c\xF3moda.",
210
+ como_leerlo: "Si falta una columna que esperabas, revisa esta lista. Con el almacenamiento del navegador bloqueado (modo privado) la elecci\xF3n dura solo mientras la p\xE1gina siga abierta."
190
211
  },
191
- DatePicker: {
192
- colorBgContainer: primitives.dark.inputBg
212
+ "plataforma.tabla_densidad": {
213
+ titulo: "Densidad de la tabla",
214
+ que_es: "Alto de las filas: compacta muestra m\xE1s filas por pantalla; c\xF3moda deja m\xE1s aire entre ellas.",
215
+ como_se_calcula: "Compacta reduce el relleno vertical de cada celda; no cambia el texto ni las columnas.",
216
+ de_donde_sale: "Preferencia guardada en este navegador, por tabla y por usuario, junto con las columnas ocultas.",
217
+ como_leerlo: "La tabla se desplaza dentro de su propio marco: la barra horizontal queda al pie de la pantalla, el encabezado arriba y las columnas de identidad y acciones fijas a los lados. Shift + rueda del rat\xF3n desplaza a lo ancho."
193
218
  },
194
- Card: {
195
- colorBgContainer: primitives.dark.cardBg,
196
- colorBorderSecondary: primitives.dark.cardBg
219
+ // ───────────────────────────── ejemplos del Backoffice (examples/) ─────────────────────────────
220
+ "inicio.planes_activos": {
221
+ titulo: "Planes activos",
222
+ que_es: "N\xFAmero de planes del cat\xE1logo que hoy se pueden contratar.",
223
+ como_se_calcula: "Conteo de planes con estado Activo; los borradores y los archivados no cuentan.",
224
+ de_donde_sale: "Cat\xE1logo de planes del backoffice (ejemplo con datos ficticios).",
225
+ como_leerlo: "Un plan archivado deja de contar aunque tenga clientes vigentes; esos clientes conservan su plan hasta renovar."
197
226
  },
198
- Modal: {
199
- contentBg: primitives.dark.gray[100],
200
- headerBg: primitives.dark.gray[100]
227
+ "inicio.paquetes": {
228
+ titulo: "Paquetes",
229
+ que_es: "N\xFAmero de paquetes de consumo definidos en el cat\xE1logo.",
230
+ como_se_calcula: "Conteo de paquetes registrados, sin importar su estado.",
231
+ de_donde_sale: "Cat\xE1logo de paquetes del backoffice (ejemplo con datos ficticios).",
232
+ como_leerlo: "Un paquete puede estar asociado a varios planes; aqu\xED se cuenta una sola vez."
201
233
  },
202
- Drawer: {
203
- colorBgElevated: primitives.dark.gray[100]
234
+ "inicio.accesos_vigentes": {
235
+ titulo: "Accesos vigentes",
236
+ que_es: "Usuarios con acceso al backoffice que no est\xE1n suspendidos ni vencidos.",
237
+ como_se_calcula: "Conteo de usuarios con estado Activo y fecha de vencimiento futura o sin vencimiento.",
238
+ de_donde_sale: "Registro de accesos del backoffice (ejemplo con datos ficticios).",
239
+ como_leerlo: "Un usuario con rol de solo consulta cuenta igual que uno administrador."
204
240
  },
205
- Notification: {
206
- colorBgElevated: primitives.dark.gray[100]
241
+ "inicio.cobertura": {
242
+ titulo: "Cobertura del cat\xE1logo",
243
+ que_es: "Porcentaje de planes que ya tienen al menos un paquete asignado.",
244
+ como_se_calcula: "Planes con al menos un paquete entre planes activos, por 100.",
245
+ de_donde_sale: "Relaci\xF3n plan-paquete del cat\xE1logo (ejemplo con datos ficticios).",
246
+ como_leerlo: "Menos de 100 % indica planes sin paquete: no se pueden vender hasta que se les asigne uno."
207
247
  },
208
- Message: {
209
- contentBg: primitives.dark.gray[100]
248
+ "inicio.resumen_catalogo": {
249
+ titulo: "Resumen del cat\xE1logo",
250
+ que_es: "Planes agrupados por estado, con el total de paquetes que suman.",
251
+ como_se_calcula: "Cada plan cae en un solo estado; los paquetes de una fila son la suma de los paquetes de sus planes.",
252
+ de_donde_sale: "Cat\xE1logo de planes y paquetes del backoffice (ejemplo con datos ficticios).",
253
+ como_leerlo: "Un paquete compartido por dos planes se cuenta una vez por cada plan."
210
254
  },
211
- Menu: {
212
- itemBg: primitives.background.bodyDark,
213
- itemHoverBg: primitives.dark.gray[100],
214
- itemSelectedBg: primitives.dark.active,
215
- itemSelectedColor: semantic.colorPrimary,
216
- itemActiveBg: primitives.dark.active,
217
- subMenuItemBg: primitives.background.bodyDark
255
+ "planes.col_codigo": {
256
+ titulo: "C\xF3digo",
257
+ que_es: "Identificador corto y estable del plan.",
258
+ como_se_calcula: "Se captura al crear el plan; no se calcula ni se puede repetir.",
259
+ de_donde_sale: "Cat\xE1logo de planes (ejemplo con datos ficticios).",
260
+ como_leerlo: "Se usa en integraciones y reportes; el nombre comercial puede cambiar, el c\xF3digo no."
218
261
  },
219
- Dropdown: {
220
- colorBgElevated: primitives.background.bodyDark,
221
- controlItemBgHover: primitives.dark.gray[100]
262
+ "planes.col_nombre": {
263
+ titulo: "Nombre",
264
+ que_es: "Nombre comercial del plan tal como lo ve el cliente.",
265
+ como_se_calcula: "Se captura al crear o editar el plan; no se calcula.",
266
+ de_donde_sale: "Cat\xE1logo de planes (ejemplo con datos ficticios).",
267
+ como_leerlo: "La b\xFAsqueda ignora may\xFAsculas y acentos."
268
+ },
269
+ "planes.col_precio": {
270
+ titulo: "Precio mensual",
271
+ que_es: "Precio de lista del plan por mes, en pesos, sin impuestos.",
272
+ como_se_calcula: "Se captura al crear o editar el plan; no se calcula.",
273
+ de_donde_sale: "Cat\xE1logo de planes (ejemplo con datos ficticios).",
274
+ como_leerlo: "Es precio de lista: un contrato puede tener un descuento que no se refleja aqu\xED."
275
+ },
276
+ "planes.col_estado": {
277
+ titulo: "Estado del plan",
278
+ que_es: "Situaci\xF3n del plan en el cat\xE1logo: Activo, Borrador o Archivado.",
279
+ como_se_calcula: "Lo fija el administrador al publicar o archivar; no se calcula.",
280
+ de_donde_sale: "Cat\xE1logo de planes (ejemplo con datos ficticios).",
281
+ como_leerlo: "Solo los planes Activos se pueden contratar. Archivado conserva el historial pero no admite altas nuevas."
282
+ },
283
+ "planes.col_actualizado": {
284
+ titulo: "Actualizado",
285
+ que_es: "Fecha y hora de la \xFAltima modificaci\xF3n del plan, en tu hora local.",
286
+ como_se_calcula: "Se registra en UTC al guardar cambios y el navegador la convierte a tu zona horaria.",
287
+ de_donde_sale: "Bit\xE1cora de cambios del cat\xE1logo (ejemplo con datos ficticios).",
288
+ como_leerlo: "Cambiar solo el estado tambi\xE9n actualiza esta fecha."
289
+ },
290
+ "planes.estado_activo": {
291
+ titulo: "Activo",
292
+ que_es: "El plan est\xE1 publicado y se puede contratar.",
293
+ como_se_calcula: "Estado asignado al publicar el plan.",
294
+ de_donde_sale: "Cat\xE1logo de planes (ejemplo con datos ficticios).",
295
+ como_leerlo: "Para retirarlo del cat\xE1logo, arch\xEDvalo; no lo borres."
296
+ },
297
+ "planes.estado_borrador": {
298
+ titulo: "Borrador",
299
+ que_es: "El plan se est\xE1 preparando y a\xFAn no es visible para contratar.",
300
+ como_se_calcula: "Estado inicial de todo plan nuevo.",
301
+ de_donde_sale: "Cat\xE1logo de planes (ejemplo con datos ficticios).",
302
+ como_leerlo: "Publica el plan cuando tenga precio y al menos un paquete."
303
+ },
304
+ "planes.estado_archivado": {
305
+ titulo: "Archivado",
306
+ que_es: "El plan se retir\xF3 del cat\xE1logo; conserva su historial.",
307
+ como_se_calcula: "Estado asignado al archivar un plan activo.",
308
+ de_donde_sale: "Cat\xE1logo de planes (ejemplo con datos ficticios).",
309
+ como_leerlo: "No admite altas nuevas; los clientes existentes conservan su plan hasta renovar."
222
310
  }
223
311
  };
224
- function buildTheme(mode = "light") {
225
- const isDark = mode === "dark";
312
+ function explicacionDe(id) {
313
+ const entrada = EXPLICACIONES[id];
314
+ if (!entrada) throw new Error(`Explicaci\xF3n desconocida: ${id}`);
315
+ return entrada;
316
+ }
317
+ function verificarCatalogo() {
318
+ const problemas = [];
319
+ for (const [id, e] of Object.entries(EXPLICACIONES)) {
320
+ if (!/^[a-z0-9_]+\.[a-z0-9_]+$/.test(id)) problemas.push(`${id}: id invalido`);
321
+ for (const [campo, texto] of Object.entries(e)) {
322
+ if (texto.length > 320) problemas.push(`${id}.${campo}: mas de 320 caracteres`);
323
+ if (/["\\\n]/.test(texto)) problemas.push(`${id}.${campo}: comillas dobles, barra invertida o salto de linea`);
324
+ }
325
+ }
326
+ return problemas;
327
+ }
328
+
329
+ // src/kit/lib/listado.ts
330
+ var TAMANOS_LISTADO = [25, 50, 100];
331
+ var TAMANO_LISTADO_DEFAULT = 25;
332
+ function normalizarTexto(s) {
333
+ return s.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/\s+/g, " ").trim();
334
+ }
335
+ function primero(v) {
336
+ return (Array.isArray(v) ? v[0] : v) ?? "";
337
+ }
338
+ function leerListado(sp, opc) {
339
+ const pagina = Number.parseInt(primero(sp.pagina), 10);
340
+ const tamano = Number.parseInt(primero(sp.tamano), 10);
341
+ const orden = primero(sp.orden);
342
+ const dir = primero(sp.dir);
343
+ const filtros = {};
344
+ for (const clave of opc.filtros) {
345
+ const valor = primero(sp[clave]).trim();
346
+ if (valor) filtros[clave] = valor;
347
+ }
226
348
  return {
227
- algorithm: isDark ? antd.theme.darkAlgorithm : antd.theme.defaultAlgorithm,
228
- token: isDark ? darkToken : lightToken,
229
- components: isDark ? darkComponents : lightComponents
349
+ q: primero(sp.q).trim(),
350
+ pagina: Number.isFinite(pagina) && pagina >= 1 ? pagina : 1,
351
+ tamano: TAMANOS_LISTADO.includes(tamano) ? tamano : TAMANO_LISTADO_DEFAULT,
352
+ orden: opc.ordenes.includes(orden) ? orden : opc.ordenDefault,
353
+ dir: dir === "asc" || dir === "desc" ? dir : opc.dirDefault ?? "asc",
354
+ filtros
230
355
  };
231
356
  }
232
- var amsTheme = buildTheme("light");
233
- var amsThemeDark = buildTheme("dark");
234
- var STORAGE_KEY = "ams-theme";
235
- function getInitialThemeMode(defaultMode = "light") {
236
- if (typeof window === "undefined") return defaultMode;
237
- const saved = window.localStorage.getItem(STORAGE_KEY);
238
- if (saved === "light" || saved === "dark") return saved;
239
- if (window.matchMedia?.("(prefers-color-scheme: dark)").matches) return "dark";
240
- return defaultMode;
241
- }
242
- function applyThemeModeToDOM(mode) {
243
- if (typeof document === "undefined") return;
244
- document.documentElement.setAttribute("data-theme", mode);
245
- document.body?.setAttribute("data-theme", mode);
246
- }
247
- var ThemeModeContext = react.createContext(null);
248
- function useThemeMode() {
249
- const ctx = react.useContext(ThemeModeContext);
250
- if (!ctx) {
251
- throw new Error("useThemeMode must be used within an AmsConfigProvider");
252
- }
253
- return ctx;
357
+ function ausente(v) {
358
+ return v === void 0 || v === null || v === "" || typeof v === "number" && Number.isNaN(v);
254
359
  }
255
- function useThemeModeState(controlledMode, defaultMode, persist) {
256
- const isControlled = controlledMode !== void 0;
257
- const [internalMode, setInternalMode] = react.useState(
258
- () => isControlled ? controlledMode : persist ? getInitialThemeMode(defaultMode) : defaultMode
259
- );
260
- const mode = isControlled ? controlledMode : internalMode;
261
- react.useEffect(() => {
262
- applyThemeModeToDOM(mode);
263
- }, [mode]);
264
- const setMode = react.useCallback(
265
- (next) => {
266
- if (!isControlled) {
267
- setInternalMode(next);
268
- if (persist && typeof window !== "undefined") {
269
- window.localStorage.setItem(STORAGE_KEY, next);
270
- }
271
- }
272
- },
273
- [isControlled, persist]
360
+ function aplicarListado(items, estado, def) {
361
+ const aguja = normalizarTexto(estado.q);
362
+ const filtrados = items.filter(
363
+ (it) => (!aguja || normalizarTexto(def.texto(it)).includes(aguja)) && (!def.filtrar || def.filtrar(it, estado.filtros))
274
364
  );
275
- const toggle = react.useCallback(() => {
276
- setMode(mode === "light" ? "dark" : "light");
277
- }, [mode, setMode]);
278
- return react.useMemo(
279
- () => ({ mode, setMode, toggle, isDark: mode === "dark" }),
280
- [mode, setMode, toggle]
281
- );
282
- }
283
- function mergeTheme(base, override) {
284
- if (!override) return base;
365
+ const valor = def.valoresOrden[estado.orden];
366
+ if (valor) {
367
+ const factor = estado.dir === "asc" ? 1 : -1;
368
+ filtrados.sort((a, b) => {
369
+ const va = valor(a);
370
+ const vb = valor(b);
371
+ if (ausente(va) && ausente(vb)) return 0;
372
+ if (ausente(va)) return 1;
373
+ if (ausente(vb)) return -1;
374
+ if (typeof va === "number" && typeof vb === "number") return (va - vb) * factor;
375
+ return String(va).localeCompare(String(vb), "es", { sensitivity: "base" }) * factor;
376
+ });
377
+ }
378
+ const totalPaginas = Math.max(1, Math.ceil(filtrados.length / estado.tamano));
379
+ const pagina = Math.min(estado.pagina, totalPaginas);
380
+ const inicio = (pagina - 1) * estado.tamano;
285
381
  return {
286
- ...base,
287
- ...override,
288
- token: { ...base.token, ...override.token },
289
- components: { ...base.components, ...override.components },
290
- // A caller-provided algorithm wins; otherwise keep the mode's algorithm.
291
- algorithm: override.algorithm ?? base.algorithm
382
+ itemsPagina: filtrados.slice(inicio, inicio + estado.tamano),
383
+ totalFiltrado: filtrados.length,
384
+ total: items.length,
385
+ pagina,
386
+ totalPaginas
292
387
  };
293
388
  }
294
- function AmsConfigProvider({
295
- mode,
296
- defaultMode = "light",
297
- persistMode = true,
298
- locale,
299
- theme,
300
- children
301
- }) {
302
- const themeMode = useThemeModeState(mode, defaultMode, persistMode);
303
- const resolvedTheme = mergeTheme(buildTheme(themeMode.mode), theme);
304
- return /* @__PURE__ */ jsxRuntime.jsx(ThemeModeContext.Provider, { value: themeMode, children: /* @__PURE__ */ jsxRuntime.jsx(antd.ConfigProvider, { theme: resolvedTheme, locale, children: /* @__PURE__ */ jsxRuntime.jsx(antd.App, { children }) }) });
389
+ function hrefListado(ruta, actual, cambios) {
390
+ const params = new URLSearchParams(actual);
391
+ if (!("pagina" in cambios)) params.delete("pagina");
392
+ for (const [clave, valor] of Object.entries(cambios)) {
393
+ if (valor === null || valor === void 0 || valor === "" || clave === "pagina" && valor === "1") {
394
+ params.delete(clave);
395
+ } else {
396
+ params.set(clave, valor);
397
+ }
398
+ }
399
+ const qs = params.toString();
400
+ return qs ? `${ruta}?${qs}` : ruta;
401
+ }
402
+
403
+ // src/kit/lib/formato.ts
404
+ var NO_DISPONIBLE = "\u2014";
405
+ var DECIMAL = /^(-?)(\d+)(?:\.(\d+))?$/;
406
+ function partes(texto) {
407
+ const m = DECIMAL.exec(texto.trim());
408
+ return m ? { signo: m[1], entero: m[2], fraccion: m[3] ?? "" } : null;
409
+ }
410
+ var miles = (entero) => entero.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
411
+ function formatoMxn(valor) {
412
+ if (valor == null) return NO_DISPONIBLE;
413
+ const p = partes(valor);
414
+ if (!p) return valor;
415
+ return `${p.signo}$${miles(p.entero)}${p.fraccion ? `.${p.fraccion}` : ""}`;
416
+ }
417
+ function formatoNumero(valor) {
418
+ if (valor == null) return NO_DISPONIBLE;
419
+ const p = partes(valor);
420
+ if (!p) return valor;
421
+ return `${p.signo}${miles(p.entero)}${p.fraccion ? `.${p.fraccion}` : ""}`;
422
+ }
423
+ function formatoEntero(valor) {
424
+ if (valor == null) return NO_DISPONIBLE;
425
+ return formatoNumero(String(Math.trunc(valor)));
426
+ }
427
+ function formatoPct(razon) {
428
+ if (razon == null) return NO_DISPONIBLE;
429
+ const p = partes(razon);
430
+ if (!p) return razon;
431
+ const fraccion = p.fraccion.padEnd(4, "0");
432
+ const entero = (p.entero + fraccion.slice(0, 2)).replace(/^0+(?=\d)/, "");
433
+ const resto = fraccion.slice(2);
434
+ const decimales = resto.length > 2 ? resto : resto.padEnd(2, "0");
435
+ return `${p.signo}${miles(entero)}.${decimales} %`;
436
+ }
437
+ function formatoDias(valor) {
438
+ return valor == null ? NO_DISPONIBLE : `${formatoNumero(valor)} d\xEDas`;
439
+ }
440
+ function formatoFecha(iso) {
441
+ if (!iso) return NO_DISPONIBLE;
442
+ return iso.slice(0, 10).replace(/-/g, "/");
443
+ }
444
+ function formatoMes(mes) {
445
+ return mes ? mes.slice(0, 7).replace(/-/g, "/") : NO_DISPONIBLE;
446
+ }
447
+
448
+ // src/kit/lib/fecha.ts
449
+ function formatearFechaHora(iso) {
450
+ if (!iso) return "\u2014";
451
+ const tieneZona = /(Z|[+-]\d{2}:?\d{2})$/.test(iso);
452
+ if (tieneZona && /T/.test(iso)) {
453
+ const d = new Date(iso);
454
+ if (!Number.isNaN(d.getTime())) {
455
+ const p = (n) => String(n).padStart(2, "0");
456
+ return `${d.getFullYear()}/${p(d.getMonth() + 1)}/${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
457
+ }
458
+ }
459
+ const conHora = /T/.test(iso) ? iso : `${iso}T00:00:00`;
460
+ const m = conHora.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/);
461
+ if (!m) return iso;
462
+ const [, anio, mes, dia, hora, min, seg] = m;
463
+ return `${anio}/${mes}/${dia} ${hora}:${min}:${seg}`;
305
464
  }
306
- var Button = react.forwardRef(
307
- function Button2(props, ref) {
308
- return /* @__PURE__ */ jsxRuntime.jsx(antd.Button, { ref, ...props });
465
+ function formatearFechaHoraCDMX(iso) {
466
+ if (!iso) return "\u2014";
467
+ if (/(Z|[+-]\d{2}:?\d{2})$/.test(iso) && /T/.test(iso)) {
468
+ const d = new Date(iso);
469
+ if (!Number.isNaN(d.getTime())) {
470
+ const partes2 = Object.fromEntries(
471
+ new Intl.DateTimeFormat("en-CA", {
472
+ timeZone: "America/Mexico_City",
473
+ year: "numeric",
474
+ month: "2-digit",
475
+ day: "2-digit",
476
+ hour: "2-digit",
477
+ minute: "2-digit",
478
+ second: "2-digit",
479
+ hourCycle: "h23"
480
+ }).formatToParts(d).map((x) => [x.type, x.value])
481
+ );
482
+ return `${partes2.year}/${partes2.month}/${partes2.day} ${partes2.hour}:${partes2.minute}:${partes2.second}`;
483
+ }
309
484
  }
310
- );
311
- var INTENT_COLOR = {
312
- success: semantic.colorSuccess,
313
- error: semantic.colorError,
314
- warning: semantic.colorWarning,
315
- info: semantic.colorInfo,
316
- primary: semantic.colorPrimary
317
- };
318
- var Tag = react.forwardRef(function Tag2({ intent, color, ...rest }, ref) {
319
- const resolvedColor = intent ? INTENT_COLOR[intent] : color;
320
- return /* @__PURE__ */ jsxRuntime.jsx(antd.Tag, { ref, color: resolvedColor, ...rest });
321
- });
322
- function useAmsMessage() {
323
- return antd.App.useApp().message;
324
- }
325
- function useAmsNotification() {
326
- return antd.App.useApp().notification;
327
- }
328
- function buildLocale(labels) {
329
- if (!labels) return void 0;
330
- const locale = {};
331
- if (labels.itemsPerPage !== void 0) locale.items_per_page = labels.itemsPerPage;
332
- if (labels.jumpTo !== void 0) locale.jump_to = labels.jumpTo;
333
- if (labels.jumpToConfirm !== void 0) locale.jump_to_confirm = labels.jumpToConfirm;
334
- if (labels.page !== void 0) locale.page = labels.page;
335
- if (labels.prevPage !== void 0) locale.prev_page = labels.prevPage;
336
- if (labels.nextPage !== void 0) locale.next_page = labels.nextPage;
337
- if (labels.pageSize !== void 0) locale.page_size = labels.pageSize;
338
- return Object.keys(locale).length > 0 ? locale : void 0;
339
- }
340
- function Pagination({
341
- labels,
342
- showTotal,
343
- itemRender,
344
- locale,
345
- ...rest
485
+ return formatearFechaHora(iso);
486
+ }
487
+ function formatearRangoFechaHora(desde, hasta) {
488
+ return `${formatearFechaHora(desde)} \u2014 ${formatearFechaHora(hasta)}`;
489
+ }
490
+
491
+ // src/kit/lib/bytes.ts
492
+ function formatearBytes(n) {
493
+ if (n === null || n === void 0 || !Number.isFinite(n) || n < 0) return "\u2014";
494
+ if (n < 1024) return `${n} B`;
495
+ const unidades = ["KB", "MB", "GB", "TB"];
496
+ let valor = n / 1024;
497
+ let i = 0;
498
+ while (valor >= 1024 && i < unidades.length - 1) {
499
+ valor /= 1024;
500
+ i += 1;
501
+ }
502
+ return `${valor.toFixed(1)} ${unidades[i]}`;
503
+ }
504
+ function SpinnerMarca({
505
+ tamano = 120,
506
+ etiqueta = "Cargando..."
346
507
  }) {
347
- const mergedLocale = { ...buildLocale(labels), ...locale };
348
- const resolvedShowTotal = showTotal ?? (labels?.total ? (total, range) => labels.total(total, range) : labels?.totalText ? (total, range) => {
349
- const { prefix, suffix, showRange } = labels.totalText;
350
- const middle = showRange ? `${range[0]}-${range[1]}` : String(total);
351
- return [prefix, middle, suffix].filter(Boolean).join(" ");
352
- } : void 0);
353
- const resolvedItemRender = itemRender ?? (labels?.previousText || labels?.nextText ? (_page, type, element) => {
354
- if (type === "prev" && labels?.previousText !== void 0) return labels.previousText;
355
- if (type === "next" && labels?.nextText !== void 0) return labels.nextText;
356
- return element;
357
- } : void 0);
358
- return /* @__PURE__ */ jsxRuntime.jsx(
359
- antd.Pagination,
508
+ const alto = Math.round(tamano * 52 / 120);
509
+ return /* @__PURE__ */ jsxRuntime.jsxs(
510
+ "div",
360
511
  {
361
- ...rest,
362
- locale: Object.keys(mergedLocale).length > 0 ? mergedLocale : void 0,
363
- showTotal: resolvedShowTotal,
364
- itemRender: resolvedItemRender
512
+ className: "inline-flex flex-col items-center gap-2",
513
+ role: "status",
514
+ "aria-live": "polite",
515
+ "aria-label": etiqueta,
516
+ children: [
517
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "spinner-marca", style: { width: tamano, height: alto }, children: [
518
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "spinner-marca__base", "aria-hidden": "true" }),
519
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "spinner-marca__fill", "aria-hidden": "true" })
520
+ ] }),
521
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-[#807f7f]", children: etiqueta })
522
+ ]
365
523
  }
366
524
  );
367
525
  }
368
- var Card = antd.Card;
369
- function ThemeToggle(props) {
370
- const { isDark, toggle } = useThemeMode();
526
+ function OverlayCargando({
527
+ etiqueta = "Procesando...",
528
+ visible = true
529
+ }) {
530
+ if (!visible) return null;
371
531
  return /* @__PURE__ */ jsxRuntime.jsx(
372
- antd.Switch,
532
+ "div",
373
533
  {
374
- checked: isDark,
375
- onChange: toggle,
376
- checkedChildren: /* @__PURE__ */ jsxRuntime.jsx(icons.BulbFilled, {}),
377
- unCheckedChildren: /* @__PURE__ */ jsxRuntime.jsx(icons.BulbOutlined, {}),
378
- ...props
534
+ className: "fixed inset-0 z-[60] flex items-center justify-center bg-white/70 backdrop-blur-[1px]",
535
+ role: "alertdialog",
536
+ "aria-busy": "true",
537
+ "aria-label": etiqueta,
538
+ children: /* @__PURE__ */ jsxRuntime.jsx(SpinnerMarca, { tamano: 140, etiqueta })
379
539
  }
380
540
  );
381
541
  }
382
- function buildPaginationLocale(labels) {
383
- if (!labels) return void 0;
384
- const locale = {};
385
- if (labels.itemsPerPage !== void 0) locale.items_per_page = labels.itemsPerPage;
386
- if (labels.jumpTo !== void 0) locale.jump_to = labels.jumpTo;
387
- if (labels.jumpToConfirm !== void 0) locale.jump_to_confirm = labels.jumpToConfirm;
388
- if (labels.page !== void 0) locale.page = labels.page;
389
- if (labels.prevPage !== void 0) locale.prev_page = labels.prevPage;
390
- if (labels.nextPage !== void 0) locale.next_page = labels.nextPage;
391
- if (labels.pageSize !== void 0) locale.page_size = labels.pageSize;
392
- return Object.keys(locale).length > 0 ? locale : void 0;
393
- }
394
- var DEFAULT_LABELS = {
395
- emptyText: "No data",
396
- sortAsc: "Sort ascending",
397
- sortDesc: "Sort descending",
398
- cancelSort: "Cancel sort"
399
- };
400
- function toNumericValue(value) {
401
- if (value === null || value === void 0 || value === "") return null;
402
- const num = typeof value === "string" ? parseFloat(value.replace(/,/g, "")) : Number(value);
403
- return Number.isNaN(num) || !Number.isFinite(num) ? null : num;
404
- }
405
- function buildTotalText(labels) {
406
- if (!labels) return void 0;
407
- if (labels.total) return (total, range) => labels.total(total, range);
408
- if (labels.totalText) {
409
- return (total, range) => {
410
- const { prefix, suffix, showRange } = labels.totalText;
411
- const middle = showRange ? `${range[0]}-${range[1]}` : String(total);
412
- return [prefix, middle, suffix].filter(Boolean).join(" ");
542
+ function useAccionConSpinner() {
543
+ const [ejecutando, setEjecutando] = react.useState(false);
544
+ const [etiqueta, setEtiqueta] = react.useState("Procesando...");
545
+ const ejecutar = react.useCallback(
546
+ async (fn, etiquetaAccion = "Procesando...") => {
547
+ setEtiqueta(etiquetaAccion);
548
+ setEjecutando(true);
549
+ try {
550
+ return await fn();
551
+ } finally {
552
+ setEjecutando(false);
553
+ }
554
+ },
555
+ []
556
+ );
557
+ const Overlay = react.useCallback(
558
+ () => /* @__PURE__ */ jsxRuntime.jsx(OverlayCargando, { visible: ejecutando, etiqueta }),
559
+ [ejecutando, etiqueta]
560
+ );
561
+ return { ejecutando, ejecutar, Overlay };
562
+ }
563
+
564
+ // src/kit/lib/preferenciasTabla.ts
565
+ var VACIAS = { orden: [], anchos: {}, ocultas: [] };
566
+ function esNavegador() {
567
+ return typeof window !== "undefined" && typeof window.localStorage !== "undefined";
568
+ }
569
+ function cargarPreferencias(clave) {
570
+ if (!esNavegador()) return { ...VACIAS };
571
+ const crudo = window.localStorage.getItem(clave);
572
+ if (!crudo) return { ...VACIAS };
573
+ try {
574
+ const p = JSON.parse(crudo);
575
+ return {
576
+ orden: Array.isArray(p.orden) ? p.orden : [],
577
+ anchos: p.anchos && typeof p.anchos === "object" ? p.anchos : {},
578
+ ocultas: Array.isArray(p.ocultas) ? p.ocultas : []
413
579
  };
580
+ } catch {
581
+ return { ...VACIAS };
582
+ }
583
+ }
584
+ function guardarPreferencias(clave, prefs) {
585
+ if (!esNavegador()) return;
586
+ try {
587
+ window.localStorage.setItem(clave, JSON.stringify(prefs));
588
+ } catch {
414
589
  }
415
- return void 0;
416
590
  }
417
- function buildItemRender(labels) {
418
- if (!labels || labels.previousText === void 0 && labels.nextText === void 0) {
419
- return void 0;
591
+ function borrarPreferencias(clave) {
592
+ if (!esNavegador()) return;
593
+ window.localStorage.removeItem(clave);
594
+ }
595
+ function ordenarColumnas(idsDefault, ordenGuardado) {
596
+ if (ordenGuardado.length === 0) return [...idsDefault];
597
+ const disponibles = new Set(idsDefault);
598
+ const delGuardado = ordenGuardado.filter((id) => disponibles.has(id));
599
+ const yaColocadas = new Set(delGuardado);
600
+ const nuevas = idsDefault.filter((id) => !yaColocadas.has(id));
601
+ return [...delGuardado, ...nuevas];
602
+ }
603
+ function moverColumna(ids, desde, hasta) {
604
+ if (desde < 0 || desde >= ids.length || hasta < 0 || hasta >= ids.length || desde === hasta) {
605
+ return [...ids];
420
606
  }
421
- return (_page, type, element) => {
422
- if (type === "prev" && labels.previousText !== void 0) return labels.previousText;
423
- if (type === "next" && labels.nextText !== void 0) return labels.nextText;
424
- return element;
607
+ const copia = [...ids];
608
+ const [movido] = copia.splice(desde, 1);
609
+ copia.splice(hasta, 0, movido);
610
+ return copia;
611
+ }
612
+
613
+ // src/kit/lib/useTablaPreferencias.ts
614
+ function useTablaPreferencias(clave, idsDefault) {
615
+ const [prefs, setPrefs] = react.useState({ orden: [], anchos: {}, ocultas: [] });
616
+ react.useEffect(() => {
617
+ setPrefs(cargarPreferencias(clave));
618
+ }, [clave]);
619
+ const persistir = react.useCallback(
620
+ (nuevas) => {
621
+ setPrefs(nuevas);
622
+ guardarPreferencias(clave, nuevas);
623
+ },
624
+ [clave]
625
+ );
626
+ const idsOrdenados = ordenarColumnas(idsDefault, prefs.orden);
627
+ const ocultas = new Set(prefs.ocultas);
628
+ const idsVisibles = idsOrdenados.filter((id) => !ocultas.has(id));
629
+ const mover = react.useCallback(
630
+ (desde, hasta) => {
631
+ const base = ordenarColumnas(idsDefault, prefs.orden);
632
+ const nuevoOrden = moverColumna(base, desde, hasta);
633
+ persistir({ ...prefs, orden: nuevoOrden });
634
+ },
635
+ [idsDefault, prefs, persistir]
636
+ );
637
+ const redimensionar = react.useCallback(
638
+ (id, ancho) => {
639
+ const w = Math.max(60, Math.round(ancho));
640
+ persistir({ ...prefs, anchos: { ...prefs.anchos, [id]: w } });
641
+ },
642
+ [prefs, persistir]
643
+ );
644
+ const alternarVisible = react.useCallback(
645
+ (id) => {
646
+ const set = new Set(prefs.ocultas);
647
+ if (set.has(id)) set.delete(id);
648
+ else set.add(id);
649
+ persistir({ ...prefs, ocultas: [...set] });
650
+ },
651
+ [prefs, persistir]
652
+ );
653
+ const restablecer = react.useCallback(() => {
654
+ borrarPreferencias(clave);
655
+ setPrefs({ orden: [], anchos: {}, ocultas: [] });
656
+ }, [clave]);
657
+ return {
658
+ /** Ids en el orden efectivo, SIN las ocultas — para pintar la tabla. */
659
+ idsVisibles,
660
+ /** Ids en el orden efectivo, incluyendo ocultas — para el menu de mostrar/ocultar. */
661
+ idsOrdenados,
662
+ /** Set de ids ocultos. */
663
+ ocultas,
664
+ /** Ancho persistido de una columna, o undefined si no se ha redimensionado. */
665
+ anchoDe: (id) => prefs.anchos[id],
666
+ mover,
667
+ redimensionar,
668
+ alternarVisible,
669
+ restablecer
425
670
  };
426
671
  }
427
- function DataTable({
428
- heading,
429
- extra,
430
- loading = false,
431
- labels,
432
- paginationLabels,
433
- firstColumnNumericTag = false,
434
- columns,
435
- dataSource,
436
- scroll,
437
- bordered = true,
438
- pagination,
439
- className,
440
- ...rest
672
+
673
+ // src/kit/lib/posicionPopover.ts
674
+ function calcularPosicionPopover({
675
+ trigger,
676
+ contenido,
677
+ viewport,
678
+ margen = 8,
679
+ separacion = 4,
680
+ anchoPreferido = 360
441
681
  }) {
442
- const mergedLabels = { ...DEFAULT_LABELS, ...labels };
443
- const resolvedPagination = pagination === false || pagination === void 0 ? pagination : (() => {
444
- const paginationLocale = buildPaginationLocale(paginationLabels);
445
- const totalFn = buildTotalText(paginationLabels);
446
- const itemRenderFn = buildItemRender(paginationLabels);
447
- return {
448
- ...totalFn ? { showTotal: totalFn } : {},
449
- ...itemRenderFn ? { itemRender: itemRenderFn } : {},
450
- ...paginationLocale ? { locale: paginationLocale } : {},
451
- ...pagination
682
+ const anchoMax = Math.min(anchoPreferido, viewport.ancho - 2 * margen);
683
+ const ancho = Math.min(contenido.ancho, anchoMax);
684
+ const left = Math.max(margen, Math.min(trigger.left, viewport.ancho - ancho - margen));
685
+ const espacioAbajo = viewport.alto - trigger.bottom - separacion - margen;
686
+ const espacioArriba = trigger.top - separacion - margen;
687
+ const abajo = contenido.alto <= espacioAbajo || espacioAbajo >= espacioArriba;
688
+ const disponible = Math.max(0, abajo ? espacioAbajo : espacioArriba);
689
+ const alto = Math.min(contenido.alto, disponible);
690
+ const top = abajo ? trigger.bottom + separacion : trigger.top - separacion - alto;
691
+ return { top, left, maxHeight: disponible, anchoMax };
692
+ }
693
+
694
+ // src/kit/ui/clases.ts
695
+ var CLASE_BOTON_PRIMARIO = "inline-flex items-center gap-1.5 rounded-md bg-[var(--tema-texto)] px-4 py-2 text-sm font-medium text-white transition hover:brightness-125 disabled:opacity-50";
696
+ var CLASE_BOTON_PRIMARIO_COMPACTO = "inline-flex items-center gap-1.5 rounded-md bg-[var(--tema-texto)] px-3 py-1.5 text-sm font-medium text-white transition hover:brightness-125 disabled:opacity-50";
697
+ var CLASE_BOTON_SECUNDARIO = "inline-flex items-center gap-1.5 rounded-md border border-[var(--tema-borde)] px-3 py-1.5 text-sm font-medium text-[#3c3c3b] transition hover:bg-[var(--tema-badge-neutro)] disabled:opacity-50";
698
+ var CLASE_BOTON_PEQUENO = "inline-flex items-center gap-1.5 rounded-md border border-[var(--tema-borde)] px-2.5 py-1.5 text-xs font-medium text-[#3c3c3b] transition hover:bg-[var(--tema-badge-neutro)] disabled:opacity-50";
699
+ var CLASE_BOTON_CONTORNO = "rounded-md border border-[var(--tema-primario)] px-3 py-1 text-xs font-medium text-[var(--tema-primario)] transition hover:bg-[#eaf2fb]";
700
+ var CLASE_BOTON_ICONO = "rounded-md p-1 text-[#807f7f] transition hover:bg-[var(--tema-badge-neutro)]";
701
+ var CLASE_BOTON_PELIGRO = "inline-flex items-center gap-1.5 rounded-md border border-[var(--tema-peligro)] px-3 py-1.5 text-sm font-medium text-[var(--tema-peligro)] transition hover:bg-[var(--tema-badge-error)] disabled:opacity-50";
702
+ var CLASE_CAMPO = "rounded-md border border-[var(--tema-borde)] bg-[var(--tema-fondo)] px-3 py-2 text-sm text-[#3c3c3b] focus:border-[var(--tema-primario)] focus:outline-none disabled:bg-[#f4f8fc] disabled:text-[#807f7f]";
703
+ var CLASE_CAMPO_COMPACTO = "rounded-md border border-[var(--tema-borde)] bg-[var(--tema-fondo)] px-2 py-1.5 text-sm text-[#3c3c3b] focus:border-[var(--tema-primario)] focus:outline-none";
704
+ var CLASE_ETIQUETA_CAMPO = "text-sm font-medium text-[#3c3c3b]";
705
+ var CLASE_AYUDA_CAMPO = "text-xs text-[#807f7f]";
706
+ var CLASE_ERROR_CAMPO = "text-sm text-[var(--tema-peligro)]";
707
+ var CLASE_EXITO_CAMPO = "text-sm text-[#1e7a34]";
708
+ var CLASE_TARJETA = "rounded-lg border border-[var(--tema-borde)] p-4";
709
+ var CLASE_TITULO_PAGINA = "flex items-center gap-2 text-xl font-semibold text-[var(--tema-primario-oscuro)]";
710
+ var CLASE_TITULO_SECCION = "text-sm font-semibold text-[var(--tema-primario-oscuro)]";
711
+ var CLASE_TEXTO_ATENUADO = "text-sm text-[#807f7f]";
712
+ function PopoverHover({
713
+ etiqueta,
714
+ disparador,
715
+ children
716
+ }) {
717
+ const [abierto, setAbierto] = react.useState(false);
718
+ const [pos, setPos] = react.useState(null);
719
+ const botonRef = react.useRef(null);
720
+ const contenidoRef = react.useRef(null);
721
+ const idContenido = react.useId();
722
+ const reposicionar = react.useCallback(() => {
723
+ const trigger = botonRef.current?.getBoundingClientRect();
724
+ if (!trigger) return;
725
+ const medida = contenidoRef.current?.getBoundingClientRect();
726
+ setPos(
727
+ calcularPosicionPopover({
728
+ trigger,
729
+ contenido: { ancho: medida?.width ?? 0, alto: medida?.height ?? 0 },
730
+ viewport: { ancho: window.innerWidth, alto: window.innerHeight }
731
+ })
732
+ );
733
+ }, []);
734
+ function abrir() {
735
+ setAbierto(true);
736
+ }
737
+ react.useLayoutEffect(() => {
738
+ if (!abierto) {
739
+ setPos(null);
740
+ return;
741
+ }
742
+ reposicionar();
743
+ window.addEventListener("scroll", reposicionar, true);
744
+ window.addEventListener("resize", reposicionar);
745
+ return () => {
746
+ window.removeEventListener("scroll", reposicionar, true);
747
+ window.removeEventListener("resize", reposicionar);
452
748
  };
453
- })();
454
- const resolvedColumns = firstColumnNumericTag && columns && columns.length > 0 ? columns.map((col, index) => {
455
- if (index !== 0) return col;
456
- const originalRender = col.render;
457
- return {
458
- ...col,
459
- render: (value, record, i) => {
460
- const content = originalRender ? originalRender(value, record, i) : value;
461
- const numeric = toNumericValue(value);
462
- if (numeric === null) return content;
463
- return /* @__PURE__ */ jsxRuntime.jsx(
464
- Tag,
749
+ }, [abierto, reposicionar]);
750
+ react.useEffect(() => {
751
+ if (!abierto) return;
752
+ function alTeclear(e) {
753
+ if (e.key === "Escape") setAbierto(false);
754
+ }
755
+ document.addEventListener("keydown", alTeclear);
756
+ return () => document.removeEventListener("keydown", alTeclear);
757
+ }, [abierto]);
758
+ return /* @__PURE__ */ jsxRuntime.jsxs(
759
+ "span",
760
+ {
761
+ className: "inline-flex",
762
+ onMouseEnter: abrir,
763
+ onMouseLeave: () => setAbierto(false),
764
+ children: [
765
+ /* @__PURE__ */ jsxRuntime.jsx(
766
+ "button",
465
767
  {
466
- intent: numeric < 0 ? "error" : "success",
467
- style: { minWidth: 72, textAlign: "center" },
468
- children: content
768
+ ref: botonRef,
769
+ type: "button",
770
+ "aria-label": etiqueta,
771
+ title: etiqueta,
772
+ "aria-expanded": abierto,
773
+ "aria-describedby": abierto ? idContenido : void 0,
774
+ onFocus: abrir,
775
+ onBlur: () => setAbierto(false),
776
+ className: "inline-flex items-center justify-center text-[#807f7f] transition hover:text-[var(--tema-primario)]",
777
+ children: disparador
469
778
  }
470
- );
471
- }
472
- };
473
- }) : columns;
474
- const resolvedScroll = scroll ?? { x: "max-content" };
475
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
476
- heading ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: 8 }, children: /* @__PURE__ */ jsxRuntime.jsx("h3", { style: { margin: 0, fontWeight: 600 }, children: heading }) }) : null,
477
- extra ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: 8 }, children: extra }) : null,
478
- loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(antd.Skeleton, { active: true, paragraph: { rows: 0 }, title: { width: "100%" } }, i)) }) : /* @__PURE__ */ jsxRuntime.jsx(
479
- antd.Table,
779
+ ),
780
+ abierto && /* @__PURE__ */ jsxRuntime.jsx(
781
+ "span",
782
+ {
783
+ ref: contenidoRef,
784
+ id: idContenido,
785
+ role: "tooltip",
786
+ style: {
787
+ position: "fixed",
788
+ top: pos?.top ?? 0,
789
+ left: pos?.left ?? 0,
790
+ // Antes de medir (pos null) se oculta para no parpadear en (0,0).
791
+ visibility: pos ? "visible" : "hidden",
792
+ maxWidth: pos?.anchoMax ?? "min(360px, calc(100vw - 16px))",
793
+ maxHeight: pos?.maxHeight,
794
+ overflowY: "auto"
795
+ },
796
+ className: "z-50 w-max rounded-md border border-[var(--tema-borde)] bg-white p-3 text-left shadow-lg",
797
+ children
798
+ }
799
+ )
800
+ ]
801
+ }
802
+ );
803
+ }
804
+ var PREGUNTAS = [
805
+ ["Qu\xE9 es", "que_es"],
806
+ ["C\xF3mo se calcula", "como_se_calcula"],
807
+ ["De d\xF3nde sale", "de_donde_sale"],
808
+ ["C\xF3mo leerlo", "como_leerlo"]
809
+ ];
810
+ function InfoExplicacion({ id }) {
811
+ const e = explicacionDe(id);
812
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { "data-explicacion-id": id, className: "inline-flex shrink-0 align-middle", children: /* @__PURE__ */ jsxRuntime.jsx(
813
+ PopoverHover,
814
+ {
815
+ etiqueta: `Explicaci\xF3n: ${e.titulo}`,
816
+ disparador: /* @__PURE__ */ jsxRuntime.jsx(ICONO_INFO_EXPLICACION, { size: 14, "aria-hidden": "true", className: "text-[#9a9999]" }),
817
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "block max-w-[18rem] text-xs font-normal normal-case tracking-normal text-[#3c3c3b]", children: [
818
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block font-semibold text-[var(--tema-primario-oscuro)]", children: e.titulo }),
819
+ PREGUNTAS.map(([pregunta, campo]) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mt-1.5 block", children: [
820
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block font-medium text-[#565656]", children: pregunta }),
821
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block leading-snug", children: e[campo] })
822
+ ] }, campo))
823
+ ] })
824
+ }
825
+ ) });
826
+ }
827
+ var CLASES = {
828
+ exito: "bg-[var(--tema-badge-exito)] text-[#1e6b34]",
829
+ error: "bg-[var(--tema-badge-error)] text-[var(--tema-peligro)]",
830
+ aviso: "bg-[var(--tema-badge-advertencia)] text-[#8a5a00]",
831
+ neutro: "bg-[var(--tema-badge-neutro)] text-[#565656]"
832
+ };
833
+ function Insignia({
834
+ tono,
835
+ children,
836
+ info,
837
+ icono: Icono
838
+ }) {
839
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 align-middle", children: [
840
+ /* @__PURE__ */ jsxRuntime.jsxs(
841
+ "span",
480
842
  {
481
- columns: resolvedColumns,
482
- dataSource,
483
- bordered,
484
- scroll: resolvedScroll,
485
- pagination: resolvedPagination,
486
- locale: {
487
- emptyText: mergedLabels.emptyText,
488
- triggerAsc: mergedLabels.sortAsc,
489
- triggerDesc: mergedLabels.sortDesc,
490
- cancelSort: mergedLabels.cancelSort
491
- },
492
- ...rest
843
+ className: `${Icono ? "inline-flex items-center gap-1" : "inline-block"} rounded-full px-2 py-0.5 text-xs font-medium ${CLASES[tono]}`,
844
+ children: [
845
+ Icono && /* @__PURE__ */ jsxRuntime.jsx(Icono, { size: 12, "aria-hidden": "true" }),
846
+ children
847
+ ]
493
848
  }
494
- )
849
+ ),
850
+ info && /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: info })
495
851
  ] });
496
852
  }
497
- function useControllableState({
498
- value,
499
- defaultValue,
500
- onChange
853
+ function Tarjeta({
854
+ titulo,
855
+ children,
856
+ nota,
857
+ info
501
858
  }) {
502
- const isControlled = value !== void 0;
503
- const [internal, setInternal] = react.useState(defaultValue);
504
- const current = isControlled ? value : internal;
505
- const setValue = react.useCallback(
506
- (next) => {
507
- if (!isControlled) {
508
- setInternal(next);
509
- }
510
- onChange?.(next);
511
- },
512
- [isControlled, onChange]
513
- );
514
- return [current, setValue];
859
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "rounded-lg border border-[var(--tema-borde)] p-5", children: [
860
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
861
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold text-[var(--tema-primario-oscuro)]", children: titulo }),
862
+ info && /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: info })
863
+ ] }),
864
+ nota && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-[#807f7f]", children: nota }),
865
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3", children })
866
+ ] });
515
867
  }
516
- function isPromise(value) {
517
- return !!value && typeof value.then === "function";
868
+ function Metrica({
869
+ etiqueta,
870
+ valor,
871
+ info
872
+ }) {
873
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-[var(--tema-borde)] px-4 py-3", children: [
874
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
875
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-[#807f7f]", children: etiqueta }),
876
+ info && /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: info })
877
+ ] }),
878
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-lg font-semibold text-[var(--tema-primario-oscuro)]", children: valor })
879
+ ] });
518
880
  }
519
- function ModalDialog({
520
- trigger,
881
+ function ThInfo({
882
+ info,
521
883
  children,
522
- open,
523
- defaultOpen = false,
524
- closeOnOk = true,
525
- closeOnCancel = true,
526
- onOk,
527
- onCancel,
528
- onOpenChange,
529
- ...modalProps
884
+ className,
885
+ derecha,
886
+ scope,
887
+ atributos
530
888
  }) {
531
- const [isOpen, setOpen] = useControllableState({
532
- value: open,
533
- defaultValue: defaultOpen,
534
- onChange: onOpenChange
535
- });
536
- const handleOpen = react.useCallback(() => setOpen(true), [setOpen]);
537
- const handleCancel = react.useCallback(
538
- (event) => {
539
- onCancel?.(event);
540
- if (closeOnCancel) setOpen(false);
541
- },
542
- [onCancel, closeOnCancel, setOpen]
543
- );
544
- const handleOk = react.useCallback(
545
- async (event) => {
546
- const result = onOk?.(event);
547
- if (!closeOnOk) return;
548
- if (isPromise(result)) {
549
- try {
550
- await result;
551
- } finally {
552
- setOpen(false);
553
- }
554
- } else {
555
- setOpen(false);
556
- }
557
- },
558
- [onOk, closeOnOk, setOpen]
559
- );
560
- const triggerNode = react.useMemo(() => {
561
- if (!trigger) return null;
562
- if (typeof trigger === "function") {
563
- return trigger(handleOpen);
564
- }
565
- if (react.isValidElement(trigger)) {
566
- const el = trigger;
567
- const originalOnClick = el.props.onClick;
568
- return react.cloneElement(el, {
569
- onClick: (event) => {
570
- originalOnClick?.(event);
571
- handleOpen();
572
- }
573
- });
574
- }
575
- return trigger;
576
- }, [trigger, handleOpen]);
577
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
578
- triggerNode,
579
- /* @__PURE__ */ jsxRuntime.jsx(antd.Modal, { open: isOpen, onOk: handleOk, onCancel: handleCancel, ...modalProps, children })
580
- ] });
889
+ return /* @__PURE__ */ jsxRuntime.jsx("th", { scope, ...atributos, className, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `inline-flex items-center gap-1 ${derecha ? "justify-end" : ""}`, children: [
890
+ children,
891
+ /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: info })
892
+ ] }) });
581
893
  }
582
- var normalize = (value) => (value ?? "").toString().normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
583
- var extractText = (node) => {
584
- if (node === null || node === void 0) return "";
585
- if (typeof node === "string" || typeof node === "number" || typeof node === "boolean") {
586
- return String(node);
587
- }
588
- if (Array.isArray(node)) return node.map(extractText).join(" ");
589
- if (typeof node === "object" && "props" in node) {
590
- const props = node.props;
591
- return extractText(props?.children);
592
- }
593
- return "";
894
+ var TIPOS = {
895
+ error: "rounded-md bg-[var(--tema-badge-error)] px-3 py-2 text-sm text-[var(--tema-peligro)]",
896
+ advertencia: "rounded-md bg-[var(--tema-badge-advertencia)] px-3 py-2 text-xs text-[#8a5a00]",
897
+ neutro: "rounded-lg bg-[var(--tema-badge-neutro)] px-3 py-2 text-sm text-[#565656]",
898
+ exito: "rounded-md bg-[var(--tema-badge-exito)] px-3 py-2 text-sm text-[#1e7a34]"
594
899
  };
595
- function FilterSelect({
596
- options,
597
- showSearch = false,
598
- filterOption,
599
- className,
600
- style,
601
- ...rest
900
+ function Aviso({
901
+ tipo,
902
+ titulo,
903
+ children,
904
+ info
602
905
  }) {
603
- const defaultFilter = (input, option) => {
604
- if (!option) return false;
605
- const query = normalize(input);
606
- const label = normalize(extractText(option.label));
607
- const value = normalize(typeof option.value === "string" ? option.value : "");
608
- return label.includes(query) || value.includes(query);
609
- };
610
- const resolvedFilter = showSearch && !filterOption ? defaultFilter : filterOption;
906
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: tipo === "error" ? "alert" : "status", className: TIPOS[tipo], children: [
907
+ titulo && /* @__PURE__ */ jsxRuntime.jsxs("strong", { className: "font-semibold", children: [
908
+ titulo,
909
+ " "
910
+ ] }),
911
+ children,
912
+ info && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 align-middle", children: /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: info }) })
913
+ ] });
914
+ }
915
+ function EstadoVacio({ children }) {
916
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "rounded-lg border border-dashed border-[var(--tema-borde)] px-4 py-6 text-sm text-[#807f7f]", children });
917
+ }
918
+ function EstadoVacioFila({ columnas, children = "Sin resultados" }) {
919
+ return /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan: columnas, className: "px-3 py-6 text-center text-sm text-[#807f7f]", children }) });
920
+ }
921
+ function NoDisponible({ motivo, info }) {
922
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-dashed border-[var(--tema-borde)] px-4 py-3 text-sm text-[#565656]", children: [
923
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "flex items-center gap-1 font-medium", children: [
924
+ "No disponible",
925
+ /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: info })
926
+ ] }),
927
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-[#807f7f]", children: motivo })
928
+ ] });
929
+ }
930
+ function BarraProgreso({ porcentaje, etiqueta }) {
611
931
  return /* @__PURE__ */ jsxRuntime.jsx(
612
- antd.Select,
932
+ "div",
613
933
  {
614
- options,
615
- showSearch,
616
- filterOption: resolvedFilter,
617
- optionFilterProp: "label",
618
- className,
619
- style: { width: "100%", ...style },
620
- ...rest
934
+ role: "progressbar",
935
+ "aria-label": etiqueta,
936
+ "aria-valuemin": 0,
937
+ "aria-valuemax": 100,
938
+ "aria-valuenow": porcentaje,
939
+ className: "h-1.5 w-full overflow-hidden rounded-full bg-[var(--tema-borde)]",
940
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full rounded-full bg-[var(--tema-primario)] transition-all", style: { width: `${porcentaje}%` } })
621
941
  }
622
942
  );
623
943
  }
624
- var resolveMessage = (template, fileName, fallback) => {
625
- if (!template) return fallback.replace("{file}", fileName);
626
- return typeof template === "function" ? template(fileName) : template.replace("{file}", fileName);
627
- };
628
- function FileUploadDragger({
629
- successMessage,
630
- errorMessage,
631
- showStatusMessages = true,
632
- hintText = "Click or drag a file to this area to upload",
633
- onUploadSuccess,
634
- onUploadError,
635
- onChange: externalOnChange,
636
- children,
637
- ...uploadProps
944
+ function useAccionAviso({
945
+ ejecutar,
946
+ errorGenerico,
947
+ onHecho
638
948
  }) {
639
- const message = useAmsMessage();
640
- const mergedProps = react.useMemo(
641
- () => ({
642
- ...uploadProps,
643
- onChange(info) {
644
- const { status, name: fileName } = info.file;
645
- if (showStatusMessages) {
646
- if (status === "done") {
647
- message.success(resolveMessage(successMessage, fileName, `${fileName} uploaded successfully.`));
648
- onUploadSuccess?.(info);
649
- } else if (status === "error") {
650
- message.error(resolveMessage(errorMessage, fileName, `${fileName} failed to upload.`));
651
- onUploadError?.(info);
652
- }
653
- }
654
- externalOnChange?.(info);
949
+ const [ejecutando, setEjecutando] = react.useState(false);
950
+ const [aviso, setAviso] = react.useState(null);
951
+ async function correr() {
952
+ setEjecutando(true);
953
+ setAviso(null);
954
+ let resultado;
955
+ try {
956
+ resultado = await ejecutar();
957
+ } catch {
958
+ resultado = { tipo: "error", texto: errorGenerico };
959
+ }
960
+ setAviso(resultado);
961
+ setEjecutando(false);
962
+ if (resultado.tipo === "ok") onHecho?.();
963
+ }
964
+ return { ejecutando, aviso, correr };
965
+ }
966
+ function AvisoAccionEstado({
967
+ ejecutando,
968
+ aviso,
969
+ etiquetaSpinner
970
+ }) {
971
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
972
+ ejecutando && /* @__PURE__ */ jsxRuntime.jsx(SpinnerMarca, { tamano: 32, etiqueta: etiquetaSpinner }),
973
+ aviso && /* @__PURE__ */ jsxRuntime.jsx(
974
+ "span",
975
+ {
976
+ role: aviso.tipo === "ok" ? "status" : "alert",
977
+ className: aviso.tipo === "ok" ? "text-xs text-[var(--tema-primario-oscuro)]" : "text-xs text-[var(--tema-peligro)]",
978
+ children: aviso.texto
979
+ }
980
+ )
981
+ ] });
982
+ }
983
+ function BotonAccionAviso({
984
+ etiqueta,
985
+ etiquetaEjecutando,
986
+ etiquetaSpinner,
987
+ ariaLabel,
988
+ info,
989
+ errorGenerico,
990
+ ejecutar,
991
+ onHecho
992
+ }) {
993
+ const { ejecutando, aviso, correr } = useAccionAviso({ ejecutar, errorGenerico, onHecho });
994
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex flex-wrap items-center gap-1.5", children: [
995
+ /* @__PURE__ */ jsxRuntime.jsx(
996
+ "button",
997
+ {
998
+ type: "button",
999
+ onClick: correr,
1000
+ disabled: ejecutando,
1001
+ "aria-label": ariaLabel,
1002
+ className: "rounded-md border border-[var(--tema-borde)] px-2 py-0.5 text-xs font-medium text-[var(--tema-primario-oscuro)] transition hover:bg-[var(--tema-badge-neutro)] disabled:opacity-50",
1003
+ children: ejecutando ? etiquetaEjecutando : etiqueta
655
1004
  }
656
- }),
657
- [uploadProps, showStatusMessages, message, successMessage, errorMessage, onUploadSuccess, onUploadError, externalOnChange]
1005
+ ),
1006
+ /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: info }),
1007
+ /* @__PURE__ */ jsxRuntime.jsx(AvisoAccionEstado, { ejecutando, aviso, etiquetaSpinner })
1008
+ ] });
1009
+ }
1010
+ function EnlaceNav({
1011
+ href,
1012
+ icono,
1013
+ etiqueta
1014
+ }) {
1015
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1016
+ LinkBase,
1017
+ {
1018
+ href,
1019
+ "aria-label": etiqueta,
1020
+ title: etiqueta,
1021
+ className: "pulsable flex items-center gap-2.5 rounded-md px-2 py-2 transition hover:bg-[var(--tema-badge-neutro)] hover:text-[var(--tema-primario)] md:group-data-[colapsado=true]/side:justify-center",
1022
+ children: [
1023
+ icono,
1024
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "md:group-data-[colapsado=true]/side:hidden", children: etiqueta })
1025
+ ]
1026
+ }
658
1027
  );
659
- return /* @__PURE__ */ jsxRuntime.jsx(antd.Upload.Dragger, { ...mergedProps, children: children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
660
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ant-upload-drag-icon", children: /* @__PURE__ */ jsxRuntime.jsx(icons.InboxOutlined, {}) }),
661
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ant-upload-text", children: hintText })
662
- ] }) });
663
1028
  }
664
- function PageHeader({
665
- title,
666
- breadcrumbs,
667
- separator = ">",
668
- uppercaseTitle = true,
669
- extra,
670
- className
1029
+ function SeccionNav({ titulo, children }) {
1030
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
1031
+ titulo && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-2 pb-1 text-[10px] font-semibold uppercase tracking-wider text-[#9a9a9a] md:group-data-[colapsado=true]/side:hidden", children: titulo }),
1032
+ children
1033
+ ] });
1034
+ }
1035
+
1036
+ // src/kit/lib/panelPrefs.ts
1037
+ function leerPref(clave, porDefecto) {
1038
+ try {
1039
+ const crudo = window.localStorage.getItem(clave);
1040
+ return crudo === null ? porDefecto : JSON.parse(crudo);
1041
+ } catch {
1042
+ return porDefecto;
1043
+ }
1044
+ }
1045
+ function guardarPref(clave, valor) {
1046
+ try {
1047
+ window.localStorage.setItem(clave, JSON.stringify(valor));
1048
+ } catch {
1049
+ }
1050
+ }
1051
+ var CLAVE = "sidebar:colapsado";
1052
+ function SidebarColapsable({
1053
+ cabecera,
1054
+ pie,
1055
+ children
671
1056
  }) {
672
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { marginBottom: 32 }, children: [
1057
+ const [colapsado, setColapsado] = react.useState(false);
1058
+ const [abiertoMovil, setAbiertoMovil] = react.useState(false);
1059
+ react.useEffect(() => {
1060
+ setColapsado(leerPref(CLAVE, false) === true);
1061
+ }, []);
1062
+ react.useEffect(() => {
1063
+ if (!abiertoMovil) return;
1064
+ const alTeclear = (e) => {
1065
+ if (e.key === "Escape") setAbiertoMovil(false);
1066
+ };
1067
+ document.addEventListener("keydown", alTeclear);
1068
+ return () => document.removeEventListener("keydown", alTeclear);
1069
+ }, [abiertoMovil]);
1070
+ function alternar() {
1071
+ const nuevo = !colapsado;
1072
+ setColapsado(nuevo);
1073
+ guardarPref(CLAVE, nuevo);
1074
+ }
1075
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1076
+ /* @__PURE__ */ jsxRuntime.jsx(
1077
+ "button",
1078
+ {
1079
+ type: "button",
1080
+ onClick: () => setAbiertoMovil(true),
1081
+ "aria-label": "Abrir men\xFA",
1082
+ title: "Abrir men\xFA",
1083
+ className: "fixed left-3 top-3 z-30 flex h-9 w-9 items-center justify-center rounded-md border border-[var(--tema-borde)] bg-[var(--tema-fondo)] text-[#565656] shadow-sm md:hidden",
1084
+ children: /* @__PURE__ */ jsxRuntime.jsx(ICONO_NAV_MENU_MOVIL, { size: TAMANO_BOTON, "aria-hidden": "true" })
1085
+ }
1086
+ ),
1087
+ abiertoMovil && /* @__PURE__ */ jsxRuntime.jsx(
1088
+ "button",
1089
+ {
1090
+ type: "button",
1091
+ "aria-label": "Cerrar men\xFA",
1092
+ onClick: () => setAbiertoMovil(false),
1093
+ className: "fixed inset-0 z-30 bg-black/40 md:hidden"
1094
+ }
1095
+ ),
673
1096
  /* @__PURE__ */ jsxRuntime.jsxs(
674
- "div",
1097
+ "aside",
675
1098
  {
676
- style: {
677
- display: "flex",
678
- alignItems: "center",
679
- justifyContent: "space-between",
680
- gap: 16
1099
+ "data-colapsado": colapsado ? "true" : "false",
1100
+ "data-abierto-movil": abiertoMovil ? "true" : "false",
1101
+ onClick: (e) => {
1102
+ if (e.target.closest("a")) setAbiertoMovil(false);
681
1103
  },
1104
+ className: `group/side fixed inset-y-0 left-0 z-40 flex w-60 shrink-0 flex-col border-r border-[var(--tema-borde)] bg-[var(--tema-fondo)] transition-transform md:sticky md:top-0 md:h-screen md:translate-x-0 md:transition-[width] md:data-[colapsado=true]:w-14 ${abiertoMovil ? "translate-x-0" : "-translate-x-full"}`,
682
1105
  children: [
683
- /* @__PURE__ */ jsxRuntime.jsx(
684
- "h1",
685
- {
686
- style: {
687
- fontSize: 20,
688
- fontWeight: 700,
689
- margin: 0,
690
- textTransform: uppercaseTitle ? "uppercase" : void 0
691
- },
692
- children: title
693
- }
694
- ),
695
- extra ? /* @__PURE__ */ jsxRuntime.jsx("div", { children: extra }) : null
1106
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 border-b border-[var(--tema-borde)] px-4 py-4 md:group-data-[colapsado=true]/side:items-center md:group-data-[colapsado=true]/side:px-1", children: [
1107
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "md:group-data-[colapsado=true]/side:hidden", children: cabecera }),
1108
+ /* @__PURE__ */ jsxRuntime.jsx(
1109
+ "button",
1110
+ {
1111
+ type: "button",
1112
+ onClick: alternar,
1113
+ "aria-label": colapsado ? "Expandir men\xFA" : "Contraer men\xFA",
1114
+ title: colapsado ? "Expandir men\xFA" : "Contraer men\xFA",
1115
+ "aria-expanded": !colapsado,
1116
+ className: "hidden w-fit items-center rounded-md p-1 text-[#807f7f] transition hover:bg-[var(--tema-badge-neutro)] hover:text-[var(--tema-primario)] md:flex",
1117
+ children: colapsado ? /* @__PURE__ */ jsxRuntime.jsx(ICONO_NAV_EXPANDIR, { size: TAMANO_BOTON, "aria-hidden": "true" }) : /* @__PURE__ */ jsxRuntime.jsx(ICONO_NAV_COLAPSAR, { size: TAMANO_BOTON, "aria-hidden": "true" })
1118
+ }
1119
+ ),
1120
+ /* @__PURE__ */ jsxRuntime.jsx(
1121
+ "button",
1122
+ {
1123
+ type: "button",
1124
+ onClick: () => setAbiertoMovil(false),
1125
+ "aria-label": "Cerrar men\xFA lateral",
1126
+ className: "absolute right-2 top-2 rounded-md p-1 text-[#807f7f] md:hidden",
1127
+ children: /* @__PURE__ */ jsxRuntime.jsx(ICONO_CERRAR, { size: TAMANO_BOTON, "aria-hidden": "true" })
1128
+ }
1129
+ )
1130
+ ] }),
1131
+ /* @__PURE__ */ jsxRuntime.jsx("nav", { className: "flex flex-1 flex-col gap-6 overflow-y-auto px-3 py-4 text-sm font-medium text-[#565656] md:group-data-[colapsado=true]/side:px-1.5", children }),
1132
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-[var(--tema-borde)] px-4 py-3 md:group-data-[colapsado=true]/side:px-1", children: pie })
696
1133
  ]
697
1134
  }
698
- ),
699
- breadcrumbs && breadcrumbs.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
700
- antd.Breadcrumb,
701
- {
702
- separator,
703
- items: breadcrumbs,
704
- style: { fontSize: 13, marginTop: 8 }
705
- }
706
- ) : null
1135
+ )
707
1136
  ] });
708
1137
  }
709
- var DownloadButton = react.forwardRef(function DownloadButton2({ label = "CSV", icon, type = "primary", children, ...rest }, ref) {
710
- return /* @__PURE__ */ jsxRuntime.jsx(Button, { ref, type, icon: icon ?? /* @__PURE__ */ jsxRuntime.jsx(icons.DownloadOutlined, {}), ...rest, children: children ?? label });
711
- });
712
- function ConfirmModal({
713
- message,
714
- danger = false,
715
- okButtonProps,
716
- children,
717
- ...rest
1138
+
1139
+ // src/kit/lib/tablaMarco.ts
1140
+ var PREFS_MARCO_DEFAULT = { ocultas: [], densidad: "comoda" };
1141
+ var ID_SEGURO = /^[A-Za-z0-9_-]+$/;
1142
+ function hashCorto(texto) {
1143
+ let h = 2166136261;
1144
+ for (let i = 0; i < texto.length; i++) {
1145
+ h ^= texto.charCodeAt(i);
1146
+ h = Math.imul(h, 16777619) >>> 0;
1147
+ }
1148
+ return h.toString(36);
1149
+ }
1150
+ function claveMarco(tabla, usuario) {
1151
+ return `tabla.marco.v1.${tabla}.${usuario ? hashCorto(usuario) : "anon"}`;
1152
+ }
1153
+ function cargarPrefsMarco(clave) {
1154
+ try {
1155
+ if (typeof window === "undefined") return { ...PREFS_MARCO_DEFAULT };
1156
+ const crudo = window.localStorage.getItem(clave);
1157
+ if (!crudo) return { ...PREFS_MARCO_DEFAULT };
1158
+ const p = JSON.parse(crudo);
1159
+ return {
1160
+ ocultas: Array.isArray(p.ocultas) ? p.ocultas.filter((x) => typeof x === "string") : [],
1161
+ densidad: p.densidad === "compacta" ? "compacta" : "comoda"
1162
+ };
1163
+ } catch {
1164
+ return { ...PREFS_MARCO_DEFAULT };
1165
+ }
1166
+ }
1167
+ function guardarPrefsMarco(clave, prefs) {
1168
+ try {
1169
+ window.localStorage.setItem(clave, JSON.stringify(prefs));
1170
+ } catch {
1171
+ }
1172
+ }
1173
+ function borrarPrefsMarco(clave) {
1174
+ try {
1175
+ window.localStorage.removeItem(clave);
1176
+ } catch {
1177
+ }
1178
+ }
1179
+ function cssColumnasOcultas(alcance, ocultas) {
1180
+ const seguras = ocultas.filter((id) => ID_SEGURO.test(id));
1181
+ if (seguras.length === 0) return "";
1182
+ const selectores = seguras.map((id) => `[data-marco-tabla="${alcance}"] [data-col="${id}"]`);
1183
+ return `${selectores.join(",")}{display:none}`;
1184
+ }
1185
+ function offsetsAcumulados(anchos) {
1186
+ const offsets = [];
1187
+ let total = 0;
1188
+ for (const ancho of anchos) {
1189
+ offsets.push(total);
1190
+ total += ancho;
1191
+ }
1192
+ return { offsets, total };
1193
+ }
1194
+ var ContextoTablaPlana = react.createContext(false);
1195
+ function TablaPlana({ children }) {
1196
+ return /* @__PURE__ */ jsxRuntime.jsx(ContextoTablaPlana.Provider, { value: true, children });
1197
+ }
1198
+ var ContextoUsuarioTablas = react.createContext(null);
1199
+ function UsuarioTablas({ usuario, children }) {
1200
+ return /* @__PURE__ */ jsxRuntime.jsx(ContextoUsuarioTablas.Provider, { value: usuario ?? null, children });
1201
+ }
1202
+ var CLASE_BOTON = "inline-flex items-center gap-1.5 rounded-md border border-[var(--tema-borde)] px-2 py-1 text-xs font-medium text-[#3c3c3b] transition hover:bg-[var(--tema-badge-neutro)]";
1203
+ function MarcoTabla({
1204
+ clave,
1205
+ etiqueta,
1206
+ columnas,
1207
+ barra,
1208
+ alto,
1209
+ ajuste = "acotado",
1210
+ pie,
1211
+ controles = true,
1212
+ className = "",
1213
+ clasePlana = "overflow-x-auto print:overflow-visible",
1214
+ children
718
1215
  }) {
1216
+ const plana = react.useContext(ContextoTablaPlana);
1217
+ if (plana)
1218
+ return pie ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1219
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: clasePlana, children }),
1220
+ pie
1221
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: clasePlana, children });
719
1222
  return /* @__PURE__ */ jsxRuntime.jsx(
720
- ModalDialog,
1223
+ MarcoActivo,
721
1224
  {
722
- okButtonProps: { danger, ...okButtonProps },
723
- ...rest,
724
- children: children ?? (message ? /* @__PURE__ */ jsxRuntime.jsx("p", { children: message }) : null)
1225
+ clave,
1226
+ etiqueta,
1227
+ columnas,
1228
+ barra,
1229
+ alto,
1230
+ ajuste,
1231
+ pie,
1232
+ controles,
1233
+ className,
1234
+ children
725
1235
  }
726
1236
  );
727
1237
  }
728
-
729
- Object.defineProperty(exports, "Affix", {
730
- enumerable: true,
731
- get: function () { return antd.Affix; }
732
- });
733
- Object.defineProperty(exports, "Alert", {
734
- enumerable: true,
735
- get: function () { return antd.Alert; }
736
- });
737
- Object.defineProperty(exports, "Anchor", {
738
- enumerable: true,
739
- get: function () { return antd.Anchor; }
740
- });
741
- Object.defineProperty(exports, "AutoComplete", {
742
- enumerable: true,
743
- get: function () { return antd.AutoComplete; }
744
- });
745
- Object.defineProperty(exports, "Avatar", {
746
- enumerable: true,
747
- get: function () { return antd.Avatar; }
748
- });
749
- Object.defineProperty(exports, "BackTop", {
750
- enumerable: true,
751
- get: function () { return antd.BackTop; }
752
- });
753
- Object.defineProperty(exports, "Badge", {
754
- enumerable: true,
755
- get: function () { return antd.Badge; }
756
- });
757
- Object.defineProperty(exports, "Breadcrumb", {
758
- enumerable: true,
759
- get: function () { return antd.Breadcrumb; }
760
- });
761
- Object.defineProperty(exports, "Calendar", {
762
- enumerable: true,
763
- get: function () { return antd.Calendar; }
764
- });
765
- Object.defineProperty(exports, "Carousel", {
766
- enumerable: true,
767
- get: function () { return antd.Carousel; }
768
- });
769
- Object.defineProperty(exports, "Cascader", {
770
- enumerable: true,
771
- get: function () { return antd.Cascader; }
772
- });
773
- Object.defineProperty(exports, "Checkbox", {
774
- enumerable: true,
775
- get: function () { return antd.Checkbox; }
776
- });
777
- Object.defineProperty(exports, "Col", {
778
- enumerable: true,
779
- get: function () { return antd.Col; }
780
- });
781
- Object.defineProperty(exports, "Collapse", {
782
- enumerable: true,
783
- get: function () { return antd.Collapse; }
784
- });
785
- Object.defineProperty(exports, "ColorPicker", {
786
- enumerable: true,
787
- get: function () { return antd.ColorPicker; }
788
- });
789
- Object.defineProperty(exports, "DatePicker", {
790
- enumerable: true,
791
- get: function () { return antd.DatePicker; }
792
- });
793
- Object.defineProperty(exports, "Descriptions", {
794
- enumerable: true,
795
- get: function () { return antd.Descriptions; }
796
- });
797
- Object.defineProperty(exports, "Divider", {
798
- enumerable: true,
799
- get: function () { return antd.Divider; }
800
- });
801
- Object.defineProperty(exports, "Drawer", {
802
- enumerable: true,
803
- get: function () { return antd.Drawer; }
804
- });
805
- Object.defineProperty(exports, "Dropdown", {
806
- enumerable: true,
807
- get: function () { return antd.Dropdown; }
808
- });
809
- Object.defineProperty(exports, "Empty", {
810
- enumerable: true,
811
- get: function () { return antd.Empty; }
812
- });
813
- Object.defineProperty(exports, "Flex", {
814
- enumerable: true,
815
- get: function () { return antd.Flex; }
816
- });
817
- Object.defineProperty(exports, "FloatButton", {
818
- enumerable: true,
819
- get: function () { return antd.FloatButton; }
820
- });
821
- Object.defineProperty(exports, "Form", {
822
- enumerable: true,
823
- get: function () { return antd.Form; }
824
- });
825
- Object.defineProperty(exports, "Image", {
826
- enumerable: true,
827
- get: function () { return antd.Image; }
828
- });
829
- Object.defineProperty(exports, "Input", {
830
- enumerable: true,
831
- get: function () { return antd.Input; }
832
- });
833
- Object.defineProperty(exports, "InputNumber", {
834
- enumerable: true,
835
- get: function () { return antd.InputNumber; }
836
- });
837
- Object.defineProperty(exports, "Layout", {
838
- enumerable: true,
839
- get: function () { return antd.Layout; }
840
- });
841
- Object.defineProperty(exports, "List", {
842
- enumerable: true,
843
- get: function () { return antd.List; }
844
- });
845
- Object.defineProperty(exports, "Mentions", {
846
- enumerable: true,
847
- get: function () { return antd.Mentions; }
848
- });
849
- Object.defineProperty(exports, "Menu", {
850
- enumerable: true,
851
- get: function () { return antd.Menu; }
852
- });
853
- Object.defineProperty(exports, "Modal", {
854
- enumerable: true,
855
- get: function () { return antd.Modal; }
856
- });
857
- Object.defineProperty(exports, "Popconfirm", {
858
- enumerable: true,
859
- get: function () { return antd.Popconfirm; }
860
- });
861
- Object.defineProperty(exports, "Popover", {
862
- enumerable: true,
863
- get: function () { return antd.Popover; }
864
- });
865
- Object.defineProperty(exports, "Progress", {
866
- enumerable: true,
867
- get: function () { return antd.Progress; }
868
- });
869
- Object.defineProperty(exports, "QRCode", {
870
- enumerable: true,
871
- get: function () { return antd.QRCode; }
872
- });
873
- Object.defineProperty(exports, "Radio", {
874
- enumerable: true,
875
- get: function () { return antd.Radio; }
876
- });
877
- Object.defineProperty(exports, "Rate", {
878
- enumerable: true,
879
- get: function () { return antd.Rate; }
880
- });
881
- Object.defineProperty(exports, "Result", {
882
- enumerable: true,
883
- get: function () { return antd.Result; }
884
- });
885
- Object.defineProperty(exports, "Row", {
886
- enumerable: true,
887
- get: function () { return antd.Row; }
888
- });
889
- Object.defineProperty(exports, "Segmented", {
890
- enumerable: true,
891
- get: function () { return antd.Segmented; }
892
- });
893
- Object.defineProperty(exports, "Select", {
894
- enumerable: true,
895
- get: function () { return antd.Select; }
896
- });
897
- Object.defineProperty(exports, "Skeleton", {
898
- enumerable: true,
899
- get: function () { return antd.Skeleton; }
900
- });
901
- Object.defineProperty(exports, "Slider", {
902
- enumerable: true,
903
- get: function () { return antd.Slider; }
904
- });
905
- Object.defineProperty(exports, "Space", {
906
- enumerable: true,
907
- get: function () { return antd.Space; }
908
- });
909
- Object.defineProperty(exports, "Spin", {
910
- enumerable: true,
911
- get: function () { return antd.Spin; }
912
- });
913
- Object.defineProperty(exports, "Splitter", {
914
- enumerable: true,
915
- get: function () { return antd.Splitter; }
916
- });
917
- Object.defineProperty(exports, "Statistic", {
918
- enumerable: true,
919
- get: function () { return antd.Statistic; }
920
- });
921
- Object.defineProperty(exports, "Steps", {
922
- enumerable: true,
923
- get: function () { return antd.Steps; }
924
- });
925
- Object.defineProperty(exports, "Switch", {
926
- enumerable: true,
927
- get: function () { return antd.Switch; }
928
- });
929
- Object.defineProperty(exports, "Table", {
930
- enumerable: true,
931
- get: function () { return antd.Table; }
932
- });
933
- Object.defineProperty(exports, "Tabs", {
934
- enumerable: true,
935
- get: function () { return antd.Tabs; }
936
- });
937
- Object.defineProperty(exports, "TimePicker", {
938
- enumerable: true,
939
- get: function () { return antd.TimePicker; }
940
- });
941
- Object.defineProperty(exports, "Timeline", {
942
- enumerable: true,
943
- get: function () { return antd.Timeline; }
944
- });
945
- Object.defineProperty(exports, "Tooltip", {
946
- enumerable: true,
947
- get: function () { return antd.Tooltip; }
948
- });
949
- Object.defineProperty(exports, "Tour", {
950
- enumerable: true,
951
- get: function () { return antd.Tour; }
952
- });
953
- Object.defineProperty(exports, "Transfer", {
954
- enumerable: true,
955
- get: function () { return antd.Transfer; }
956
- });
957
- Object.defineProperty(exports, "Tree", {
958
- enumerable: true,
959
- get: function () { return antd.Tree; }
960
- });
961
- Object.defineProperty(exports, "TreeSelect", {
962
- enumerable: true,
963
- get: function () { return antd.TreeSelect; }
964
- });
965
- Object.defineProperty(exports, "Typography", {
966
- enumerable: true,
967
- get: function () { return antd.Typography; }
968
- });
969
- Object.defineProperty(exports, "Upload", {
970
- enumerable: true,
971
- get: function () { return antd.Upload; }
972
- });
973
- Object.defineProperty(exports, "Watermark", {
974
- enumerable: true,
975
- get: function () { return antd.Watermark; }
976
- });
977
- Object.defineProperty(exports, "CheckCircleOutlined", {
978
- enumerable: true,
979
- get: function () { return icons.CheckCircleOutlined; }
980
- });
981
- Object.defineProperty(exports, "CheckOutlined", {
982
- enumerable: true,
983
- get: function () { return icons.CheckOutlined; }
984
- });
985
- Object.defineProperty(exports, "CloseCircleOutlined", {
986
- enumerable: true,
987
- get: function () { return icons.CloseCircleOutlined; }
988
- });
989
- Object.defineProperty(exports, "CloseOutlined", {
990
- enumerable: true,
991
- get: function () { return icons.CloseOutlined; }
992
- });
993
- Object.defineProperty(exports, "DeleteOutlined", {
994
- enumerable: true,
995
- get: function () { return icons.DeleteOutlined; }
996
- });
997
- Object.defineProperty(exports, "DownOutlined", {
998
- enumerable: true,
999
- get: function () { return icons.DownOutlined; }
1000
- });
1001
- Object.defineProperty(exports, "DownloadOutlined", {
1002
- enumerable: true,
1003
- get: function () { return icons.DownloadOutlined; }
1004
- });
1005
- Object.defineProperty(exports, "EditOutlined", {
1006
- enumerable: true,
1007
- get: function () { return icons.EditOutlined; }
1008
- });
1009
- Object.defineProperty(exports, "ExclamationCircleOutlined", {
1010
- enumerable: true,
1011
- get: function () { return icons.ExclamationCircleOutlined; }
1012
- });
1013
- Object.defineProperty(exports, "EyeInvisibleOutlined", {
1014
- enumerable: true,
1015
- get: function () { return icons.EyeInvisibleOutlined; }
1016
- });
1017
- Object.defineProperty(exports, "EyeOutlined", {
1018
- enumerable: true,
1019
- get: function () { return icons.EyeOutlined; }
1020
- });
1021
- Object.defineProperty(exports, "FileExcelOutlined", {
1022
- enumerable: true,
1023
- get: function () { return icons.FileExcelOutlined; }
1024
- });
1025
- Object.defineProperty(exports, "FileOutlined", {
1026
- enumerable: true,
1027
- get: function () { return icons.FileOutlined; }
1028
- });
1029
- Object.defineProperty(exports, "FilePdfOutlined", {
1030
- enumerable: true,
1031
- get: function () { return icons.FilePdfOutlined; }
1032
- });
1033
- Object.defineProperty(exports, "FilterOutlined", {
1034
- enumerable: true,
1035
- get: function () { return icons.FilterOutlined; }
1036
- });
1037
- Object.defineProperty(exports, "FolderOutlined", {
1038
- enumerable: true,
1039
- get: function () { return icons.FolderOutlined; }
1040
- });
1041
- Object.defineProperty(exports, "InfoCircleOutlined", {
1042
- enumerable: true,
1043
- get: function () { return icons.InfoCircleOutlined; }
1044
- });
1045
- Object.defineProperty(exports, "LeftOutlined", {
1046
- enumerable: true,
1047
- get: function () { return icons.LeftOutlined; }
1048
- });
1049
- Object.defineProperty(exports, "LoadingOutlined", {
1050
- enumerable: true,
1051
- get: function () { return icons.LoadingOutlined; }
1052
- });
1053
- Object.defineProperty(exports, "LogoutOutlined", {
1054
- enumerable: true,
1055
- get: function () { return icons.LogoutOutlined; }
1056
- });
1057
- Object.defineProperty(exports, "MenuOutlined", {
1058
- enumerable: true,
1059
- get: function () { return icons.MenuOutlined; }
1060
- });
1061
- Object.defineProperty(exports, "PlusOutlined", {
1062
- enumerable: true,
1063
- get: function () { return icons.PlusOutlined; }
1064
- });
1065
- Object.defineProperty(exports, "ReloadOutlined", {
1066
- enumerable: true,
1067
- get: function () { return icons.ReloadOutlined; }
1068
- });
1069
- Object.defineProperty(exports, "RightOutlined", {
1070
- enumerable: true,
1071
- get: function () { return icons.RightOutlined; }
1072
- });
1073
- Object.defineProperty(exports, "SearchOutlined", {
1074
- enumerable: true,
1075
- get: function () { return icons.SearchOutlined; }
1076
- });
1077
- Object.defineProperty(exports, "SettingOutlined", {
1078
- enumerable: true,
1079
- get: function () { return icons.SettingOutlined; }
1080
- });
1081
- Object.defineProperty(exports, "UpOutlined", {
1082
- enumerable: true,
1083
- get: function () { return icons.UpOutlined; }
1084
- });
1085
- Object.defineProperty(exports, "UploadOutlined", {
1086
- enumerable: true,
1087
- get: function () { return icons.UploadOutlined; }
1088
- });
1089
- Object.defineProperty(exports, "UserOutlined", {
1090
- enumerable: true,
1091
- get: function () { return icons.UserOutlined; }
1092
- });
1093
- Object.defineProperty(exports, "WarningOutlined", {
1094
- enumerable: true,
1095
- get: function () { return icons.WarningOutlined; }
1096
- });
1097
- exports.AmsConfigProvider = AmsConfigProvider;
1098
- exports.Button = Button;
1099
- exports.Card = Card;
1100
- exports.ConfirmModal = ConfirmModal;
1101
- exports.DataTable = DataTable;
1102
- exports.DownloadButton = DownloadButton;
1103
- exports.FileUploadDragger = FileUploadDragger;
1104
- exports.FilterSelect = FilterSelect;
1105
- exports.ModalDialog = ModalDialog;
1106
- exports.PageHeader = PageHeader;
1107
- exports.Pagination = Pagination;
1108
- exports.Tag = Tag;
1109
- exports.ThemeToggle = ThemeToggle;
1110
- exports.amsTheme = amsTheme;
1111
- exports.amsThemeDark = amsThemeDark;
1112
- exports.applyThemeModeToDOM = applyThemeModeToDOM;
1113
- exports.buildTheme = buildTheme;
1114
- exports.getInitialThemeMode = getInitialThemeMode;
1115
- exports.primitives = primitives;
1116
- exports.radius = radius;
1117
- exports.semantic = semantic;
1118
- exports.sizing = sizing;
1119
- exports.typography = typography;
1120
- exports.useAmsMessage = useAmsMessage;
1121
- exports.useAmsNotification = useAmsNotification;
1122
- exports.useControllableState = useControllableState;
1123
- exports.useThemeMode = useThemeMode;
1238
+ function MarcoActivo({
1239
+ clave,
1240
+ etiqueta,
1241
+ columnas,
1242
+ barra,
1243
+ alto,
1244
+ ajuste,
1245
+ pie,
1246
+ controles,
1247
+ className,
1248
+ children
1249
+ }) {
1250
+ const usuario = react.useContext(ContextoUsuarioTablas);
1251
+ const claveLs = claveMarco(clave, usuario);
1252
+ const alcance = react.useId();
1253
+ const scroll = react.useRef(null);
1254
+ const [prefs, setPrefs] = react.useState(PREFS_MARCO_DEFAULT);
1255
+ const [menu, setMenu] = react.useState(false);
1256
+ const zonaMenu = react.useRef(null);
1257
+ react.useEffect(() => {
1258
+ setPrefs(cargarPrefsMarco(claveLs));
1259
+ }, [claveLs]);
1260
+ const persistir = react.useCallback(
1261
+ (nuevas) => {
1262
+ setPrefs(nuevas);
1263
+ guardarPrefsMarco(claveLs, nuevas);
1264
+ },
1265
+ [claveLs]
1266
+ );
1267
+ const ocultasValidas = prefs.ocultas.filter((id) => columnas?.some((c) => c.id === id));
1268
+ const css = cssColumnasOcultas(alcance, ocultasValidas);
1269
+ const actualizarSombras = react.useCallback(() => {
1270
+ const el = scroll.current;
1271
+ if (!el) return;
1272
+ const izq = el.scrollLeft > 0;
1273
+ const der = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
1274
+ el.toggleAttribute("data-sombra-izq", izq);
1275
+ el.toggleAttribute("data-sombra-der", der);
1276
+ }, []);
1277
+ react.useLayoutEffect(() => {
1278
+ const el = scroll.current;
1279
+ if (!el) return;
1280
+ const medir = () => {
1281
+ const fila = el.querySelector("thead tr");
1282
+ const izq = fila ? Array.from(fila.querySelectorAll(':scope > [data-fija="izq"]')) : [];
1283
+ const { offsets, total } = offsetsAcumulados(izq.map((c) => c.offsetWidth));
1284
+ offsets.forEach((o, i) => el.style.setProperty(`--fija-izq-${i}`, `${o}px`));
1285
+ el.style.setProperty("--fija-izq-total", `${total}px`);
1286
+ el.setAttribute("data-fijas-izq", String(izq.length));
1287
+ const der = fila?.querySelector(':scope > [data-fija="der"]');
1288
+ el.style.setProperty("--fija-der-total", `${der?.offsetWidth ?? 0}px`);
1289
+ actualizarSombras();
1290
+ };
1291
+ medir();
1292
+ if (typeof ResizeObserver === "undefined") return;
1293
+ const obs = new ResizeObserver(medir);
1294
+ obs.observe(el);
1295
+ const tabla = el.querySelector("table");
1296
+ if (tabla) obs.observe(tabla);
1297
+ return () => obs.disconnect();
1298
+ });
1299
+ react.useEffect(() => {
1300
+ const el = scroll.current;
1301
+ if (!el) return;
1302
+ const alGirar = (e) => {
1303
+ if (!e.shiftKey || e.deltaY === 0 || Math.abs(e.deltaX) > 0) return;
1304
+ if (el.scrollWidth <= el.clientWidth) return;
1305
+ el.scrollLeft += e.deltaY;
1306
+ e.preventDefault();
1307
+ actualizarSombras();
1308
+ };
1309
+ el.addEventListener("wheel", alGirar, { passive: false });
1310
+ return () => el.removeEventListener("wheel", alGirar);
1311
+ }, [actualizarSombras]);
1312
+ react.useEffect(() => {
1313
+ if (!menu) return;
1314
+ const tecla = (e) => e.key === "Escape" && setMenu(false);
1315
+ const fuera = (e) => {
1316
+ if (zonaMenu.current && !zonaMenu.current.contains(e.target)) setMenu(false);
1317
+ };
1318
+ document.addEventListener("keydown", tecla);
1319
+ document.addEventListener("mousedown", fuera);
1320
+ return () => {
1321
+ document.removeEventListener("keydown", tecla);
1322
+ document.removeEventListener("mousedown", fuera);
1323
+ };
1324
+ }, [menu]);
1325
+ function alternarColumna(id) {
1326
+ const ocultas = prefs.ocultas.includes(id) ? prefs.ocultas.filter((x) => x !== id) : [...prefs.ocultas, id];
1327
+ persistir({ ...prefs, ocultas });
1328
+ }
1329
+ function fijarDensidad(densidad) {
1330
+ persistir({ ...prefs, densidad });
1331
+ }
1332
+ function restablecer() {
1333
+ setPrefs({ ...PREFS_MARCO_DEFAULT });
1334
+ borrarPrefsMarco(claveLs);
1335
+ }
1336
+ const hayColumnas = Boolean(columnas && columnas.length > 0);
1337
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1338
+ "div",
1339
+ {
1340
+ "data-marco-ajuste": ajuste,
1341
+ className: `flex min-w-0 flex-col gap-2 ${ajuste === "pagina" ? "min-h-0 flex-1" : ""} ${className}`,
1342
+ children: [
1343
+ css && /* @__PURE__ */ jsxRuntime.jsx("style", { children: css }),
1344
+ (controles || barra) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2", "data-marco-barra": "", children: [
1345
+ barra && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mr-auto flex flex-wrap items-center gap-2", children: barra }),
1346
+ controles && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
1347
+ hayColumnas && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: zonaMenu, children: [
1348
+ /* @__PURE__ */ jsxRuntime.jsxs(
1349
+ "button",
1350
+ {
1351
+ type: "button",
1352
+ onClick: () => setMenu((v) => !v),
1353
+ "aria-expanded": menu,
1354
+ "aria-haspopup": "true",
1355
+ title: "Mostrar u ocultar columnas",
1356
+ className: CLASE_BOTON,
1357
+ children: [
1358
+ /* @__PURE__ */ jsxRuntime.jsx(ICONO_COLUMNAS, { size: TAMANO_BADGE, "aria-hidden": "true" }),
1359
+ "Columnas",
1360
+ ocultasValidas.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "rounded bg-[var(--tema-badge-neutro)] px-1 tabular-nums", children: [
1361
+ "-",
1362
+ ocultasValidas.length
1363
+ ] })
1364
+ ]
1365
+ }
1366
+ ),
1367
+ menu && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 top-full z-30 mt-1 w-60 rounded-md border border-[var(--tema-borde)] bg-[var(--tema-fondo)] p-2 shadow-lg", children: [
1368
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", "aria-label": "Columnas visibles", children: [
1369
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "flex items-center gap-1 px-1 pb-1 text-[10px] font-semibold uppercase tracking-wide text-[#9a9a9a]", children: "Columnas visibles" }),
1370
+ columnas.map((c) => {
1371
+ const idInput = `${alcance}-${c.id}`;
1372
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1373
+ "label",
1374
+ {
1375
+ htmlFor: idInput,
1376
+ className: "flex cursor-pointer items-center gap-2 rounded px-1.5 py-1 text-sm text-[#3c3c3b] hover:bg-[var(--tema-badge-neutro)]",
1377
+ children: [
1378
+ /* @__PURE__ */ jsxRuntime.jsx(
1379
+ "input",
1380
+ {
1381
+ id: idInput,
1382
+ type: "checkbox",
1383
+ checked: !prefs.ocultas.includes(c.id),
1384
+ onChange: () => alternarColumna(c.id)
1385
+ }
1386
+ ),
1387
+ c.etiqueta
1388
+ ]
1389
+ },
1390
+ c.id
1391
+ );
1392
+ })
1393
+ ] }),
1394
+ /* @__PURE__ */ jsxRuntime.jsxs(
1395
+ "button",
1396
+ {
1397
+ type: "button",
1398
+ onClick: restablecer,
1399
+ className: "mt-2 inline-flex w-full items-center justify-center gap-1.5 rounded border border-[var(--tema-borde)] px-2 py-1 text-xs text-[#3c3c3b] hover:bg-[var(--tema-badge-neutro)]",
1400
+ children: [
1401
+ /* @__PURE__ */ jsxRuntime.jsx(ICONO_RESTABLECER, { size: TAMANO_BADGE, "aria-hidden": "true" }),
1402
+ "Restablecer"
1403
+ ]
1404
+ }
1405
+ )
1406
+ ] })
1407
+ ] }),
1408
+ hayColumnas && /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: "plataforma.tabla_columnas" }),
1409
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center overflow-hidden rounded-md border border-[var(--tema-borde)]", role: "group", "aria-label": "Densidad de filas", children: [
1410
+ /* @__PURE__ */ jsxRuntime.jsx(BotonDensidad, { actual: prefs.densidad, valor: "compacta", onClick: fijarDensidad }),
1411
+ /* @__PURE__ */ jsxRuntime.jsx(BotonDensidad, { actual: prefs.densidad, valor: "comoda", onClick: fijarDensidad })
1412
+ ] }),
1413
+ /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: "plataforma.tabla_densidad" })
1414
+ ] })
1415
+ ] }),
1416
+ /* @__PURE__ */ jsxRuntime.jsx(
1417
+ "div",
1418
+ {
1419
+ ref: scroll,
1420
+ role: "region",
1421
+ "aria-label": etiqueta,
1422
+ tabIndex: 0,
1423
+ "data-marco-tabla": alcance,
1424
+ "data-densidad": prefs.densidad,
1425
+ onScroll: actualizarSombras,
1426
+ onFocus: (e) => {
1427
+ if (e.target === e.currentTarget) return;
1428
+ const celda = e.target.closest("td,th");
1429
+ celda?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
1430
+ },
1431
+ "data-ajuste": ajuste,
1432
+ style: alto ? { "--marco-alto": alto } : void 0,
1433
+ className: `marco-tabla-scroll rounded-lg border border-[var(--tema-borde)] ${ajuste === "pagina" ? "min-h-28" : ""}`,
1434
+ children
1435
+ }
1436
+ ),
1437
+ pie && // Al cambiar de pagina (o de tamano) el cuerpo vuelve arriba: la pagina ya no se desplaza, la tabla si.
1438
+ /* @__PURE__ */ jsxRuntime.jsx(
1439
+ "div",
1440
+ {
1441
+ className: "shrink-0",
1442
+ "data-marco-pie": "",
1443
+ onClick: (e) => {
1444
+ if (e.target.closest("button")) scroll.current?.scrollTo?.({ top: 0 });
1445
+ },
1446
+ children: pie
1447
+ }
1448
+ )
1449
+ ]
1450
+ }
1451
+ );
1452
+ }
1453
+ function BotonDensidad({
1454
+ actual,
1455
+ valor,
1456
+ onClick
1457
+ }) {
1458
+ const Icono = valor === "compacta" ? ICONO_DENSIDAD_COMPACTA : ICONO_DENSIDAD_COMODA;
1459
+ const nombre = valor === "compacta" ? "Densidad compacta" : "Densidad c\xF3moda";
1460
+ const activo = actual === valor;
1461
+ return /* @__PURE__ */ jsxRuntime.jsx(
1462
+ "button",
1463
+ {
1464
+ type: "button",
1465
+ "aria-pressed": activo,
1466
+ "aria-label": nombre,
1467
+ title: nombre,
1468
+ onClick: () => onClick(valor),
1469
+ className: `px-2 py-1 transition ${activo ? "bg-[var(--tema-primario)] text-white" : "text-[#565656] hover:bg-[var(--tema-badge-neutro)]"}`,
1470
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icono, { size: TAMANO_BADGE, "aria-hidden": "true" })
1471
+ }
1472
+ );
1473
+ }
1474
+ function CascaronApp({
1475
+ producto,
1476
+ hrefInicio = "/",
1477
+ logo,
1478
+ usuario,
1479
+ hrefCerrarSesion = "/auth/logout",
1480
+ grupos,
1481
+ pieExtra,
1482
+ children
1483
+ }) {
1484
+ return /* @__PURE__ */ jsxRuntime.jsx(UsuarioTablas, { usuario: usuario.idEstable, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-full", children: [
1485
+ /* @__PURE__ */ jsxRuntime.jsx(
1486
+ SidebarColapsable,
1487
+ {
1488
+ cabecera: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
1489
+ /* @__PURE__ */ jsxRuntime.jsx(LinkBase, { href: hrefInicio, className: "flex shrink-0 items-center gap-2", children: logo && // eslint-disable-next-line @next/next/no-img-element
1490
+ /* @__PURE__ */ jsxRuntime.jsx("img", { src: logo.src, alt: logo.alt, width: logo.ancho, height: logo.alto }) }),
1491
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-[var(--tema-primario)]", children: producto })
1492
+ ] }),
1493
+ pie: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 md:group-data-[colapsado=true]/side:flex-col", children: [
1494
+ pieExtra,
1495
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-1 flex-col leading-tight md:group-data-[colapsado=true]/side:hidden", children: [
1496
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] font-medium text-[#3c3c3b]", title: usuario.nombre, children: usuario.nombre }),
1497
+ usuario.rol && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mt-0.5 inline-flex items-center gap-1", children: [
1498
+ /* @__PURE__ */ jsxRuntime.jsx(
1499
+ "span",
1500
+ {
1501
+ className: "w-fit rounded-full bg-[var(--tema-badge-neutro)] px-1.5 py-0.5 text-[10px] font-medium text-[#565656]",
1502
+ title: `Rol: ${usuario.rol}`,
1503
+ children: usuario.rol
1504
+ }
1505
+ ),
1506
+ /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: "plataforma.rol" })
1507
+ ] })
1508
+ ] }),
1509
+ /* @__PURE__ */ jsxRuntime.jsx(
1510
+ "a",
1511
+ {
1512
+ href: hrefCerrarSesion,
1513
+ title: "Cerrar sesi\xF3n",
1514
+ "aria-label": "Cerrar sesi\xF3n",
1515
+ className: "flex shrink-0 items-center transition hover:text-[var(--tema-primario)]",
1516
+ children: /* @__PURE__ */ jsxRuntime.jsx(ICONO_CERRAR_SESION, { size: TAMANO_BOTON, "aria-hidden": "true" })
1517
+ }
1518
+ )
1519
+ ] }),
1520
+ children: grupos.map((g, i) => /* @__PURE__ */ jsxRuntime.jsx(SeccionNav, { titulo: g.titulo, children: g.enlaces.map(({ href, etiqueta, icono: Icono }) => /* @__PURE__ */ jsxRuntime.jsx(EnlaceNav, { href, etiqueta, icono: /* @__PURE__ */ jsxRuntime.jsx(Icono, { size: TAMANO_BOTON, "aria-hidden": "true" }) }, href)) }, g.titulo ?? i))
1521
+ }
1522
+ ),
1523
+ /* @__PURE__ */ jsxRuntime.jsx(
1524
+ "div",
1525
+ {
1526
+ "data-area-contenido": "",
1527
+ className: "flex min-h-screen min-w-0 flex-1 flex-col overflow-x-clip pt-14 transition-[padding] md:pt-0 md:pr-[var(--asistente-ancho,0px)]",
1528
+ children
1529
+ }
1530
+ )
1531
+ ] }) });
1532
+ }
1533
+ function Migas({
1534
+ hrefVolver,
1535
+ etiquetaVolver,
1536
+ actual,
1537
+ actualMono = false
1538
+ }) {
1539
+ return /* @__PURE__ */ jsxRuntime.jsxs("nav", { "aria-label": "Ruta", className: "mb-3 flex flex-wrap items-center gap-2 text-xs text-[#807f7f]", children: [
1540
+ /* @__PURE__ */ jsxRuntime.jsxs(LinkBase, { href: hrefVolver, className: "text-[var(--tema-primario)] underline", children: [
1541
+ "\u2190 ",
1542
+ etiquetaVolver
1543
+ ] }),
1544
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "/" }),
1545
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: actualMono ? "font-mono" : void 0, children: actual })
1546
+ ] });
1547
+ }
1548
+ function SubNav({
1549
+ etiqueta,
1550
+ secciones,
1551
+ activa
1552
+ }) {
1553
+ return /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": etiqueta, className: "mt-4 flex flex-wrap gap-2 text-sm", children: secciones.map((s) => /* @__PURE__ */ jsxRuntime.jsx(
1554
+ LinkBase,
1555
+ {
1556
+ href: s.href,
1557
+ "aria-current": s.id === activa ? "page" : void 0,
1558
+ className: `pulsable rounded-md border px-3 py-1 ${s.id === activa ? "border-[var(--tema-primario)] bg-[var(--tema-badge-neutro)] font-semibold" : "border-[var(--tema-borde)] hover:bg-[var(--tema-badge-neutro)]"}`,
1559
+ children: s.texto
1560
+ },
1561
+ s.id
1562
+ )) });
1563
+ }
1564
+ var BOTON = {
1565
+ // Primario = relleno neutro oscuro (no el rojo de marca); el rojo queda reservado a peligro.
1566
+ primario: "inline-flex items-center gap-1.5 rounded-md bg-[var(--tema-texto)] px-3 py-1.5 text-sm font-medium text-white transition hover:brightness-125 disabled:opacity-50",
1567
+ secundario: "inline-flex items-center gap-1.5 rounded-md border border-[var(--tema-borde)] px-3 py-1.5 text-sm font-medium text-[#3c3c3b] transition hover:bg-[var(--tema-badge-neutro)] disabled:opacity-50"
1568
+ };
1569
+ function AccionConDialogo({
1570
+ etiqueta,
1571
+ titulo,
1572
+ icono,
1573
+ variante = "secundario",
1574
+ children
1575
+ }) {
1576
+ const [abierto, setAbierto] = react.useState(false);
1577
+ const disparador = react.useRef(null);
1578
+ const panel = react.useRef(null);
1579
+ const tituloId = react.useId();
1580
+ react.useEffect(() => {
1581
+ if (!abierto) return;
1582
+ queueMicrotask(() => {
1583
+ const campo = panel.current?.querySelector("input, select, textarea");
1584
+ campo?.focus();
1585
+ });
1586
+ }, [abierto]);
1587
+ function cerrar() {
1588
+ setAbierto(false);
1589
+ disparador.current?.focus();
1590
+ }
1591
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1592
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { ref: disparador, type: "button", onClick: () => setAbierto(true), className: BOTON[variante], children: [
1593
+ icono,
1594
+ etiqueta
1595
+ ] }),
1596
+ abierto && /* @__PURE__ */ jsxRuntime.jsx(
1597
+ "div",
1598
+ {
1599
+ "data-testid": "dialogo-fondo",
1600
+ className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",
1601
+ onClick: cerrar,
1602
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1603
+ "div",
1604
+ {
1605
+ ref: panel,
1606
+ role: "dialog",
1607
+ "aria-modal": "true",
1608
+ "aria-labelledby": tituloId,
1609
+ onClick: (e) => e.stopPropagation(),
1610
+ onKeyDown: (e) => {
1611
+ if (e.key === "Escape") {
1612
+ e.stopPropagation();
1613
+ cerrar();
1614
+ }
1615
+ },
1616
+ className: "max-h-[90vh] w-full max-w-md overflow-y-auto rounded-xl border border-[var(--tema-borde)] bg-[var(--tema-fondo)] p-6 shadow-xl",
1617
+ children: [
1618
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
1619
+ /* @__PURE__ */ jsxRuntime.jsxs("h2", { id: tituloId, className: "flex items-center gap-2 text-base font-semibold text-[var(--tema-primario-oscuro)]", children: [
1620
+ icono,
1621
+ titulo
1622
+ ] }),
1623
+ /* @__PURE__ */ jsxRuntime.jsx(
1624
+ "button",
1625
+ {
1626
+ type: "button",
1627
+ onClick: cerrar,
1628
+ "aria-label": "Cerrar",
1629
+ title: "Cerrar",
1630
+ className: "rounded-md p-1 text-[#807f7f] transition hover:bg-[var(--tema-badge-neutro)]",
1631
+ children: /* @__PURE__ */ jsxRuntime.jsx(ICONO_CERRAR, { size: TAMANO_BADGE, "aria-hidden": "true" })
1632
+ }
1633
+ )
1634
+ ] }),
1635
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4", children })
1636
+ ]
1637
+ }
1638
+ )
1639
+ }
1640
+ )
1641
+ ] });
1642
+ }
1643
+ var CLASE_REGION_LISTADO = "flex min-h-0 flex-1 flex-col";
1644
+ function PaginaListado({
1645
+ titulo,
1646
+ icono,
1647
+ descripcion,
1648
+ acciones,
1649
+ className = "",
1650
+ children
1651
+ }) {
1652
+ return /* @__PURE__ */ jsxRuntime.jsxs("main", { "data-pagina-listado": "", className: `flex min-h-0 w-full flex-1 flex-col gap-3 px-6 py-4 ${className}`, children: [
1653
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex shrink-0 flex-wrap items-center gap-x-3 gap-y-1", children: [
1654
+ /* @__PURE__ */ jsxRuntime.jsxs("h1", { className: "flex shrink-0 items-center gap-2 text-xl font-semibold text-[var(--tema-primario-oscuro)]", children: [
1655
+ icono,
1656
+ titulo
1657
+ ] }),
1658
+ descripcion && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "min-w-[12rem] flex-1 truncate text-sm text-[#807f7f]", title: descripcion, children: descripcion }),
1659
+ acciones && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex shrink-0 flex-wrap items-center gap-2", children: acciones })
1660
+ ] }),
1661
+ children
1662
+ ] });
1663
+ }
1664
+ function ModalPanel({
1665
+ titulo,
1666
+ tituloId,
1667
+ acciones,
1668
+ onCerrar,
1669
+ etiquetaCerrar = "Cerrar",
1670
+ ancho = "max-w-[min(96vw,100rem)]",
1671
+ children
1672
+ }) {
1673
+ const dialogo = react.useRef(null);
1674
+ react.useEffect(() => {
1675
+ dialogo.current?.focus();
1676
+ }, []);
1677
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": "modal-fondo", className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4", onClick: onCerrar, children: /* @__PURE__ */ jsxRuntime.jsxs(
1678
+ "div",
1679
+ {
1680
+ ref: dialogo,
1681
+ role: "dialog",
1682
+ "aria-modal": "true",
1683
+ "aria-labelledby": tituloId,
1684
+ tabIndex: -1,
1685
+ onClick: (e) => e.stopPropagation(),
1686
+ onKeyDown: (e) => {
1687
+ if (e.key === "Escape") {
1688
+ e.stopPropagation();
1689
+ onCerrar();
1690
+ }
1691
+ },
1692
+ className: `flex max-h-[90dvh] w-full ${ancho} flex-col gap-3 rounded-xl border border-[var(--tema-borde)] bg-[var(--tema-fondo)] p-4 shadow-xl outline-none`,
1693
+ children: [
1694
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-modal-cabecera": "", className: "flex shrink-0 items-start justify-between gap-3", children: [
1695
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { id: tituloId, className: "flex min-w-0 items-center gap-1.5 text-base font-semibold text-[var(--tema-primario-oscuro)]", children: titulo }),
1696
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
1697
+ acciones,
1698
+ /* @__PURE__ */ jsxRuntime.jsx(
1699
+ "button",
1700
+ {
1701
+ type: "button",
1702
+ onClick: onCerrar,
1703
+ "aria-label": etiquetaCerrar,
1704
+ title: etiquetaCerrar,
1705
+ className: "rounded-md p-1 text-[#807f7f] transition hover:bg-[var(--tema-badge-neutro)]",
1706
+ children: /* @__PURE__ */ jsxRuntime.jsx(ICONO_CERRAR, { size: TAMANO_BADGE, "aria-hidden": "true" })
1707
+ }
1708
+ )
1709
+ ] })
1710
+ ] }),
1711
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "data-modal-cuerpo": "", className: `${CLASE_REGION_LISTADO} gap-3`, children })
1712
+ ]
1713
+ }
1714
+ ) });
1715
+ }
1716
+ function Pestanas({
1717
+ etiqueta,
1718
+ pestanas,
1719
+ inicial,
1720
+ extra
1721
+ }) {
1722
+ const base = react.useId();
1723
+ const [activa, setActiva] = react.useState(inicial ?? pestanas[0]?.id);
1724
+ const ids = pestanas.map((p) => p.id);
1725
+ const teclado = (e) => {
1726
+ if (e.key !== "ArrowRight" && e.key !== "ArrowLeft") return;
1727
+ e.preventDefault();
1728
+ const i = ids.indexOf(activa ?? "");
1729
+ const siguiente = ids[(i + (e.key === "ArrowRight" ? 1 : ids.length - 1)) % ids.length];
1730
+ setActiva(siguiente);
1731
+ document.getElementById(`${base}-pestana-${siguiente}`)?.focus();
1732
+ };
1733
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
1734
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "tablist", "aria-label": etiqueta, className: "inline-flex gap-1 rounded-lg bg-[var(--tema-badge-neutro)] p-1 print:hidden", children: [
1735
+ pestanas.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
1736
+ "button",
1737
+ {
1738
+ type: "button",
1739
+ role: "tab",
1740
+ id: `${base}-pestana-${p.id}`,
1741
+ "aria-selected": activa === p.id,
1742
+ "aria-controls": `${base}-panel-${p.id}`,
1743
+ tabIndex: activa === p.id ? 0 : -1,
1744
+ onClick: () => setActiva(p.id),
1745
+ onKeyDown: teclado,
1746
+ className: `rounded-md px-3 py-1 text-xs font-medium ${activa === p.id ? "bg-[var(--tema-fondo)] text-[var(--tema-primario-oscuro)] shadow-sm" : "text-[#565656] hover:text-[var(--tema-primario)]"}`,
1747
+ children: p.texto
1748
+ },
1749
+ p.id
1750
+ )),
1751
+ extra && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "self-center pr-1", children: extra })
1752
+ ] }),
1753
+ pestanas.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
1754
+ "div",
1755
+ {
1756
+ role: "tabpanel",
1757
+ id: `${base}-panel-${p.id}`,
1758
+ "aria-labelledby": `${base}-pestana-${p.id}`,
1759
+ className: `space-y-3 ${activa !== p.id ? "hidden print:block" : ""}`,
1760
+ children: p.contenido
1761
+ },
1762
+ p.id
1763
+ ))
1764
+ ] });
1765
+ }
1766
+ function PantallaAcceso({
1767
+ producto,
1768
+ titulo,
1769
+ descripcion,
1770
+ hrefLogin,
1771
+ etiquetaBoton = "Iniciar sesi\xF3n con Microsoft 365",
1772
+ capacidades = [],
1773
+ logo,
1774
+ error
1775
+ }) {
1776
+ return /* @__PURE__ */ jsxRuntime.jsxs("main", { className: "reflejo-fondo flex min-h-screen flex-1 flex-col items-center justify-center gap-10 bg-[#f4f8fc] px-6 py-16", children: [
1777
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex items-center gap-2", children: [
1778
+ logo,
1779
+ logo && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-6 w-px bg-[var(--tema-borde)]" }),
1780
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold text-[var(--tema-primario)]", children: producto })
1781
+ ] }),
1782
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex max-w-xl flex-col items-center gap-4 text-center", children: [
1783
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-semibold text-[var(--tema-primario-oscuro)] sm:text-4xl", children: titulo }),
1784
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base text-[#565656]", children: descripcion }),
1785
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "rounded-md bg-[var(--tema-badge-error)] px-3 py-2 text-sm text-[var(--tema-peligro)]", children: error }),
1786
+ /* @__PURE__ */ jsxRuntime.jsxs(
1787
+ "a",
1788
+ {
1789
+ href: hrefLogin,
1790
+ className: "mt-2 inline-flex items-center gap-2 rounded-md bg-[var(--tema-texto)] px-5 py-2.5 text-sm font-medium text-white transition hover:brightness-125",
1791
+ children: [
1792
+ /* @__PURE__ */ jsxRuntime.jsx(ICONO_LOGIN_MICROSOFT, { size: TAMANO_BOTON, "aria-hidden": "true" }),
1793
+ etiquetaBoton
1794
+ ]
1795
+ }
1796
+ )
1797
+ ] }),
1798
+ capacidades.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid w-full max-w-4xl gap-4 sm:grid-cols-3", children: capacidades.map(({ icono: Icono, titulo: t, descripcion: d }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 rounded-xl border border-[var(--tema-borde)] bg-white p-5 text-left shadow-sm", children: [
1799
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-10 w-10 items-center justify-center rounded-lg bg-[var(--tema-badge-neutro)] text-[var(--tema-primario)]", children: /* @__PURE__ */ jsxRuntime.jsx(Icono, { size: TAMANO_BOTON, "aria-hidden": "true" }) }),
1800
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold text-[var(--tema-texto)]", children: t }),
1801
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs leading-relaxed text-[#565656]", children: d })
1802
+ ] }, t)) })
1803
+ ] });
1804
+ }
1805
+ function PantallaCargando({ etiqueta = "Cargando..." }) {
1806
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[60vh] w-full flex-1 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(SpinnerMarca, { tamano: 140, etiqueta }) });
1807
+ }
1808
+ function FechaHoraLocal({ iso }) {
1809
+ const [texto, setTexto] = react.useState(() => _isoLegible(iso));
1810
+ react.useEffect(() => {
1811
+ setTexto(formatearFechaHora(iso));
1812
+ }, [iso]);
1813
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { suppressHydrationWarning: true, children: texto });
1814
+ }
1815
+ function _isoLegible(iso) {
1816
+ if (!iso) return "\u2014";
1817
+ const m = iso.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/);
1818
+ if (!m) return iso;
1819
+ const [, anio, mes, dia, hora, min, seg] = m;
1820
+ return `${anio}/${mes}/${dia} ${hora}:${min}:${seg}`;
1821
+ }
1822
+ var ANCHOS = {
1823
+ completo: "",
1824
+ "2xl": "mx-auto max-w-2xl",
1825
+ "3xl": "mx-auto max-w-3xl",
1826
+ "4xl": "mx-auto max-w-4xl"
1827
+ };
1828
+ function PaginaContenido({
1829
+ titulo,
1830
+ icono,
1831
+ descripcion,
1832
+ acciones,
1833
+ ancho = "completo",
1834
+ children
1835
+ }) {
1836
+ return /* @__PURE__ */ jsxRuntime.jsxs("main", { className: `w-full flex-1 px-6 py-10 ${ANCHOS[ancho]}`, children: [
1837
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
1838
+ /* @__PURE__ */ jsxRuntime.jsxs("h1", { className: "flex items-center gap-2 text-xl font-semibold text-[var(--tema-primario-oscuro)]", children: [
1839
+ icono,
1840
+ titulo
1841
+ ] }),
1842
+ acciones && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-2", children: acciones })
1843
+ ] }),
1844
+ descripcion && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-[#807f7f]", children: descripcion }),
1845
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6", children })
1846
+ ] });
1847
+ }
1848
+ function usePaginacionLocal(items, tamanoInicial = 10) {
1849
+ const [tamano, setTamano] = react.useState(tamanoInicial);
1850
+ const [paginaActual, setPaginaActual] = react.useState(1);
1851
+ const totalPaginas = Math.max(1, Math.ceil(items.length / tamano));
1852
+ const pagina = Math.min(paginaActual, totalPaginas);
1853
+ const itemsPagina = react.useMemo(() => {
1854
+ const inicio = (pagina - 1) * tamano;
1855
+ return items.slice(inicio, inicio + tamano);
1856
+ }, [items, pagina, tamano]);
1857
+ return {
1858
+ itemsPagina,
1859
+ paginaActual: pagina,
1860
+ totalPaginas,
1861
+ tamano,
1862
+ totalItems: items.length,
1863
+ siguiente: () => setPaginaActual((p) => Math.min(p + 1, totalPaginas)),
1864
+ anterior: () => setPaginaActual((p) => Math.max(p - 1, 1)),
1865
+ cambiarTamano: (n) => {
1866
+ setTamano(n);
1867
+ setPaginaActual(1);
1868
+ }
1869
+ };
1870
+ }
1871
+ var TAMANOS = [10, 25, 50, 100];
1872
+ function PaginadorLocal({
1873
+ paginaActual,
1874
+ totalPaginas,
1875
+ totalItems,
1876
+ tamano,
1877
+ tamanos = TAMANOS,
1878
+ onAnterior,
1879
+ onSiguiente,
1880
+ onCambiarTamano
1881
+ }) {
1882
+ if (totalPaginas <= 1) return null;
1883
+ const desde = totalItems === 0 ? 0 : (paginaActual - 1) * tamano + 1;
1884
+ const hasta = Math.min(paginaActual * tamano, totalItems);
1885
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 flex flex-wrap items-center justify-between gap-3", children: [
1886
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1887
+ onCambiarTamano && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1888
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-[#807f7f]", children: "Por p\xE1gina" }),
1889
+ /* @__PURE__ */ jsxRuntime.jsx(
1890
+ "select",
1891
+ {
1892
+ value: tamano,
1893
+ onChange: (e) => onCambiarTamano(Number(e.target.value)),
1894
+ "aria-label": "Filas por p\xE1gina",
1895
+ className: "rounded-md border border-[var(--tema-borde)] bg-[var(--tema-fondo)] px-2 py-1.5 text-sm focus:border-[var(--tema-primario)] focus:outline-none",
1896
+ children: tamanos.map((n) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: n, children: n }, n))
1897
+ }
1898
+ )
1899
+ ] }),
1900
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-[#807f7f]", children: [
1901
+ desde,
1902
+ "\u2013",
1903
+ hasta,
1904
+ " de ",
1905
+ totalItems
1906
+ ] })
1907
+ ] }),
1908
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1909
+ /* @__PURE__ */ jsxRuntime.jsxs(
1910
+ "button",
1911
+ {
1912
+ type: "button",
1913
+ onClick: onAnterior,
1914
+ disabled: paginaActual <= 1,
1915
+ "aria-label": "P\xE1gina anterior",
1916
+ className: "inline-flex items-center gap-1 rounded-md border border-[var(--tema-borde)] px-3 py-1.5 text-sm font-medium text-[#3c3c3b] transition hover:bg-[var(--tema-badge-neutro)] disabled:opacity-50",
1917
+ children: [
1918
+ /* @__PURE__ */ jsxRuntime.jsx(ICONO_PAGINA_ANTERIOR, { size: TAMANO_BADGE, "aria-hidden": "true" }),
1919
+ "Anterior"
1920
+ ]
1921
+ }
1922
+ ),
1923
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-[#807f7f]", children: [
1924
+ paginaActual,
1925
+ " / ",
1926
+ totalPaginas
1927
+ ] }),
1928
+ /* @__PURE__ */ jsxRuntime.jsxs(
1929
+ "button",
1930
+ {
1931
+ type: "button",
1932
+ onClick: onSiguiente,
1933
+ disabled: paginaActual >= totalPaginas,
1934
+ "aria-label": "P\xE1gina siguiente",
1935
+ className: "inline-flex items-center gap-1 rounded-md border border-[var(--tema-borde)] px-3 py-1.5 text-sm font-medium text-[#3c3c3b] transition hover:bg-[var(--tema-badge-neutro)] disabled:opacity-50",
1936
+ children: [
1937
+ "Siguiente",
1938
+ /* @__PURE__ */ jsxRuntime.jsx(ICONO_PAGINA_SIGUIENTE, { size: TAMANO_BADGE, "aria-hidden": "true" })
1939
+ ]
1940
+ }
1941
+ )
1942
+ ] })
1943
+ ] });
1944
+ }
1945
+ function PaginadorUrl({
1946
+ pagina,
1947
+ totalPaginas,
1948
+ totalItems,
1949
+ tamano
1950
+ }) {
1951
+ const { ir, pendiente } = useNavegarListado();
1952
+ if (totalPaginas <= 1) return null;
1953
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", "aria-busy": pendiente || void 0, children: [
1954
+ /* @__PURE__ */ jsxRuntime.jsx(
1955
+ PaginadorLocal,
1956
+ {
1957
+ paginaActual: pagina,
1958
+ totalPaginas,
1959
+ totalItems,
1960
+ tamano,
1961
+ tamanos: TAMANOS_LISTADO,
1962
+ onAnterior: () => ir({ pagina: String(Math.max(1, pagina - 1)) }, { scroll: true }),
1963
+ onSiguiente: () => ir({ pagina: String(Math.min(totalPaginas, pagina + 1)) }, { scroll: true })
1964
+ }
1965
+ ),
1966
+ pendiente && /* @__PURE__ */ jsxRuntime.jsxs("span", { role: "status", className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-[var(--tema-primario)]", children: [
1967
+ /* @__PURE__ */ jsxRuntime.jsx(ICONO_CARGANDO, { size: 14, className: "animate-spin motion-reduce:animate-none", "aria-hidden": "true" }),
1968
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Cargando p\xE1gina..." })
1969
+ ] })
1970
+ ] });
1971
+ }
1972
+ var DEBOUNCE_MS = 300;
1973
+ var CAMPO = "rounded-md border border-[var(--tema-borde)] bg-[var(--tema-fondo)] px-2 py-1.5 text-sm text-[#3c3c3b] focus:border-[var(--tema-primario)] focus:outline-none";
1974
+ function BarraListado({
1975
+ filtros,
1976
+ totalFiltrado,
1977
+ total,
1978
+ placeholder = "Buscar...",
1979
+ etiquetaBusqueda = "Buscar",
1980
+ extra
1981
+ }) {
1982
+ const { ir, pendiente, params } = useNavegarListado();
1983
+ const qUrl = params.get("q") ?? "";
1984
+ const [texto, setTexto] = react.useState(qUrl);
1985
+ const [numeros, setNumeros] = react.useState(
1986
+ () => Object.fromEntries(filtros.filter((f) => f.tipo === "numero").map((f) => [f.clave, params.get(f.clave) ?? ""]))
1987
+ );
1988
+ const idBuscar = react.useId();
1989
+ const temporizador = react.useRef(null);
1990
+ react.useEffect(() => setTexto(qUrl), [qUrl]);
1991
+ react.useEffect(() => () => cancelar(), []);
1992
+ function cancelar() {
1993
+ if (temporizador.current) clearTimeout(temporizador.current);
1994
+ temporizador.current = null;
1995
+ }
1996
+ function aplicarBusqueda(valor) {
1997
+ cancelar();
1998
+ if (valor.trim() === qUrl.trim()) return;
1999
+ ir({ q: valor.trim() || null });
2000
+ }
2001
+ function alEscribir(valor) {
2002
+ setTexto(valor);
2003
+ cancelar();
2004
+ temporizador.current = setTimeout(() => aplicarBusqueda(valor), DEBOUNCE_MS);
2005
+ }
2006
+ function limpiar() {
2007
+ setTexto("");
2008
+ aplicarBusqueda("");
2009
+ }
2010
+ function aplicarNumero(clave) {
2011
+ const valor = (numeros[clave] ?? "").trim();
2012
+ if (valor === (params.get(clave) ?? "")) return;
2013
+ ir({ [clave]: valor || null });
2014
+ }
2015
+ function alEnviar(e) {
2016
+ e.preventDefault();
2017
+ e.stopPropagation();
2018
+ cancelar();
2019
+ const cambios = {};
2020
+ if (texto.trim() !== qUrl.trim()) cambios.q = texto.trim() || null;
2021
+ for (const [clave, valor] of Object.entries(numeros)) {
2022
+ if (valor.trim() !== (params.get(clave) ?? "")) cambios[clave] = valor.trim() || null;
2023
+ }
2024
+ if (Object.keys(cambios).length > 0) ir(cambios);
2025
+ }
2026
+ const tamano = params.get("tamano") ?? String(TAMANO_LISTADO_DEFAULT);
2027
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2028
+ "form",
2029
+ {
2030
+ role: "search",
2031
+ onSubmit: alEnviar,
2032
+ className: "flex flex-wrap items-end gap-x-3 gap-y-2 rounded-lg border border-[var(--tema-borde)] bg-[#f9fafb] px-3 py-2",
2033
+ children: [
2034
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-[16rem] flex-1 flex-col gap-1", children: [
2035
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: idBuscar, className: "text-xs font-medium text-[#565656]", children: etiquetaBusqueda }),
2036
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2037
+ /* @__PURE__ */ jsxRuntime.jsx(
2038
+ ICONO_BUSCAR,
2039
+ {
2040
+ size: 14,
2041
+ "aria-hidden": "true",
2042
+ className: "pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 text-[#807f7f]"
2043
+ }
2044
+ ),
2045
+ /* @__PURE__ */ jsxRuntime.jsx(
2046
+ "input",
2047
+ {
2048
+ id: idBuscar,
2049
+ type: "search",
2050
+ value: texto,
2051
+ onChange: (e) => alEscribir(e.target.value),
2052
+ onKeyDown: (e) => {
2053
+ if (e.key === "Escape" && texto) {
2054
+ e.preventDefault();
2055
+ limpiar();
2056
+ }
2057
+ },
2058
+ placeholder,
2059
+ autoComplete: "off",
2060
+ spellCheck: false,
2061
+ className: `${CAMPO} w-full pl-7 pr-8 [&::-webkit-search-cancel-button]:hidden`
2062
+ }
2063
+ ),
2064
+ texto && /* @__PURE__ */ jsxRuntime.jsx(
2065
+ "button",
2066
+ {
2067
+ type: "button",
2068
+ onClick: limpiar,
2069
+ "aria-label": "Limpiar b\xFAsqueda",
2070
+ title: "Limpiar b\xFAsqueda",
2071
+ className: "absolute right-1 top-1/2 -translate-y-1/2 rounded p-1 text-[#807f7f] transition hover:bg-[var(--tema-badge-neutro)]",
2072
+ children: /* @__PURE__ */ jsxRuntime.jsx(ICONO_LIMPIAR, { size: 12, "aria-hidden": "true" })
2073
+ }
2074
+ )
2075
+ ] })
2076
+ ] }),
2077
+ filtros.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ICONO_FILTRO, { size: TAMANO_BADGE, "aria-hidden": "true", className: "mb-2 hidden text-[#807f7f] sm:block" }),
2078
+ filtros.map((f) => {
2079
+ const id = `${idBuscar}-${f.clave}`;
2080
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
2081
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: "text-xs font-medium text-[#565656]", children: f.etiqueta }),
2082
+ f.tipo === "numero" ? /* @__PURE__ */ jsxRuntime.jsx(
2083
+ "input",
2084
+ {
2085
+ id,
2086
+ type: "number",
2087
+ inputMode: "numeric",
2088
+ min: f.min,
2089
+ max: f.max,
2090
+ value: numeros[f.clave] ?? "",
2091
+ onChange: (e) => setNumeros((n) => ({ ...n, [f.clave]: e.target.value })),
2092
+ onBlur: () => aplicarNumero(f.clave),
2093
+ className: `${CAMPO} w-24`
2094
+ }
2095
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
2096
+ "select",
2097
+ {
2098
+ id,
2099
+ value: params.get(f.clave) ?? "",
2100
+ onChange: (e) => ir({ [f.clave]: e.target.value || null }),
2101
+ className: CAMPO,
2102
+ children: f.opciones.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.valor, children: o.etiqueta }, o.valor))
2103
+ }
2104
+ )
2105
+ ] }, f.clave);
2106
+ }),
2107
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
2108
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `${idBuscar}-tamano`, className: "text-xs font-medium text-[#565656]", children: "Por p\xE1gina" }),
2109
+ /* @__PURE__ */ jsxRuntime.jsx(
2110
+ "select",
2111
+ {
2112
+ id: `${idBuscar}-tamano`,
2113
+ "aria-label": "Filas por p\xE1gina",
2114
+ value: tamano,
2115
+ onChange: (e) => ir({ tamano: e.target.value === String(TAMANO_LISTADO_DEFAULT) ? null : e.target.value }),
2116
+ className: CAMPO,
2117
+ children: TAMANOS_LISTADO.map((n) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: n, children: n }, n))
2118
+ }
2119
+ )
2120
+ ] }),
2121
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-auto flex items-center gap-3 pb-1.5", children: [
2122
+ pendiente ? /* @__PURE__ */ jsxRuntime.jsxs("span", { role: "status", className: "inline-flex items-center gap-1 text-xs text-[#807f7f]", children: [
2123
+ /* @__PURE__ */ jsxRuntime.jsx(ICONO_CARGANDO, { size: 12, "aria-hidden": "true", className: "animate-spin text-[var(--tema-primario)] motion-reduce:animate-none" }),
2124
+ "Actualizando..."
2125
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-[#807f7f]", "aria-live": "polite", children: [
2126
+ totalFiltrado === total ? `${total} en total` : `${totalFiltrado} de ${total}`,
2127
+ /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: "plataforma.listado_resultados" })
2128
+ ] }),
2129
+ extra
2130
+ ] })
2131
+ ]
2132
+ }
2133
+ );
2134
+ }
2135
+ function EncabezadoOrdenUrl({
2136
+ clave,
2137
+ etiqueta,
2138
+ icono,
2139
+ info,
2140
+ ordenActual,
2141
+ dirActual,
2142
+ alineado = "left",
2143
+ celda
2144
+ }) {
2145
+ const { ir } = useNavegarListado();
2146
+ const activa = ordenActual === clave;
2147
+ const dir = activa ? dirActual : null;
2148
+ const Flecha = dir === "asc" ? ICONO_ORDENAR_ASC : dir === "desc" ? ICONO_ORDENAR_DESC : ICONO_ORDENAR_NEUTRO;
2149
+ const descripcion = dir === "asc" ? "ascendente" : dir === "desc" ? "descendente" : "sin ordenar";
2150
+ function alternar() {
2151
+ if (!activa) return ir({ orden: clave, dir: "asc" });
2152
+ if (dir === "asc") return ir({ orden: clave, dir: "desc" });
2153
+ return ir({ orden: null, dir: null });
2154
+ }
2155
+ return /* @__PURE__ */ jsxRuntime.jsx(
2156
+ "th",
2157
+ {
2158
+ ...celda,
2159
+ scope: "col",
2160
+ "aria-sort": dir === "asc" ? "ascending" : dir === "desc" ? "descending" : "none",
2161
+ className: `whitespace-nowrap px-3 py-2 font-medium ${alineado === "right" ? "text-right" : ""}`,
2162
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `inline-flex items-center gap-1 ${alineado === "right" ? "justify-end" : ""}`, children: [
2163
+ /* @__PURE__ */ jsxRuntime.jsxs(
2164
+ "button",
2165
+ {
2166
+ type: "button",
2167
+ onClick: alternar,
2168
+ title: `Ordenar por ${etiqueta}`,
2169
+ "aria-label": `Ordenar por ${etiqueta} (orden actual: ${descripcion})`,
2170
+ className: `inline-flex items-center gap-1 transition hover:text-[var(--tema-primario)] ${activa ? "text-[var(--tema-primario)]" : ""}`,
2171
+ children: [
2172
+ icono,
2173
+ etiqueta,
2174
+ /* @__PURE__ */ jsxRuntime.jsx(Flecha, { size: 12, "aria-hidden": "true", className: activa ? "" : "opacity-40" })
2175
+ ]
2176
+ }
2177
+ ),
2178
+ /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: info })
2179
+ ] })
2180
+ }
2181
+ );
2182
+ }
2183
+ function fija(lado, idx = 0, opciones) {
2184
+ if (lado === "der") return { "data-fija": "der" };
2185
+ const primaria = opciones?.primaria ?? idx === 0;
2186
+ return primaria ? { "data-fija": "izq", "data-fija-idx": idx, "data-fija-primaria": "" } : { "data-fija": "izq", "data-fija-idx": idx };
2187
+ }
2188
+ function col(id) {
2189
+ return { "data-col": id };
2190
+ }
2191
+ function CeldaIdentidad({ texto, className = "" }) {
2192
+ const valor = texto ?? "";
2193
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { title: valor, className: `block max-w-[16rem] truncate ${className}`, children: valor });
2194
+ }
2195
+ var CLASE_ACCION_FILA = "inline-flex items-center gap-1 whitespace-nowrap rounded-md border border-[var(--tema-borde)] px-2.5 py-1 text-xs hover:bg-[var(--tema-badge-neutro)] disabled:opacity-50";
2196
+ var ANCHO_BASE = 160;
2197
+ function TablaConfigurable({
2198
+ columnas,
2199
+ filas,
2200
+ claveFila,
2201
+ clavePrefs,
2202
+ columnaFija,
2203
+ columnaAcciones,
2204
+ idsFijos = [],
2205
+ etiqueta = "Resultados",
2206
+ mensajeVacio = "Sin resultados.",
2207
+ ajuste,
2208
+ pie,
2209
+ barraExtra
2210
+ }) {
2211
+ const fijos = idsFijos.filter((id) => columnas.some((c) => c.id === id));
2212
+ const idsDefault = columnas.map((c) => c.id).filter((id) => !fijos.includes(id));
2213
+ const { idsVisibles, idsOrdenados, ocultas, anchoDe, mover, redimensionar, alternarVisible, restablecer } = useTablaPreferencias(clavePrefs, idsDefault);
2214
+ const porId = new Map(columnas.map((c) => [c.id, c]));
2215
+ const [menuAbierto, setMenuAbierto] = react.useState(false);
2216
+ const [orden, setOrden] = react.useState(null);
2217
+ const arrastrando = react.useRef(null);
2218
+ const filasOrdenadas = (() => {
2219
+ if (!orden) return filas;
2220
+ const col2 = porId.get(orden.id);
2221
+ if (!col2?.valorOrden) return filas;
2222
+ const factor = orden.dir === "asc" ? 1 : -1;
2223
+ return [...filas].sort((a, b) => {
2224
+ const va = col2.valorOrden(a);
2225
+ const vb = col2.valorOrden(b);
2226
+ if (va === void 0 || va === null || va === "") return 1;
2227
+ if (vb === void 0 || vb === null || vb === "") return -1;
2228
+ if (typeof va === "number" && typeof vb === "number") {
2229
+ return (va - vb) * factor;
2230
+ }
2231
+ return String(va).localeCompare(String(vb), "es") * factor;
2232
+ });
2233
+ })();
2234
+ function alternarOrdenColumna(id) {
2235
+ setOrden((prev) => {
2236
+ if (prev?.id !== id) return { id, dir: "asc" };
2237
+ if (prev.dir === "asc") return { id, dir: "desc" };
2238
+ return null;
2239
+ });
2240
+ }
2241
+ const colSpanTotal = fijos.length + idsVisibles.length + (columnaFija ? 1 : 0) + (columnaAcciones ? 1 : 0);
2242
+ const baseFijos = columnaFija ? 1 : 0;
2243
+ const atributosFijo = (i) => fija("izq", baseFijos + i, { primaria: i === 0 });
2244
+ const atributosCasilla = fija("izq", 0, { primaria: fijos.length === 0 });
2245
+ const anchoEfectivo = (id) => {
2246
+ const persistido = anchoDe(id);
2247
+ if (persistido !== void 0) return persistido;
2248
+ return porId.get(id)?.anchoDefault ?? ANCHO_BASE;
2249
+ };
2250
+ const ANCHO_FIJA = 44;
2251
+ const ANCHO_ACCIONES = 96;
2252
+ const anchoTabla = (columnaFija ? ANCHO_FIJA : 0) + (columnaAcciones ? ANCHO_ACCIONES : 0) + [...fijos, ...idsVisibles].reduce((suma, id) => suma + anchoEfectivo(id), 0);
2253
+ const contenidoEncabezado = (col2) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2254
+ col2.ordenable ? /* @__PURE__ */ jsxRuntime.jsx(
2255
+ EncabezadoOrdenable,
2256
+ {
2257
+ columnaId: col2.id,
2258
+ contenido: col2.encabezado,
2259
+ etiqueta: col2.etiquetaOrden ?? (typeof col2.encabezado === "string" ? col2.encabezado : "esta columna"),
2260
+ orden,
2261
+ onAlternar: () => alternarOrdenColumna(col2.id)
2262
+ }
2263
+ ) : col2.encabezado,
2264
+ col2.info && /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: col2.info })
2265
+ ] });
2266
+ const barra = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-center gap-2", children: [
2267
+ /* @__PURE__ */ jsxRuntime.jsxs(
2268
+ "button",
2269
+ {
2270
+ type: "button",
2271
+ onClick: () => setMenuAbierto((v) => !v),
2272
+ title: "Mostrar u ocultar columnas",
2273
+ "aria-label": "Mostrar u ocultar columnas",
2274
+ className: "inline-flex items-center gap-1.5 rounded-md border border-[var(--tema-borde)] px-2.5 py-1.5 text-xs font-medium text-[#3c3c3b] transition hover:bg-[var(--tema-badge-neutro)]",
2275
+ children: [
2276
+ /* @__PURE__ */ jsxRuntime.jsx(ICONO_COLUMNAS, { size: TAMANO_BADGE, "aria-hidden": "true" }),
2277
+ "Columnas"
2278
+ ]
2279
+ }
2280
+ ),
2281
+ /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: "plataforma.tabla_columnas" }),
2282
+ /* @__PURE__ */ jsxRuntime.jsxs(
2283
+ "button",
2284
+ {
2285
+ type: "button",
2286
+ onClick: restablecer,
2287
+ title: "Restablecer orden y tama\xF1o de columnas",
2288
+ "aria-label": "Restablecer columnas",
2289
+ className: "inline-flex items-center gap-1.5 rounded-md border border-[var(--tema-borde)] px-2.5 py-1.5 text-xs font-medium text-[#3c3c3b] transition hover:bg-[var(--tema-badge-neutro)]",
2290
+ children: [
2291
+ /* @__PURE__ */ jsxRuntime.jsx(ICONO_RESTABLECER, { size: TAMANO_BADGE, "aria-hidden": "true" }),
2292
+ "Restablecer"
2293
+ ]
2294
+ }
2295
+ ),
2296
+ menuAbierto && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute left-0 top-full z-30 mt-1 w-56 rounded-md border border-[var(--tema-borde)] bg-[var(--tema-fondo)] p-2 shadow-lg", children: [
2297
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "flex items-center gap-1 px-1 pb-1 text-[10px] font-semibold uppercase tracking-wide text-[#9a9a9a]", children: "Columnas visibles" }),
2298
+ idsOrdenados.map((id) => {
2299
+ const col2 = porId.get(id);
2300
+ if (!col2) return null;
2301
+ const visible = !ocultas.has(id);
2302
+ const Icono = visible ? ICONO_COLUMNA_VISIBLE : ICONO_COLUMNA_OCULTA;
2303
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2304
+ "button",
2305
+ {
2306
+ type: "button",
2307
+ onClick: () => alternarVisible(id),
2308
+ className: "flex w-full items-center gap-2 rounded px-1.5 py-1 text-left text-sm text-[#3c3c3b] transition hover:bg-[var(--tema-badge-neutro)]",
2309
+ children: [
2310
+ /* @__PURE__ */ jsxRuntime.jsx(Icono, { size: 14, "aria-hidden": "true", className: visible ? "" : "opacity-40" }),
2311
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: visible ? "" : "opacity-50", children: col2.encabezadoMenu ?? col2.encabezado })
2312
+ ]
2313
+ },
2314
+ id
2315
+ );
2316
+ })
2317
+ ] }),
2318
+ barraExtra
2319
+ ] });
2320
+ return /* @__PURE__ */ jsxRuntime.jsx(MarcoTabla, { clave: clavePrefs, etiqueta, barra, ajuste, pie, children: /* @__PURE__ */ jsxRuntime.jsxs(
2321
+ "table",
2322
+ {
2323
+ className: "table-fixed divide-y divide-[var(--tema-borde)] text-sm",
2324
+ style: { width: anchoTabla, minWidth: "100%" },
2325
+ children: [
2326
+ /* @__PURE__ */ jsxRuntime.jsxs("colgroup", { children: [
2327
+ columnaFija && /* @__PURE__ */ jsxRuntime.jsx("col", { style: { width: ANCHO_FIJA } }),
2328
+ fijos.map((id) => /* @__PURE__ */ jsxRuntime.jsx("col", { style: { width: anchoEfectivo(id) } }, id)),
2329
+ idsVisibles.map((id) => /* @__PURE__ */ jsxRuntime.jsx("col", { style: { width: anchoEfectivo(id) } }, id)),
2330
+ columnaAcciones && /* @__PURE__ */ jsxRuntime.jsx("col", { style: { width: ANCHO_ACCIONES } })
2331
+ ] }),
2332
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "text-left text-xs font-medium text-[#807f7f]", children: [
2333
+ columnaFija && /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2", ...atributosCasilla, children: columnaFija.encabezado ?? null }),
2334
+ fijos.map((id, i) => {
2335
+ const col2 = porId.get(id);
2336
+ return /* @__PURE__ */ jsxRuntime.jsxs("th", { className: "relative overflow-hidden px-3 py-2", ...atributosFijo(i), children: [
2337
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `flex items-center gap-1 truncate ${col2.alineado === "right" ? "justify-end" : ""}`, children: contenidoEncabezado(col2) }),
2338
+ /* @__PURE__ */ jsxRuntime.jsx(
2339
+ ResizerColumna,
2340
+ {
2341
+ anchoActual: anchoEfectivo(id),
2342
+ onFinRedimension: (nuevoAncho) => redimensionar(id, nuevoAncho)
2343
+ }
2344
+ )
2345
+ ] }, id);
2346
+ }),
2347
+ idsVisibles.map((id) => {
2348
+ const col2 = porId.get(id);
2349
+ if (!col2) return null;
2350
+ const idxOrden = idsOrdenados.indexOf(id);
2351
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2352
+ "th",
2353
+ {
2354
+ className: "relative overflow-hidden px-3 py-2",
2355
+ onDragOver: (e) => e.preventDefault(),
2356
+ onDrop: () => {
2357
+ if (arrastrando.current !== null && arrastrando.current !== idxOrden) {
2358
+ mover(arrastrando.current, idxOrden);
2359
+ }
2360
+ arrastrando.current = null;
2361
+ },
2362
+ children: [
2363
+ /* @__PURE__ */ jsxRuntime.jsxs(
2364
+ "span",
2365
+ {
2366
+ className: `flex items-center gap-1 truncate ${col2.alineado === "right" ? "justify-end" : ""}`,
2367
+ children: [
2368
+ /* @__PURE__ */ jsxRuntime.jsx(
2369
+ "span",
2370
+ {
2371
+ draggable: true,
2372
+ onDragStart: () => {
2373
+ arrastrando.current = idxOrden;
2374
+ },
2375
+ onDragEnd: () => {
2376
+ arrastrando.current = null;
2377
+ },
2378
+ "aria-label": "Arrastrar para reordenar la columna",
2379
+ className: "shrink-0 cursor-grab text-[#c4c4c4] hover:text-[var(--tema-primario)]",
2380
+ children: /* @__PURE__ */ jsxRuntime.jsx(ICONO_ARRASTRAR_COLUMNA, { size: 12, "aria-hidden": "true" })
2381
+ }
2382
+ ),
2383
+ contenidoEncabezado(col2)
2384
+ ]
2385
+ }
2386
+ ),
2387
+ /* @__PURE__ */ jsxRuntime.jsx(
2388
+ ResizerColumna,
2389
+ {
2390
+ anchoActual: anchoEfectivo(id),
2391
+ onFinRedimension: (nuevoAncho) => redimensionar(id, nuevoAncho)
2392
+ }
2393
+ )
2394
+ ]
2395
+ },
2396
+ id
2397
+ );
2398
+ }),
2399
+ columnaAcciones && /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2", ...fija("der"), children: columnaAcciones.encabezado ?? null })
2400
+ ] }) }),
2401
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-[var(--tema-borde)]", children: filasOrdenadas.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan: colSpanTotal, className: "px-3 py-6 text-center text-sm text-[#807f7f]", children: mensajeVacio }) }) : filasOrdenadas.map((fila) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "text-[#3c3c3b]", children: [
2402
+ columnaFija && /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2", ...atributosCasilla, children: columnaFija.celda(fila) }),
2403
+ fijos.map((id, i) => {
2404
+ const col2 = porId.get(id);
2405
+ return /* @__PURE__ */ jsxRuntime.jsx(
2406
+ "td",
2407
+ {
2408
+ ...atributosFijo(i),
2409
+ className: `overflow-hidden px-3 py-2 align-top ${col2.alineado === "right" ? "text-right tabular-nums" : ""}`,
2410
+ children: col2.celda(fila)
2411
+ },
2412
+ id
2413
+ );
2414
+ }),
2415
+ idsVisibles.map((id) => {
2416
+ const col2 = porId.get(id);
2417
+ if (!col2) return null;
2418
+ return /* @__PURE__ */ jsxRuntime.jsx(
2419
+ "td",
2420
+ {
2421
+ className: `overflow-hidden px-3 py-2 align-top ${col2.alineado === "right" ? "text-right tabular-nums" : ""}`,
2422
+ children: col2.celda(fila)
2423
+ },
2424
+ id
2425
+ );
2426
+ }),
2427
+ columnaAcciones && /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2", ...fija("der"), children: columnaAcciones.celda(fila) })
2428
+ ] }, claveFila(fila))) })
2429
+ ]
2430
+ }
2431
+ ) });
2432
+ }
2433
+ function EncabezadoOrdenable({
2434
+ columnaId,
2435
+ contenido,
2436
+ etiqueta,
2437
+ orden,
2438
+ onAlternar
2439
+ }) {
2440
+ const activa = orden?.id === columnaId;
2441
+ const dir = activa ? orden.dir : null;
2442
+ const Flecha = dir === "asc" ? ICONO_ORDENAR_ASC : dir === "desc" ? ICONO_ORDENAR_DESC : ICONO_ORDENAR_NEUTRO;
2443
+ const descripcionDir = dir === "asc" ? "ascendente" : dir === "desc" ? "descendente" : "sin ordenar";
2444
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2445
+ "button",
2446
+ {
2447
+ type: "button",
2448
+ onClick: onAlternar,
2449
+ title: "Ordena solo la pagina visible (no todo el universo)",
2450
+ "aria-label": `Ordenar por ${etiqueta} en esta pagina (orden actual: ${descripcionDir})`,
2451
+ className: `inline-flex items-center gap-1 truncate font-medium transition hover:text-[var(--tema-primario)] ${activa ? "text-[var(--tema-primario)]" : ""}`,
2452
+ children: [
2453
+ contenido,
2454
+ /* @__PURE__ */ jsxRuntime.jsx(Flecha, { size: 12, "aria-hidden": "true", className: activa ? "" : "opacity-40" })
2455
+ ]
2456
+ }
2457
+ );
2458
+ }
2459
+ function ResizerColumna({
2460
+ anchoActual,
2461
+ onFinRedimension
2462
+ }) {
2463
+ function onMouseDown(e) {
2464
+ e.preventDefault();
2465
+ e.stopPropagation();
2466
+ const inicioX = e.clientX;
2467
+ const inicioAncho = anchoActual;
2468
+ let ultimoAncho = anchoActual;
2469
+ function onMove(ev) {
2470
+ ultimoAncho = Math.max(60, inicioAncho + (ev.clientX - inicioX));
2471
+ onFinRedimension(ultimoAncho);
2472
+ }
2473
+ function onUp() {
2474
+ onFinRedimension(ultimoAncho);
2475
+ window.removeEventListener("mousemove", onMove);
2476
+ window.removeEventListener("mouseup", onUp);
2477
+ document.body.style.cursor = "";
2478
+ document.body.style.userSelect = "";
2479
+ }
2480
+ document.body.style.cursor = "col-resize";
2481
+ document.body.style.userSelect = "none";
2482
+ window.addEventListener("mousemove", onMove);
2483
+ window.addEventListener("mouseup", onUp);
2484
+ }
2485
+ return /* @__PURE__ */ jsxRuntime.jsx(
2486
+ "span",
2487
+ {
2488
+ role: "separator",
2489
+ "aria-label": "Redimensionar columna",
2490
+ "aria-orientation": "vertical",
2491
+ onMouseDown,
2492
+ draggable: false,
2493
+ onDragStart: (e) => e.preventDefault(),
2494
+ className: "absolute right-0 top-0 z-10 h-full w-2 cursor-col-resize select-none border-r-2 border-transparent hover:border-[var(--tema-primario)]"
2495
+ }
2496
+ );
2497
+ }
2498
+ function EtiquetaCol({
2499
+ icono: Icono,
2500
+ texto,
2501
+ info
2502
+ }) {
2503
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
2504
+ Icono && /* @__PURE__ */ jsxRuntime.jsx(Icono, { size: TAMANO_BADGE, "aria-hidden": "true" }),
2505
+ texto,
2506
+ info && /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: info })
2507
+ ] });
2508
+ }
2509
+ function Campo({
2510
+ id,
2511
+ name,
2512
+ etiqueta,
2513
+ defaultValue,
2514
+ tipo = "text",
2515
+ icono: Icono
2516
+ }) {
2517
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
2518
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: id, className: "inline-flex items-center gap-1 text-xs font-medium text-[#3c3c3b]", children: [
2519
+ Icono ? /* @__PURE__ */ jsxRuntime.jsx(Icono, { size: TAMANO_BADGE, "aria-hidden": "true" }) : null,
2520
+ etiqueta
2521
+ ] }),
2522
+ /* @__PURE__ */ jsxRuntime.jsx(
2523
+ "input",
2524
+ {
2525
+ id,
2526
+ name,
2527
+ type: tipo,
2528
+ defaultValue,
2529
+ className: "w-full rounded-md border border-[var(--tema-borde)] px-2 py-1.5 text-sm focus:border-[var(--tema-primario)] focus:outline-none"
2530
+ }
2531
+ )
2532
+ ] });
2533
+ }
2534
+ function CampoSelect({
2535
+ id,
2536
+ name,
2537
+ etiqueta,
2538
+ defaultValue,
2539
+ opciones,
2540
+ icono: Icono
2541
+ }) {
2542
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
2543
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: id, className: "inline-flex items-center gap-1 text-xs font-medium text-[#3c3c3b]", children: [
2544
+ Icono ? /* @__PURE__ */ jsxRuntime.jsx(Icono, { size: TAMANO_BADGE, "aria-hidden": "true" }) : null,
2545
+ etiqueta
2546
+ ] }),
2547
+ /* @__PURE__ */ jsxRuntime.jsx(
2548
+ "select",
2549
+ {
2550
+ id,
2551
+ name,
2552
+ defaultValue,
2553
+ className: "w-full rounded-md border border-[var(--tema-borde)] bg-[var(--tema-fondo)] px-2 py-1.5 text-sm focus:border-[var(--tema-primario)] focus:outline-none",
2554
+ children: opciones.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.valor, children: o.etiqueta }, o.valor))
2555
+ }
2556
+ )
2557
+ ] });
2558
+ }
2559
+ function IconoConEtiqueta({ Icono, texto, info }) {
2560
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
2561
+ /* @__PURE__ */ jsxRuntime.jsx(Icono, { size: 16, "aria-hidden": "true" }),
2562
+ texto,
2563
+ info && /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: info })
2564
+ ] });
2565
+ }
2566
+ function MetricaKpi({
2567
+ etiqueta,
2568
+ valor,
2569
+ icono: Icono,
2570
+ destacada = false,
2571
+ sub,
2572
+ progreso,
2573
+ info
2574
+ }) {
2575
+ const claseTarjeta = destacada ? "rounded-lg border-2 border-[var(--tema-primario)] bg-[var(--tema-badge-neutro)] px-4 py-3" : "rounded-lg border border-[var(--tema-borde)] px-4 py-3";
2576
+ const claseEtiqueta = destacada ? "flex items-center gap-1 text-xs font-medium text-[var(--tema-primario)]" : "flex items-center gap-1 text-xs text-[#807f7f]";
2577
+ const claseValor = destacada ? "mt-1 text-2xl font-bold text-[var(--tema-primario-oscuro)]" : "mt-1 text-lg font-semibold text-[var(--tema-primario-oscuro)]";
2578
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: claseTarjeta, children: [
2579
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: claseEtiqueta, children: [
2580
+ /* @__PURE__ */ jsxRuntime.jsx(Icono, { size: 12, "aria-hidden": "true" }),
2581
+ etiqueta,
2582
+ info && /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: info })
2583
+ ] }),
2584
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: claseValor, children: valor }),
2585
+ sub ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs text-[#807f7f]", children: sub }) : null,
2586
+ progreso ? /* @__PURE__ */ jsxRuntime.jsx(
2587
+ "div",
2588
+ {
2589
+ role: "progressbar",
2590
+ "aria-label": progreso.etiqueta,
2591
+ "aria-valuemin": 0,
2592
+ "aria-valuemax": 100,
2593
+ "aria-valuenow": progreso.porcentaje,
2594
+ className: "mt-2 h-1.5 w-full overflow-hidden rounded-full bg-[var(--tema-borde)]",
2595
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2596
+ "div",
2597
+ {
2598
+ className: "h-full rounded-full bg-[var(--tema-primario)] transition-all",
2599
+ style: { width: `${progreso.porcentaje}%` }
2600
+ }
2601
+ )
2602
+ }
2603
+ ) : null
2604
+ ] });
2605
+ }
2606
+ function GrupoMetricas({ titulo, icono: Icono, children }) {
2607
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "mt-4", children: [
2608
+ /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-[#807f7f]", children: [
2609
+ /* @__PURE__ */ jsxRuntime.jsx(Icono, { size: 13, "aria-hidden": "true" }),
2610
+ titulo
2611
+ ] }),
2612
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 grid grid-cols-2 gap-3 sm:grid-cols-4", children })
2613
+ ] });
2614
+ }
2615
+ function RejillaMetricas({ children }) {
2616
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-4", children });
2617
+ }
2618
+ function Rotulo({ texto, info }) {
2619
+ return /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "flex items-center gap-1 text-xs font-medium text-[#565656]", children: [
2620
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: texto }),
2621
+ /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: info })
2622
+ ] });
2623
+ }
2624
+ function AccionesFila({
2625
+ etiqueta = "M\xE1s acciones",
2626
+ children
2627
+ }) {
2628
+ const acciones = react.Children.toArray(children).filter(
2629
+ (c) => react.isValidElement(c) || typeof c === "string" && c.trim() !== ""
2630
+ );
2631
+ const [abierto, setAbierto] = react.useState(false);
2632
+ const [pos, setPos] = react.useState({
2633
+ top: 0,
2634
+ right: 0
2635
+ });
2636
+ const boton = react.useRef(null);
2637
+ const menu = react.useRef(null);
2638
+ react.useEffect(() => {
2639
+ if (!abierto) return;
2640
+ const tecla = (e) => {
2641
+ if (e.key === "Escape") {
2642
+ setAbierto(false);
2643
+ boton.current?.focus();
2644
+ }
2645
+ };
2646
+ const fuera = (e) => {
2647
+ const t = e.target;
2648
+ if (!menu.current?.contains(t) && !boton.current?.contains(t))
2649
+ setAbierto(false);
2650
+ };
2651
+ const desplazar = (e) => {
2652
+ if (menu.current?.contains(e.target) || hayDialogo()) return;
2653
+ setAbierto(false);
2654
+ };
2655
+ document.addEventListener("keydown", tecla);
2656
+ document.addEventListener("mousedown", fuera);
2657
+ window.addEventListener("scroll", desplazar, true);
2658
+ return () => {
2659
+ document.removeEventListener("keydown", tecla);
2660
+ document.removeEventListener("mousedown", fuera);
2661
+ window.removeEventListener("scroll", desplazar, true);
2662
+ };
2663
+ }, [abierto]);
2664
+ function hayDialogo() {
2665
+ return Boolean(menu.current?.querySelector('[aria-modal="true"]'));
2666
+ }
2667
+ if (acciones.length <= 2) {
2668
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end gap-1 whitespace-nowrap", children: acciones });
2669
+ }
2670
+ function alternar() {
2671
+ const r = boton.current?.getBoundingClientRect();
2672
+ if (r)
2673
+ setPos({
2674
+ top: r.bottom + 4,
2675
+ right: Math.max(8, window.innerWidth - r.right)
2676
+ });
2677
+ setAbierto((v) => !v);
2678
+ }
2679
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end", children: [
2680
+ /* @__PURE__ */ jsxRuntime.jsx(
2681
+ "button",
2682
+ {
2683
+ ref: boton,
2684
+ type: "button",
2685
+ onClick: alternar,
2686
+ "aria-label": etiqueta,
2687
+ title: etiqueta,
2688
+ "aria-haspopup": "menu",
2689
+ "aria-expanded": abierto,
2690
+ className: "inline-flex items-center rounded-md border border-[var(--tema-borde)] p-1 text-[#565656] transition hover:bg-[var(--tema-badge-neutro)] hover:text-[var(--tema-primario)]",
2691
+ children: /* @__PURE__ */ jsxRuntime.jsx(ICONO_ACCIONES_FILA, { size: TAMANO_BADGE, "aria-hidden": "true" })
2692
+ }
2693
+ ),
2694
+ abierto && reactDom.createPortal(
2695
+ /* @__PURE__ */ jsxRuntime.jsx(
2696
+ "div",
2697
+ {
2698
+ ref: menu,
2699
+ role: "menu",
2700
+ "aria-label": etiqueta,
2701
+ style: { top: pos.top, right: pos.right },
2702
+ onClickCapture: () => setTimeout(() => {
2703
+ if (!hayDialogo()) setAbierto(false);
2704
+ }, 0),
2705
+ className: "fixed z-50 flex min-w-40 flex-col items-stretch gap-1 rounded-md border border-[var(--tema-borde)] bg-[var(--tema-fondo)] p-1.5 text-left shadow-lg [&>*]:justify-start",
2706
+ children: acciones
2707
+ }
2708
+ ),
2709
+ document.body
2710
+ )
2711
+ ] });
2712
+ }
2713
+ function BotonAccionFila({
2714
+ icono: Icono,
2715
+ etiqueta,
2716
+ ariaLabel,
2717
+ onClick,
2718
+ disabled = false,
2719
+ info
2720
+ }) {
2721
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
2722
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className: CLASE_ACCION_FILA, children: [
2723
+ /* @__PURE__ */ jsxRuntime.jsx(Icono, { size: 12, "aria-hidden": "true" }),
2724
+ etiqueta
2725
+ ] }),
2726
+ info && /* @__PURE__ */ jsxRuntime.jsx(InfoExplicacion, { id: info })
2727
+ ] });
2728
+ }
2729
+
2730
+ exports.AccionConDialogo = AccionConDialogo;
2731
+ exports.AccionesFila = AccionesFila;
2732
+ exports.Aviso = Aviso;
2733
+ exports.AvisoAccionEstado = AvisoAccionEstado;
2734
+ exports.BarraListado = BarraListado;
2735
+ exports.BarraProgreso = BarraProgreso;
2736
+ exports.BotonAccionAviso = BotonAccionAviso;
2737
+ exports.BotonAccionFila = BotonAccionFila;
2738
+ exports.CLASE_ACCION_FILA = CLASE_ACCION_FILA;
2739
+ exports.CLASE_AYUDA_CAMPO = CLASE_AYUDA_CAMPO;
2740
+ exports.CLASE_BOTON_CONTORNO = CLASE_BOTON_CONTORNO;
2741
+ exports.CLASE_BOTON_ICONO = CLASE_BOTON_ICONO;
2742
+ exports.CLASE_BOTON_PELIGRO = CLASE_BOTON_PELIGRO;
2743
+ exports.CLASE_BOTON_PEQUENO = CLASE_BOTON_PEQUENO;
2744
+ exports.CLASE_BOTON_PRIMARIO = CLASE_BOTON_PRIMARIO;
2745
+ exports.CLASE_BOTON_PRIMARIO_COMPACTO = CLASE_BOTON_PRIMARIO_COMPACTO;
2746
+ exports.CLASE_BOTON_SECUNDARIO = CLASE_BOTON_SECUNDARIO;
2747
+ exports.CLASE_CAMPO = CLASE_CAMPO;
2748
+ exports.CLASE_CAMPO_COMPACTO = CLASE_CAMPO_COMPACTO;
2749
+ exports.CLASE_ERROR_CAMPO = CLASE_ERROR_CAMPO;
2750
+ exports.CLASE_ETIQUETA_CAMPO = CLASE_ETIQUETA_CAMPO;
2751
+ exports.CLASE_EXITO_CAMPO = CLASE_EXITO_CAMPO;
2752
+ exports.CLASE_REGION_LISTADO = CLASE_REGION_LISTADO;
2753
+ exports.CLASE_TARJETA = CLASE_TARJETA;
2754
+ exports.CLASE_TEXTO_ATENUADO = CLASE_TEXTO_ATENUADO;
2755
+ exports.CLASE_TITULO_PAGINA = CLASE_TITULO_PAGINA;
2756
+ exports.CLASE_TITULO_SECCION = CLASE_TITULO_SECCION;
2757
+ exports.Campo = Campo;
2758
+ exports.CampoSelect = CampoSelect;
2759
+ exports.CascaronApp = CascaronApp;
2760
+ exports.CeldaIdentidad = CeldaIdentidad;
2761
+ exports.ETIQUETAS_SEMAFORO = ETIQUETAS_SEMAFORO;
2762
+ exports.EXPLICACIONES = EXPLICACIONES;
2763
+ exports.EncabezadoOrdenUrl = EncabezadoOrdenUrl;
2764
+ exports.EnlaceNav = EnlaceNav;
2765
+ exports.EstadoVacio = EstadoVacio;
2766
+ exports.EstadoVacioFila = EstadoVacioFila;
2767
+ exports.EtiquetaCol = EtiquetaCol;
2768
+ exports.FechaHoraLocal = FechaHoraLocal;
2769
+ exports.GrupoMetricas = GrupoMetricas;
2770
+ exports.ICONOS_ESTATUS = ICONOS_ESTATUS;
2771
+ exports.ICONOS_SEMAFORO = ICONOS_SEMAFORO;
2772
+ exports.ICONO_ACCIONES_FILA = ICONO_ACCIONES_FILA;
2773
+ exports.ICONO_ALTA = ICONO_ALTA;
2774
+ exports.ICONO_API_KEY = ICONO_API_KEY;
2775
+ exports.ICONO_ARRASTRAR_COLUMNA = ICONO_ARRASTRAR_COLUMNA;
2776
+ exports.ICONO_AVISO = ICONO_AVISO;
2777
+ exports.ICONO_BAJA = ICONO_BAJA;
2778
+ exports.ICONO_BUSCAR = ICONO_BUSCAR;
2779
+ exports.ICONO_CAMPANA = ICONO_CAMPANA;
2780
+ exports.ICONO_CARGANDO = ICONO_CARGANDO;
2781
+ exports.ICONO_CERRAR = ICONO_CERRAR;
2782
+ exports.ICONO_CERRAR_SESION = ICONO_CERRAR_SESION;
2783
+ exports.ICONO_COLUMNAS = ICONO_COLUMNAS;
2784
+ exports.ICONO_COLUMNA_OCULTA = ICONO_COLUMNA_OCULTA;
2785
+ exports.ICONO_COLUMNA_VISIBLE = ICONO_COLUMNA_VISIBLE;
2786
+ exports.ICONO_COL_CODIGO = ICONO_COL_CODIGO;
2787
+ exports.ICONO_COL_DOCUMENTO = ICONO_COL_DOCUMENTO;
2788
+ exports.ICONO_COL_ESTADO = ICONO_COL_ESTADO;
2789
+ exports.ICONO_COL_ETIQUETA = ICONO_COL_ETIQUETA;
2790
+ exports.ICONO_COL_FECHA = ICONO_COL_FECHA;
2791
+ exports.ICONO_COL_FECHA_HORA = ICONO_COL_FECHA_HORA;
2792
+ exports.ICONO_COL_MECANISMO = ICONO_COL_MECANISMO;
2793
+ exports.ICONO_COL_NOMBRE = ICONO_COL_NOMBRE;
2794
+ exports.ICONO_COL_TOTAL = ICONO_COL_TOTAL;
2795
+ exports.ICONO_COL_USUARIO = ICONO_COL_USUARIO;
2796
+ exports.ICONO_CONFIRMAR = ICONO_CONFIRMAR;
2797
+ exports.ICONO_COPIAR = ICONO_COPIAR;
2798
+ exports.ICONO_DEFAULT = ICONO_DEFAULT;
2799
+ exports.ICONO_DENSIDAD_COMODA = ICONO_DENSIDAD_COMODA;
2800
+ exports.ICONO_DENSIDAD_COMPACTA = ICONO_DENSIDAD_COMPACTA;
2801
+ exports.ICONO_DESCARGAR = ICONO_DESCARGAR;
2802
+ exports.ICONO_DESHABILITAR = ICONO_DESHABILITAR;
2803
+ exports.ICONO_DURACION = ICONO_DURACION;
2804
+ exports.ICONO_EDITAR = ICONO_EDITAR;
2805
+ exports.ICONO_EMAIL = ICONO_EMAIL;
2806
+ exports.ICONO_ERROR = ICONO_ERROR;
2807
+ exports.ICONO_EXPORTAR = ICONO_EXPORTAR;
2808
+ exports.ICONO_EXPORTAR_CSV = ICONO_EXPORTAR_CSV;
2809
+ exports.ICONO_FILTRO = ICONO_FILTRO;
2810
+ exports.ICONO_GENERAR = ICONO_GENERAR;
2811
+ exports.ICONO_GUARDAR = ICONO_GUARDAR;
2812
+ exports.ICONO_INACTIVAR = ICONO_INACTIVAR;
2813
+ exports.ICONO_INFO_EXPLICACION = ICONO_INFO_EXPLICACION;
2814
+ exports.ICONO_INICIAR_SESION = ICONO_INICIAR_SESION;
2815
+ exports.ICONO_INVITACION_GENERICA = ICONO_INVITACION_GENERICA;
2816
+ exports.ICONO_KPI_ALERTA = ICONO_KPI_ALERTA;
2817
+ exports.ICONO_KPI_BLOQUEO = ICONO_KPI_BLOQUEO;
2818
+ exports.ICONO_KPI_CONSUMO = ICONO_KPI_CONSUMO;
2819
+ exports.ICONO_KPI_CONTEO = ICONO_KPI_CONTEO;
2820
+ exports.ICONO_KPI_CREDENCIALES = ICONO_KPI_CREDENCIALES;
2821
+ exports.ICONO_KPI_RENDIMIENTO = ICONO_KPI_RENDIMIENTO;
2822
+ exports.ICONO_KPI_RITMO = ICONO_KPI_RITMO;
2823
+ exports.ICONO_LIMPIAR = ICONO_LIMPIAR;
2824
+ exports.ICONO_LINK = ICONO_LINK;
2825
+ exports.ICONO_LOGIN_MICROSOFT = ICONO_LOGIN_MICROSOFT;
2826
+ exports.ICONO_NAV_ACCESOS = ICONO_NAV_ACCESOS;
2827
+ exports.ICONO_NAV_ANALITICA = ICONO_NAV_ANALITICA;
2828
+ exports.ICONO_NAV_API_KEYS = ICONO_NAV_API_KEYS;
2829
+ exports.ICONO_NAV_BITACORA = ICONO_NAV_BITACORA;
2830
+ exports.ICONO_NAV_CLIENTE = ICONO_NAV_CLIENTE;
2831
+ exports.ICONO_NAV_COLAPSAR = ICONO_NAV_COLAPSAR;
2832
+ exports.ICONO_NAV_CONFIGURACION = ICONO_NAV_CONFIGURACION;
2833
+ exports.ICONO_NAV_DASHBOARD = ICONO_NAV_DASHBOARD;
2834
+ exports.ICONO_NAV_ENTIDADES = ICONO_NAV_ENTIDADES;
2835
+ exports.ICONO_NAV_ETIQUETAS = ICONO_NAV_ETIQUETAS;
2836
+ exports.ICONO_NAV_EXPANDIR = ICONO_NAV_EXPANDIR;
2837
+ exports.ICONO_NAV_EXTRACCIONES = ICONO_NAV_EXTRACCIONES;
2838
+ exports.ICONO_NAV_INVITACIONES = ICONO_NAV_INVITACIONES;
2839
+ exports.ICONO_NAV_JOBS = ICONO_NAV_JOBS;
2840
+ exports.ICONO_NAV_MANUAL = ICONO_NAV_MANUAL;
2841
+ exports.ICONO_NAV_MENU_MOVIL = ICONO_NAV_MENU_MOVIL;
2842
+ exports.ICONO_NAV_NOTIFICACIONES = ICONO_NAV_NOTIFICACIONES;
2843
+ exports.ICONO_NAV_PAQUETES = ICONO_NAV_PAQUETES;
2844
+ exports.ICONO_NAV_PLANES = ICONO_NAV_PLANES;
2845
+ exports.ICONO_NAV_SALUD = ICONO_NAV_SALUD;
2846
+ exports.ICONO_NAV_TEMA = ICONO_NAV_TEMA;
2847
+ exports.ICONO_NAV_USUARIOS = ICONO_NAV_USUARIOS;
2848
+ exports.ICONO_OK = ICONO_OK;
2849
+ exports.ICONO_ORDENAR_ASC = ICONO_ORDENAR_ASC;
2850
+ exports.ICONO_ORDENAR_DESC = ICONO_ORDENAR_DESC;
2851
+ exports.ICONO_ORDENAR_NEUTRO = ICONO_ORDENAR_NEUTRO;
2852
+ exports.ICONO_PAGINA_ANTERIOR = ICONO_PAGINA_ANTERIOR;
2853
+ exports.ICONO_PAGINA_SIGUIENTE = ICONO_PAGINA_SIGUIENTE;
2854
+ exports.ICONO_PERIODO = ICONO_PERIODO;
2855
+ exports.ICONO_REACTIVAR = ICONO_REACTIVAR;
2856
+ exports.ICONO_REACTIVAR_ETIQUETA = ICONO_REACTIVAR_ETIQUETA;
2857
+ exports.ICONO_REINTENTAR = ICONO_REINTENTAR;
2858
+ exports.ICONO_RESTABLECER = ICONO_RESTABLECER;
2859
+ exports.ICONO_REVOCAR_KEY = ICONO_REVOCAR_KEY;
2860
+ exports.ICONO_ROTAR_KEY = ICONO_ROTAR_KEY;
2861
+ exports.ICONO_SEGURIDAD = ICONO_SEGURIDAD;
2862
+ exports.ICONO_SELECCIONADO = ICONO_SELECCIONADO;
2863
+ exports.ICONO_SILENCIAR = ICONO_SILENCIAR;
2864
+ exports.ICONO_UBICACION = ICONO_UBICACION;
2865
+ exports.ICONO_VER_TODOS = ICONO_VER_TODOS;
2866
+ exports.ICONO_VIGENCIA = ICONO_VIGENCIA;
2867
+ exports.ICONO_VISTA_TABLA = ICONO_VISTA_TABLA;
2868
+ exports.ICONO_VISTA_TARJETAS = ICONO_VISTA_TARJETAS;
2869
+ exports.ICONO_VOLVER = ICONO_VOLVER;
2870
+ exports.IconoConEtiqueta = IconoConEtiqueta;
2871
+ exports.InfoExplicacion = InfoExplicacion;
2872
+ exports.Insignia = Insignia;
2873
+ exports.LinkBase = LinkBase;
2874
+ exports.LinkComponentProvider = LinkComponentProvider;
2875
+ exports.ListadoNavProvider = ListadoNavProvider;
2876
+ exports.MarcoTabla = MarcoTabla;
2877
+ exports.Metrica = Metrica;
2878
+ exports.MetricaKpi = MetricaKpi;
2879
+ exports.Migas = Migas;
2880
+ exports.ModalPanel = ModalPanel;
2881
+ exports.NOMBRE_PRODUCTO_DEFAULT = NOMBRE_PRODUCTO_DEFAULT;
2882
+ exports.NO_DISPONIBLE = NO_DISPONIBLE;
2883
+ exports.NoDisponible = NoDisponible;
2884
+ exports.OverlayCargando = OverlayCargando;
2885
+ exports.PaginaContenido = PaginaContenido;
2886
+ exports.PaginaListado = PaginaListado;
2887
+ exports.PaginadorLocal = PaginadorLocal;
2888
+ exports.PaginadorUrl = PaginadorUrl;
2889
+ exports.PantallaAcceso = PantallaAcceso;
2890
+ exports.PantallaCargando = PantallaCargando;
2891
+ exports.Pestanas = Pestanas;
2892
+ exports.PopoverHover = PopoverHover;
2893
+ exports.RejillaMetricas = RejillaMetricas;
2894
+ exports.Rotulo = Rotulo;
2895
+ exports.SeccionNav = SeccionNav;
2896
+ exports.SidebarColapsable = SidebarColapsable;
2897
+ exports.SpinnerMarca = SpinnerMarca;
2898
+ exports.SubNav = SubNav;
2899
+ exports.TAMANOS_LISTADO = TAMANOS_LISTADO;
2900
+ exports.TAMANO_BADGE = TAMANO_BADGE;
2901
+ exports.TAMANO_BOTON = TAMANO_BOTON;
2902
+ exports.TAMANO_LISTADO_DEFAULT = TAMANO_LISTADO_DEFAULT;
2903
+ exports.TEMA_DEFAULT = TEMA_DEFAULT;
2904
+ exports.TablaConfigurable = TablaConfigurable;
2905
+ exports.TablaPlana = TablaPlana;
2906
+ exports.Tarjeta = Tarjeta;
2907
+ exports.ThInfo = ThInfo;
2908
+ exports.UsuarioTablas = UsuarioTablas;
2909
+ exports.aplicarListado = aplicarListado;
2910
+ exports.calcularPosicionPopover = calcularPosicionPopover;
2911
+ exports.col = col;
2912
+ exports.etiquetaSemaforo = etiquetaSemaforo;
2913
+ exports.explicacionDe = explicacionDe;
2914
+ exports.fija = fija;
2915
+ exports.formatearBytes = formatearBytes;
2916
+ exports.formatearFechaHora = formatearFechaHora;
2917
+ exports.formatearFechaHoraCDMX = formatearFechaHoraCDMX;
2918
+ exports.formatearRangoFechaHora = formatearRangoFechaHora;
2919
+ exports.formatoDias = formatoDias;
2920
+ exports.formatoEntero = formatoEntero;
2921
+ exports.formatoFecha = formatoFecha;
2922
+ exports.formatoMes = formatoMes;
2923
+ exports.formatoMxn = formatoMxn;
2924
+ exports.formatoNumero = formatoNumero;
2925
+ exports.formatoPct = formatoPct;
2926
+ exports.hrefListado = hrefListado;
2927
+ exports.iconoDeEstatus = iconoDeEstatus;
2928
+ exports.iconoDeSemaforo = iconoDeSemaforo;
2929
+ exports.leerListado = leerListado;
2930
+ exports.normalizarTexto = normalizarTexto;
2931
+ exports.useAccionAviso = useAccionAviso;
2932
+ exports.useAccionConSpinner = useAccionConSpinner;
2933
+ exports.useNavegarListado = useNavegarListado;
2934
+ exports.usePaginacionLocal = usePaginacionLocal;
2935
+ exports.useTablaPreferencias = useTablaPreferencias;
2936
+ exports.variablesDeTema = variablesDeTema;
2937
+ exports.verificarCatalogo = verificarCatalogo;
1124
2938
  //# sourceMappingURL=index.cjs.map
1125
2939
  //# sourceMappingURL=index.cjs.map