@innertia-solutions/innertia-nuxt 0.1.10 → 0.1.12

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/app.config.ts +10 -10
  3. package/components/Auth/LoginForm.vue +1 -1
  4. package/components/DataPreview.vue +323 -0
  5. package/components/DataTable.vue +12 -12
  6. package/components/Forms/DatePicker.vue +64 -39
  7. package/components/Forms/Input.vue +8 -2
  8. package/components/Forms/Select.vue +56 -20
  9. package/components/Forms/SelectServer.vue +39 -34
  10. package/components/MobileBlocker.vue +5 -5
  11. package/components/MobileLoginPicker.vue +13 -13
  12. package/components/Organization/Picker.vue +3 -3
  13. package/components/PermissionsTree.vue +4 -4
  14. package/components/Table/Database.vue +5 -5
  15. package/components/Table/DownloadDropdown.vue +8 -8
  16. package/components/Table/Enterprise.vue +9 -9
  17. package/components/Table/FilterDropdown.vue +3 -3
  18. package/components/Table/Grid.vue +4 -4
  19. package/components/Table/Kanban.vue +3 -3
  20. package/components/Table/List.vue +6 -6
  21. package/components/Table/Standard.vue +69 -324
  22. package/components/Table/index.vue +13 -13
  23. package/components/TableExportable.vue +6 -6
  24. package/components/TableFilter.vue +4 -4
  25. package/components/Tree/Node.vue +187 -84
  26. package/components/Tree/Standard.vue +345 -57
  27. package/composables/useAuth.js +1 -1
  28. package/composables/useContext.js +53 -5
  29. package/composables/useMobileGuard.ts +21 -21
  30. package/composables/useOrganization.ts +3 -3
  31. package/css/tokens.css +90 -0
  32. package/docs/superpowers/plans/2026-05-27-nuxt-apps-to-contexts-rename.md +630 -0
  33. package/docs/superpowers/specs/2026-05-27-nuxt-apps-to-contexts-rename-design.md +113 -0
  34. package/middleware/{03.apps.global.ts → 03.contexts.global.ts} +25 -25
  35. package/package.json +1 -1
  36. package/theme.css +2 -0
  37. package/composables/useApp.ts +0 -46
package/CHANGELOG.md ADDED
@@ -0,0 +1,38 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@innertia-solutions/innertia-nuxt` are documented here.
4
+
5
+ ## [Unreleased]
6
+
7
+ ### BREAKING CHANGES
8
+
9
+ #### apps → contexts rename
10
+
11
+ Aligns frontend with innertia-laravel which already renamed the concept.
12
+
13
+ **Config (`nuxt.config.ts` in your product):**
14
+ ```diff
15
+ - innertia.apps: { backoffice: { ... }, technician: { ... } }
16
+ + innertia.contexts: { backoffice: { ... }, technician: { ... } }
17
+ ```
18
+
19
+ **Types:**
20
+ ```diff
21
+ - import type { AppDefinition } from '@innertia-solutions/innertia-nuxt/app.config'
22
+ + import type { ContextDefinition } from '@innertia-solutions/innertia-nuxt/app.config'
23
+
24
+ - import type { AppMobileMode } from '@innertia-solutions/innertia-nuxt/app.config'
25
+ + import type { ContextMobileMode } from '@innertia-solutions/innertia-nuxt/app.config'
26
+ ```
27
+
28
+ **Composables:**
29
+ ```diff
30
+ - const { current, all, accessible, canAccess } = useApp()
31
+ + const { current, all, accessible, canAccess } = useContext()
32
+ ```
33
+
34
+ `useApp()` has been removed. Use `useContext()` for all context/app logic. `useContext()` now also exposes: `contexts` (dict), `currentContext`, `availableContexts`, `switchContext()`, `confirmSwitch()`, `hasAccessToContext()`.
35
+
36
+ **Login body field:**
37
+
38
+ `performLogin()` now sends `context` instead of `app` in the POST body — matching what innertia-laravel now expects.
package/app.config.ts CHANGED
@@ -2,12 +2,12 @@
2
2
  //
3
3
  // appConfig: {
4
4
  // innertia: {
5
- // apps: { backoffice: {...}, technician: {...} },
5
+ // contexts: { backoffice: {...}, technician: {...} },
6
6
  // mobile: { breakpoint: 1024 },
7
7
  // },
8
8
  // }
9
9
  //
10
- // Si el producto no declara `apps`, el feature de contextos queda inactivo
10
+ // Si el producto no declara `contexts`, el feature de contextos queda inactivo
11
11
  // (middleware no redirige, picker no se muestra).
12
12
  export default defineAppConfig({
13
13
  innertia: {
@@ -121,11 +121,11 @@ export default defineAppConfig({
121
121
  },
122
122
 
123
123
  /**
124
- * Declaración de "apps" (contextos) del producto.
125
- * Cada app define un prefijo de URL que mapea a un contexto del backend
124
+ * Declaración de contextos del producto.
125
+ * Cada contexto define un prefijo de URL que mapea a un contexto del backend
126
126
  * (matching con `availableContexts` que devuelve `auth/me`).
127
127
  */
128
- apps: {} as Record<string, AppDefinition>,
128
+ contexts: {} as Record<string, ContextDefinition>,
129
129
 
130
130
  /**
131
131
  * Política mobile global. Se aplica si una `app` no tiene `mobile.mode` propio.
@@ -144,7 +144,7 @@ export default defineAppConfig({
144
144
 
145
145
  export type InnertiaMode = 'saas' | 'app'
146
146
 
147
- export type AppMobileMode = 'allow' | 'block' | 'redirect'
147
+ export type ContextMobileMode = 'allow' | 'block' | 'redirect'
148
148
 
149
149
  /** Color: nombre de paleta Tailwind o scale custom 50→950. */
150
150
  export type ColorScale = Partial<Record<50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950, string>>
@@ -175,8 +175,8 @@ export interface MenuItem {
175
175
  pattern?: string
176
176
  }
177
177
 
178
- export interface AppDefinition {
179
- /** Prefijo de URL — la ruta debe empezar con esto para considerarse "dentro" del app. */
178
+ export interface ContextDefinition {
179
+ /** Prefijo de URL — la ruta debe empezar con esto para considerarse "dentro" del contexto. */
180
180
  path: string
181
181
  /** Clave del contexto que matchea con `availableContexts` del backend. */
182
182
  context: string
@@ -186,13 +186,13 @@ export interface AppDefinition {
186
186
  description?: string
187
187
  /** Nombre del icono de @tabler/icons-vue (sin "Icon"). */
188
188
  icon?: string
189
- /** Ruta de login del app. */
189
+ /** Ruta de login del contexto. */
190
190
  loginPath: string
191
191
  /** Ruta home (post-login). */
192
192
  home: string
193
193
  /** Política de uso en mobile. */
194
194
  mobile: {
195
- mode: AppMobileMode
195
+ mode: ContextMobileMode
196
196
  /** Solo si mode === 'redirect' — adónde mandar desde mobile. */
197
197
  redirectTo?: string
198
198
  }
@@ -99,7 +99,7 @@ async function handleOauth(provider: OAuthProvider) {
99
99
 
100
100
  // ── Cross-links a otros contextos mobile-friendly (opcional) ───────────────
101
101
  const { isMobile } = useMobileGuard()
102
- const { all } = useApp()
102
+ const { all } = useContext()
103
103
  const otherMobileApps = computed(() => {
104
104
  if (!props.showCrossLinks) return []
105
105
  return all.value.filter((a: any) => a.path !== `/${props.context}` && a.mobile?.mode === 'allow')
@@ -0,0 +1,323 @@
1
+ <script setup>
2
+ /**
3
+ * <DataPreview> — Panel de preview overlay compartido por Table.Standard y Tree.Standard.
4
+ *
5
+ * Encapsula:
6
+ * - Panel absoluto a la derecha del container padre (position:relative)
7
+ * - Resize por drag del handle vertical en el borde izquierdo
8
+ * - Header con título + acciones + botones (abrir, eliminar, minimizar, cerrar)
9
+ * - Tabs inferior cuando se pasa `historyEndpoint` (Datos / Bitácora)
10
+ * - Cache de la fila previewada en sessionStorage cuando `cached`
11
+ * - Chip flotante (Teleport to body) compartido vía `useDockedPreviews` Pinia store
12
+ *
13
+ * El parent es dueño de `row` (v-model) — DataPreview lo seteoa a null al cerrar.
14
+ * El parent también maneja el click fuera + ESC porque conoce su containerRef.
15
+ *
16
+ * Slots:
17
+ * - header ({ row, close }) — título del panel
18
+ * - actions ({ row, close }) — botones extra al lado de los built-in
19
+ * - default ({ row, close }) — body del preview
20
+ */
21
+
22
+ import {
23
+ IconExternalLink, IconTrash, IconMinus, IconX, IconMaximize,
24
+ } from '@tabler/icons-vue'
25
+
26
+ const props = defineProps({
27
+ /** Activa el preview (típicamente derivado de !!slots.preview en el parent). */
28
+ enabled: { type: Boolean, default: false },
29
+ /** Nombre único para cache + dock id. */
30
+ name: { type: String, default: 'default' },
31
+ /** Si true, persiste la fila previewada en sessionStorage entre navegaciones. */
32
+ cached: { type: Boolean, default: false },
33
+ /** URL fija o (row) => url para botón "abrir externa". */
34
+ previewHref: { type: [String, Function], default: null },
35
+ /** Muestra botón eliminar (emite @delete). */
36
+ previewDeletable: { type: Boolean, default: false },
37
+ /** Porcentaje inicial de la tabla (0-100). */
38
+ splitRatio: { type: Number, default: 55 },
39
+ /** Endpoint del historial. Cuando se pasa, muestra tabs Datos/Bitácora. */
40
+ historyEndpoint: { type: String, default: null },
41
+ /** Container ref (HTMLElement) para medir el width al hacer resize. */
42
+ containerRef: { type: Object, default: null },
43
+ })
44
+
45
+ const emit = defineEmits(['delete'])
46
+
47
+ // v-model:row → parent es dueño de la fila activa.
48
+ const row = defineModel('row', { default: null })
49
+
50
+ const previewFromCache = ref(false)
51
+ const currentRatio = ref(props.splitRatio)
52
+ const panelRef = ref(null)
53
+ const previewTab = ref('datos')
54
+
55
+ const cacheKey = computed(() => `data-preview-${props.name}`)
56
+
57
+ const resolvedPreviewHref = computed(() => {
58
+ if (!props.previewHref || !row.value) return null
59
+ return typeof props.previewHref === 'function'
60
+ ? props.previewHref(row.value)
61
+ : props.previewHref
62
+ })
63
+
64
+ const hasHistory = computed(() => !!props.historyEndpoint)
65
+
66
+ // Resetear tab al cambiar de fila + reinit tooltips de Preline
67
+ watch(row, async (r) => {
68
+ previewTab.value = 'datos'
69
+ if (r) {
70
+ await nextTick()
71
+ if (typeof window !== 'undefined') window.HSStaticMethods?.autoInit?.(['Tooltip'])
72
+ }
73
+ })
74
+
75
+ // Persistir fila en sessionStorage
76
+ watch(row, (r) => {
77
+ if (!props.cached) return
78
+ if (r) sessionStorage.setItem(cacheKey.value, JSON.stringify(r))
79
+ else sessionStorage.removeItem(cacheKey.value)
80
+ })
81
+
82
+ const close = () => { row.value = null }
83
+
84
+ const startResize = (e) => {
85
+ e.preventDefault()
86
+ const onMove = (ev) => {
87
+ const el = props.containerRef
88
+ if (!el) return
89
+ const rect = el.getBoundingClientRect()
90
+ const ratio = ((ev.clientX - rect.left) / rect.width) * 100
91
+ currentRatio.value = Math.min(80, Math.max(25, ratio))
92
+ }
93
+ const onUp = () => {
94
+ window.removeEventListener('mousemove', onMove)
95
+ window.removeEventListener('mouseup', onUp)
96
+ }
97
+ window.addEventListener('mousemove', onMove)
98
+ window.addEventListener('mouseup', onUp)
99
+ }
100
+
101
+ // ─── Dock (chip flotante) ─────────────────────────────────────────────────────
102
+ const {
103
+ docked,
104
+ dock, undock: undockItem,
105
+ activeDockId, activeDockRect,
106
+ collapseDock,
107
+ } = useDockedPreviews()
108
+ const route = useRoute()
109
+
110
+ const minimize = () => {
111
+ if (!row.value) return
112
+ const label = row.value.name ?? row.value.title ?? row.value.email ?? String(row.value.id)
113
+ const subtitle = row.value.email ?? row.value.description ?? null
114
+ dock({
115
+ id: `${props.name}-${row.value.id}`,
116
+ label,
117
+ subtitle,
118
+ row: { ...row.value },
119
+ tableName: props.name,
120
+ route: route.path,
121
+ })
122
+ close()
123
+ }
124
+
125
+ const floatingItem = computed(() =>
126
+ activeDockId.value
127
+ ? docked.value.find(d => d.id === activeDockId.value && d.tableName === props.name) ?? null
128
+ : null
129
+ )
130
+
131
+ const floatingPanelStyle = computed(() => {
132
+ const rect = activeDockRect.value
133
+ const panelW = 384
134
+ const bottom = 52
135
+ if (!rect || typeof window === 'undefined') return { bottom: bottom + 'px', right: '16px' }
136
+ const tabCenter = rect.left + rect.width / 2
137
+ let right = window.innerWidth - tabCenter - panelW / 2
138
+ right = Math.max(8, Math.min(right, window.innerWidth - panelW - 8))
139
+ return { bottom: bottom + 'px', right: right + 'px' }
140
+ })
141
+
142
+ const expandToFull = (item) => {
143
+ row.value = item.row
144
+ undockItem(item.id)
145
+ }
146
+
147
+ // Restore from cache on mount
148
+ onMounted(async () => {
149
+ if (props.cached && props.enabled) {
150
+ try {
151
+ const raw = sessionStorage.getItem(cacheKey.value)
152
+ if (raw) {
153
+ previewFromCache.value = true
154
+ row.value = JSON.parse(raw)
155
+ await nextTick()
156
+ previewFromCache.value = false
157
+ }
158
+ } catch {}
159
+ }
160
+ })
161
+
162
+ defineExpose({ panelRef })
163
+ </script>
164
+
165
+ <template>
166
+ <!-- Panel overlay (absolute right, dentro del container del parent) -->
167
+ <Transition
168
+ :enter-active-class="previewFromCache ? '' : 'transition ease-out duration-200'"
169
+ :enter-from-class="previewFromCache ? '' : 'opacity-0 translate-x-6'"
170
+ :enter-to-class="previewFromCache ? '' : 'opacity-100 translate-x-0'"
171
+ leave-active-class="transition ease-in duration-150"
172
+ leave-from-class="opacity-100 translate-x-0"
173
+ leave-to-class="opacity-0 translate-x-6"
174
+ >
175
+ <div
176
+ v-if="row && enabled"
177
+ ref="panelRef"
178
+ class="absolute top-0 right-0 bottom-0 z-30 flex flex-col bg-card border border-card-line rounded-card shadow-xl overflow-hidden"
179
+ :style="{ width: (100 - currentRatio) + '%' }"
180
+ >
181
+ <!-- Resize handle -->
182
+ <div
183
+ class="absolute left-1 top-1/2 -translate-y-1/2 h-12 w-1 cursor-col-resize rounded-full bg-border hover:bg-primary/50 transition-colors z-10"
184
+ @mousedown="startResize"
185
+ />
186
+
187
+ <!-- Header -->
188
+ <div class="shrink-0 flex items-center gap-3 px-5 py-4 border-b border-card-line">
189
+ <div class="flex-1 min-w-0">
190
+ <slot name="header" :row="row" :close="close" />
191
+ </div>
192
+
193
+ <div class="flex items-center gap-0.5 shrink-0">
194
+ <slot name="actions" :row="row" :close="close" />
195
+
196
+ <!-- Abrir externa -->
197
+ <div v-if="resolvedPreviewHref" class="hs-tooltip [--placement:top] inline-block">
198
+ <NuxtLink
199
+ :to="resolvedPreviewHref"
200
+ class="hs-tooltip-toggle inline-flex items-center justify-center size-7 rounded-control text-muted-foreground hover:text-foreground hover:bg-muted-hover transition-colors"
201
+ >
202
+ <IconExternalLink class="size-3.5" />
203
+ </NuxtLink>
204
+ <span class="hs-tooltip-content hs-tooltip-shown:opacity-100 hs-tooltip-shown:visible opacity-0 transition-opacity inline-block absolute invisible z-10 py-1 px-2 bg-tooltip border border-tooltip-line text-xs font-medium text-tooltip-foreground rounded-popover shadow-2xs" role="tooltip">Abrir</span>
205
+ </div>
206
+
207
+ <!-- Eliminar -->
208
+ <div v-if="previewDeletable" class="hs-tooltip [--placement:top] inline-block">
209
+ <button
210
+ type="button"
211
+ class="hs-tooltip-toggle inline-flex items-center justify-center size-7 rounded-control text-muted-foreground hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"
212
+ @click.stop="emit('delete', row)"
213
+ >
214
+ <IconTrash class="size-3.5" />
215
+ </button>
216
+ <span class="hs-tooltip-content hs-tooltip-shown:opacity-100 hs-tooltip-shown:visible opacity-0 transition-opacity inline-block absolute invisible z-10 py-1 px-2 bg-tooltip border border-tooltip-line text-xs font-medium text-tooltip-foreground rounded-popover shadow-2xs" role="tooltip">Eliminar</span>
217
+ </div>
218
+
219
+ <!-- Minimizar -->
220
+ <div class="hs-tooltip [--placement:top] inline-block">
221
+ <button
222
+ type="button"
223
+ class="hs-tooltip-toggle inline-flex items-center justify-center size-7 rounded-control text-muted-foreground hover:text-foreground hover:bg-muted-hover transition-colors"
224
+ @click.stop="minimize"
225
+ >
226
+ <IconMinus class="size-3.5" />
227
+ </button>
228
+ <span class="hs-tooltip-content hs-tooltip-shown:opacity-100 hs-tooltip-shown:visible opacity-0 transition-opacity inline-block absolute invisible z-10 py-1 px-2 bg-tooltip border border-tooltip-line text-xs font-medium text-tooltip-foreground rounded-popover shadow-2xs" role="tooltip">Minimizar</span>
229
+ </div>
230
+
231
+ <!-- Cerrar -->
232
+ <div class="hs-tooltip [--placement:top] inline-block">
233
+ <button
234
+ type="button"
235
+ class="hs-tooltip-toggle inline-flex items-center justify-center size-7 rounded-control text-muted-foreground hover:text-foreground hover:bg-muted-hover transition-colors"
236
+ @click.stop="close"
237
+ >
238
+ <IconX class="size-3.5" />
239
+ </button>
240
+ <span class="hs-tooltip-content hs-tooltip-shown:opacity-100 hs-tooltip-shown:visible opacity-0 transition-opacity inline-block absolute invisible z-10 py-1 px-2 bg-tooltip border border-tooltip-line text-xs font-medium text-tooltip-foreground rounded-popover shadow-2xs" role="tooltip">Cerrar</span>
241
+ </div>
242
+ </div>
243
+ </div>
244
+
245
+ <!-- Body -->
246
+ <div class="flex-1 overflow-y-auto min-h-0">
247
+ <slot v-if="previewTab === 'datos'" :row="row" :close="close" />
248
+ <TablePreviewTimeline
249
+ v-else-if="previewTab === 'bitacora' && historyEndpoint"
250
+ :endpoint="historyEndpoint"
251
+ />
252
+ </div>
253
+
254
+ <!-- Tabs (cuando hay historyEndpoint) -->
255
+ <div v-if="hasHistory" class="shrink-0 flex border-t border-card-line">
256
+ <button
257
+ type="button"
258
+ @click="previewTab = 'datos'"
259
+ :class="[
260
+ 'flex-1 py-2.5 text-xs font-semibold transition-colors border-r border-card-line border-t-2 -mt-px',
261
+ previewTab === 'datos'
262
+ ? 'border-t-card text-foreground'
263
+ : 'border-t-transparent text-muted-foreground hover:text-foreground hover:bg-muted-hover'
264
+ ]"
265
+ >
266
+ Datos
267
+ </button>
268
+ <button
269
+ type="button"
270
+ @click="previewTab = 'bitacora'"
271
+ :class="[
272
+ 'flex-1 py-2.5 text-xs font-semibold transition-colors border-t-2 -mt-px',
273
+ previewTab === 'bitacora'
274
+ ? 'border-t-card text-foreground'
275
+ : 'border-t-transparent text-muted-foreground hover:text-foreground hover:bg-muted-hover'
276
+ ]"
277
+ >
278
+ Bitácora
279
+ </button>
280
+ </div>
281
+ </div>
282
+ </Transition>
283
+
284
+ <!-- Floating chip (dock minimizado) — siempre teleportado a body -->
285
+ <Teleport to="body">
286
+ <Transition
287
+ enter-active-class="transition ease-out duration-200"
288
+ enter-from-class="opacity-0 translate-y-4"
289
+ enter-to-class="opacity-100 translate-y-0"
290
+ leave-active-class="transition ease-in duration-150"
291
+ leave-from-class="opacity-100 translate-y-0"
292
+ leave-to-class="opacity-0 translate-y-4"
293
+ >
294
+ <div
295
+ v-if="floatingItem"
296
+ class="fixed z-[60] w-96 flex flex-col bg-card border border-card-line rounded-t-card shadow-2xl overflow-hidden"
297
+ :style="{ ...floatingPanelStyle, maxHeight: 'min(480px, calc(100vh - 60px))' }"
298
+ >
299
+ <div class="flex items-center gap-2 px-3 py-2.5 border-b border-card-line shrink-0 bg-surface select-none">
300
+ <span class="size-6 rounded-avatar bg-primary flex items-center justify-center text-[10px] font-bold text-primary-foreground shrink-0">
301
+ {{ (floatingItem.label?.[0] ?? '?').toUpperCase() }}
302
+ </span>
303
+ <div class="flex-1 min-w-0">
304
+ <p class="text-sm font-semibold text-foreground truncate leading-tight">{{ floatingItem.label }}</p>
305
+ <p v-if="floatingItem.subtitle" class="text-xs text-muted-foreground truncate">{{ floatingItem.subtitle }}</p>
306
+ </div>
307
+ <button type="button" title="Expandir" class="inline-flex items-center justify-center size-6 rounded-control text-muted-foreground hover:text-foreground hover:bg-muted-hover transition-colors" @click.stop="expandToFull(floatingItem)">
308
+ <IconMaximize class="size-3.5" />
309
+ </button>
310
+ <button type="button" title="Minimizar" class="inline-flex items-center justify-center size-6 rounded-control text-muted-foreground hover:text-foreground hover:bg-muted-hover transition-colors" @click.stop="collapseDock()">
311
+ <IconMinus class="size-3.5" />
312
+ </button>
313
+ <button type="button" title="Cerrar" class="inline-flex items-center justify-center size-6 rounded-control text-muted-foreground hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors" @click.stop="undockItem(floatingItem.id)">
314
+ <IconX class="size-3.5" />
315
+ </button>
316
+ </div>
317
+ <div class="flex-1 overflow-y-auto min-h-0">
318
+ <slot :row="floatingItem.row" :close="() => undockItem(floatingItem.id)" />
319
+ </div>
320
+ </div>
321
+ </Transition>
322
+ </Teleport>
323
+ </template>
@@ -436,7 +436,7 @@ const handleRowKeydown = (row, event) => {
436
436
  type="checkbox"
437
437
  :checked="isAllVisibleSelected"
438
438
  @change="toggleSelectAll"
439
- class="mx-2 shrink-0 border-gray-300 rounded-sm text-blue-900 focus:ring-blue-900 dark:bg-card border-card-line"
439
+ class="mx-2 shrink-0 border-gray-300 rounded-control text-blue-900 focus:ring-blue-900 dark:bg-card border-card-line"
440
440
  />
441
441
  </th>
442
442
  <th
@@ -538,7 +538,7 @@ const handleRowKeydown = (row, event) => {
538
538
  <!-- Empty overlays -->
539
539
  <div
540
540
  v-if="!loading && data.length === 0 && !search"
541
- class="absolute inset-0 z-10 pointer-events-none flex items-center justify-center backdrop-blur-sm bg-card/60 transition-all rounded-xl"
541
+ class="absolute inset-0 z-10 pointer-events-none flex items-center justify-center backdrop-blur-sm bg-card/60 transition-all rounded-card"
542
542
  >
543
543
  <slot name="empty">
544
544
  <p class="text-muted-foreground text-lg font-medium italic">No hay registros</p>
@@ -547,7 +547,7 @@ const handleRowKeydown = (row, event) => {
547
547
 
548
548
  <div
549
549
  v-if="!loading && data.length === 0 && search"
550
- class="absolute inset-0 z-10 pointer-events-none flex items-center justify-center backdrop-blur-sm bg-card/60 transition-all rounded-xl"
550
+ class="absolute inset-0 z-10 pointer-events-none flex items-center justify-center backdrop-blur-sm bg-card/60 transition-all rounded-card"
551
551
  >
552
552
  <slot name="empty-search">
553
553
  <p class="text-muted-foreground text-lg font-medium italic">No hay registros en la búsqueda</p>
@@ -561,7 +561,7 @@ const handleRowKeydown = (row, event) => {
561
561
  <div v-if="loading" :class="gridClass">
562
562
  <div v-for="(_, index) in skeletonRows" :key="'grid-skeleton-' + index" class="animate-pulse">
563
563
  <slot name="grid-skeleton">
564
- <div class="bg-card rounded-lg border border-card-line p-4">
564
+ <div class="bg-card rounded-card border border-card-line p-4">
565
565
  <div class="space-y-3">
566
566
  <div class="h-4 bg-surface-1 rounded w-3/4"></div>
567
567
  <div class="h-4 bg-surface-1 rounded w-1/2"></div>
@@ -583,7 +583,7 @@ const handleRowKeydown = (row, event) => {
583
583
  :checkable="checkable"
584
584
  :toggleRow="() => toggleRow(row)"
585
585
  >
586
- <div class="bg-card rounded-lg border border-card-line p-4 hover:shadow-md transition-shadow relative">
586
+ <div class="bg-card rounded-card border border-card-line p-4 hover:shadow-md transition-shadow relative">
587
587
  <div v-if="checkable" class="absolute top-2 left-2 z-10">
588
588
  <input
589
589
  type="checkbox"
@@ -636,11 +636,11 @@ const handleRowKeydown = (row, event) => {
636
636
  <p class="text-sm text-foreground font-medium">{{ meta.total }} registros</p>
637
637
 
638
638
  <div v-if="isDataFromCache && cached" class="group relative flex items-center">
639
- <div class="flex items-center gap-x-1.5 py-1 px-2.5 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 rounded-lg cursor-help transition-colors hover:bg-emerald-500/20">
639
+ <div class="flex items-center gap-x-1.5 py-1 px-2.5 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 rounded-badge cursor-help transition-colors hover:bg-emerald-500/20">
640
640
  <IconBolt class="size-3.5 fill-current" />
641
641
  <span class="text-[10px] font-bold uppercase tracking-wider">Instant</span>
642
642
  </div>
643
- <div class="absolute bottom-full mb-2 left-0 hidden group-hover:block w-48 p-2.5 bg-slate-900 text-white text-[11px] leading-relaxed rounded-xl shadow-2xl z-50 animate-in fade-in zoom-in duration-200">
643
+ <div class="absolute bottom-full mb-2 left-0 hidden group-hover:block w-48 p-2.5 bg-slate-900 text-white text-[11px] leading-relaxed rounded-popover shadow-2xl z-50 animate-in fade-in zoom-in duration-200">
644
644
  <div class="font-bold mb-1 flex items-center gap-x-1.5 text-emerald-400">
645
645
  <IconBolt class="size-3" />
646
646
  Datos en Caché
@@ -659,7 +659,7 @@ const handleRowKeydown = (row, event) => {
659
659
  v-if="!isCustomPerPage"
660
660
  :value="perPage"
661
661
  @change="(e) => handlePerPageChange(e.target.value)"
662
- class="bg-surface border-none text-[11px] font-bold text-muted-foreground-1 rounded-lg focus:ring-0 cursor-pointer py-1 pl-2 pr-8"
662
+ class="bg-surface border-none text-[11px] font-bold text-muted-foreground-1 rounded-control focus:ring-0 cursor-pointer py-1 pl-2 pr-8"
663
663
  >
664
664
  <option :value="10">10</option>
665
665
  <option :value="25">25</option>
@@ -673,7 +673,7 @@ const handleRowKeydown = (row, event) => {
673
673
  v-model.number="perPage"
674
674
  min="1"
675
675
  max="500"
676
- class="w-14 bg-surface border-none text-[11px] font-bold text-muted-foreground-1 rounded-lg focus:ring-2 focus:ring-indigo-500/20 py-1 px-2"
676
+ class="w-14 bg-surface border-none text-[11px] font-bold text-muted-foreground-1 rounded-control focus:ring-2 focus:ring-indigo-500/20 py-1 px-2"
677
677
  @blur="perPage = perPage || 10"
678
678
  />
679
679
  <button @click="resetPerPage" class="text-[10px] text-indigo-500 font-bold hover:underline">Volver</button>
@@ -683,7 +683,7 @@ const handleRowKeydown = (row, event) => {
683
683
  <nav class="flex justify-end items-center gap-x-1" aria-label="Pagination">
684
684
  <button
685
685
  type="button"
686
- class="size-8 flex items-center justify-center rounded-lg text-foreground hover:bg-muted-hover disabled:opacity-30"
686
+ class="size-8 flex items-center justify-center rounded-control text-foreground hover:bg-muted-hover disabled:opacity-30"
687
687
  :disabled="page <= 1"
688
688
  @click="goToPreviousPage"
689
689
  >
@@ -692,13 +692,13 @@ const handleRowKeydown = (row, event) => {
692
692
  </svg>
693
693
  </button>
694
694
  <div class="flex items-center gap-x-1 mx-2">
695
- <span class="size-8 flex items-center justify-center text-xs font-bold rounded-lg bg-surface text-foreground">{{ meta.current_page }}</span>
695
+ <span class="size-8 flex items-center justify-center text-xs font-bold rounded-control bg-surface text-foreground">{{ meta.current_page }}</span>
696
696
  <span class="text-[10px] font-bold text-muted-foreground uppercase mx-1">de</span>
697
697
  <span class="text-[10px] font-bold text-muted-foreground">{{ meta.last_page }}</span>
698
698
  </div>
699
699
  <button
700
700
  type="button"
701
- class="size-8 flex items-center justify-center rounded-lg text-foreground hover:bg-muted-hover disabled:opacity-30"
701
+ class="size-8 flex items-center justify-center rounded-control text-foreground hover:bg-muted-hover disabled:opacity-30"
702
702
  :disabled="page >= meta.last_page"
703
703
  @click="goToNextPage"
704
704
  >