@innertia-solutions/innertia-nuxt 0.1.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.
Files changed (108) hide show
  1. package/.github/workflows/auto-publish.yml +64 -0
  2. package/.github/workflows/release.yml +59 -0
  3. package/README.md +60 -0
  4. package/app.config.ts +70 -0
  5. package/components/Admin/Base.vue +144 -0
  6. package/components/Admin/Header.vue +32 -0
  7. package/components/Admin/Page.vue +65 -0
  8. package/components/Admin/PageHeader.vue +31 -0
  9. package/components/App/Button.vue +59 -0
  10. package/components/App/DevEnvironmentBar.vue +43 -0
  11. package/components/App/Dropdown.vue +286 -0
  12. package/components/App/EmptyState.vue +433 -0
  13. package/components/App/LoadingState.vue +40 -0
  14. package/components/App/PageLoadingSpinner.vue +118 -0
  15. package/components/App/PreviewDock.vue +64 -0
  16. package/components/App/SwitchColorTheme.vue +51 -0
  17. package/components/App/Tag.vue +193 -0
  18. package/components/DataTable.vue +713 -0
  19. package/components/Forms/DatePicker.vue +255 -0
  20. package/components/Forms/Input.vue +75 -0
  21. package/components/Forms/Select.vue +100 -0
  22. package/components/Forms/SelectServer.vue +726 -0
  23. package/components/Layout/Admin.vue +32 -0
  24. package/components/Layout/Auth.vue +29 -0
  25. package/components/Layout/SidebarWithAppColumn.vue +388 -0
  26. package/components/Layout/TopBar.vue +113 -0
  27. package/components/MobileBlocker.vue +85 -0
  28. package/components/MobileLoginPicker.vue +83 -0
  29. package/components/Modal/Base.vue +29 -0
  30. package/components/Modal/DeleteConfirm.vue +48 -0
  31. package/components/Modal.vue +103 -0
  32. package/components/Nav/Tabs.vue +55 -0
  33. package/components/PermissionsTree.vue +272 -0
  34. package/components/Table/Database.vue +183 -0
  35. package/components/Table/DownloadDropdown.vue +111 -0
  36. package/components/Table/Enterprise.vue +540 -0
  37. package/components/Table/FilterDropdown.vue +226 -0
  38. package/components/Table/Grid.vue +62 -0
  39. package/components/Table/Kanban.vue +188 -0
  40. package/components/Table/List.vue +128 -0
  41. package/components/Table/PreviewTimeline.vue +118 -0
  42. package/components/Table/Standard.vue +1217 -0
  43. package/components/Table/index.vue +974 -0
  44. package/components/TableExportable.vue +172 -0
  45. package/components/TableFilter.vue +93 -0
  46. package/components/Toast/Alert.vue +113 -0
  47. package/components/Toast/Container.vue +34 -0
  48. package/components/Toast/Notification.vue +45 -0
  49. package/components/Toast/Process.vue +88 -0
  50. package/composables/useApi.js +95 -0
  51. package/composables/useApp.ts +46 -0
  52. package/composables/useAuth.js +82 -0
  53. package/composables/useContext.js +44 -0
  54. package/composables/useDate.js +241 -0
  55. package/composables/useDevice.js +21 -0
  56. package/composables/useDockedPreviews.js +56 -0
  57. package/composables/useDownload.js +87 -0
  58. package/composables/useEntity.js +82 -0
  59. package/composables/useForm.js +119 -0
  60. package/composables/useInnertiaMode.ts +25 -0
  61. package/composables/useMobileGuard.ts +81 -0
  62. package/composables/useNotifications.js +22 -0
  63. package/composables/usePermissions.js +23 -0
  64. package/composables/useRealtime.js +123 -0
  65. package/composables/useRequestInterceptors.js +27 -0
  66. package/composables/useRoles.js +53 -0
  67. package/composables/useRutFormatter.js +39 -0
  68. package/composables/useTable.ts +94 -0
  69. package/composables/useTablePreferences.ts +33 -0
  70. package/composables/useTenant.js +27 -0
  71. package/composables/useTimeAgo.js +37 -0
  72. package/composables/useToast.js +69 -0
  73. package/composables/useUserRealtime.js +17 -0
  74. package/composables/useUsers.js +111 -0
  75. package/css/themes/autumn.css +401 -0
  76. package/css/themes/bubblegum.css +408 -0
  77. package/css/themes/cashmere.css +412 -0
  78. package/css/themes/harvest.css +416 -0
  79. package/css/themes/moon.css +140 -0
  80. package/css/themes/ocean.css +273 -0
  81. package/css/themes/olive.css +413 -0
  82. package/css/themes/retro.css +431 -0
  83. package/css/themes/theme.css +725 -0
  84. package/error.vue +78 -0
  85. package/middleware/01.detect-subdomain.global.ts +43 -0
  86. package/middleware/02.validate-tenant.global.ts +67 -0
  87. package/middleware/03.apps.global.ts +88 -0
  88. package/middleware/auth.ts +9 -0
  89. package/middleware/guest.ts +9 -0
  90. package/nuxt.config.ts +42 -0
  91. package/package.json +60 -0
  92. package/pages/tenant-error.vue +50 -0
  93. package/plugins/api-auth.ts +12 -0
  94. package/plugins/api-tenant.client.ts +21 -0
  95. package/plugins/appearance.ts +8 -0
  96. package/plugins/auth-init.ts +34 -0
  97. package/plugins/dark-state.client.ts +29 -0
  98. package/plugins/dockedPreviewsSync.client.js +17 -0
  99. package/plugins/preline.client.ts +68 -0
  100. package/plugins/theme.client.ts +7 -0
  101. package/plugins/vue-query.ts +29 -0
  102. package/public/init-theme.js +15 -0
  103. package/spark.css +721 -0
  104. package/stores/auth.js +130 -0
  105. package/stores/dockedPreviews.js +34 -0
  106. package/stores/notifications.js +24 -0
  107. package/stores/tenant.js +54 -0
  108. package/stores/toast.js +129 -0
@@ -0,0 +1,183 @@
1
+ <script setup>
2
+ import { IconSearch, IconLoader2, IconCheck, IconX } from '@tabler/icons-vue'
3
+
4
+ // Dense database view with inline cell editing — click cell → input → blur → mutation
5
+ const props = defineProps({
6
+ endpoint: { type: String, required: true },
7
+ columns: { type: Array, required: true }, // [{ key, label, editable?, type?: 'text'|'number'|'select', options?: [] }]
8
+ name: { type: String, required: true },
9
+ params: { type: Object, default: () => ({}) },
10
+ updateMutation: { type: Function, default: null }, // (id, field, value) => Promise
11
+ cached: { type: Boolean, default: false },
12
+ searchPlaceholder: { type: String, default: 'Buscar...' },
13
+ showSearch: { type: Boolean, default: true },
14
+ })
15
+
16
+ const emit = defineEmits(['row-click', 'cell-save'])
17
+
18
+ const tableRef = ref(null)
19
+
20
+ // ─── Inline editing ───────────────────────────────────────────────────────────
21
+ const editingCell = ref(null) // { rowId, key }
22
+ const editingValue = ref('')
23
+ const savingCell = ref(null)
24
+ const cellError = ref(null)
25
+
26
+ const startEdit = (row, col) => {
27
+ if (!col.editable) return
28
+ editingCell.value = { rowId: row.id, key: col.key }
29
+ editingValue.value = row[col.key] ?? ''
30
+ cellError.value = null
31
+ nextTick(() => {
32
+ const input = document.querySelector(`[data-cell-input="${row.id}-${col.key}"]`)
33
+ input?.focus()
34
+ input?.select()
35
+ })
36
+ }
37
+
38
+ const cancelEdit = () => {
39
+ editingCell.value = null
40
+ editingValue.value = ''
41
+ cellError.value = null
42
+ }
43
+
44
+ const saveEdit = async (row, col) => {
45
+ if (!editingCell.value) return
46
+ const newValue = editingValue.value
47
+ const oldValue = row[col.key]
48
+
49
+ if (newValue === String(oldValue ?? '')) {
50
+ cancelEdit()
51
+ return
52
+ }
53
+
54
+ editingCell.value = null
55
+ savingCell.value = { rowId: row.id, key: col.key }
56
+
57
+ try {
58
+ if (props.updateMutation) {
59
+ await props.updateMutation(row.id, col.key, newValue)
60
+ }
61
+ // Patch local row
62
+ row[col.key] = newValue
63
+ emit('cell-save', { id: row.id, field: col.key, value: newValue, oldValue })
64
+ tableRef.value?.reload()
65
+ } catch (e) {
66
+ cellError.value = { rowId: row.id, key: col.key, message: e.message }
67
+ } finally {
68
+ savingCell.value = null
69
+ }
70
+ }
71
+
72
+ const isEditing = (rowId, key) => editingCell.value?.rowId === rowId && editingCell.value?.key === key
73
+ const isSaving = (rowId, key) => savingCell.value?.rowId === rowId && savingCell.value?.key === key
74
+
75
+ const search = ref('')
76
+ const mergedParams = computed(() => ({ ...props.params }))
77
+
78
+ const reload = () => tableRef.value?.reload()
79
+ defineExpose({ reload })
80
+ </script>
81
+
82
+ <template>
83
+ <div class="flex flex-col gap-3">
84
+ <div v-if="showSearch" class="relative max-w-sm">
85
+ <div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
86
+ <IconSearch class="size-4 text-slate-400" stroke="1.5" />
87
+ </div>
88
+ <input
89
+ v-model="search"
90
+ type="search"
91
+ :placeholder="searchPlaceholder"
92
+ class="block w-full rounded-lg border border-card-line bg-card text-foreground py-1.5 ps-9 pe-4 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500"
93
+ />
94
+ </div>
95
+
96
+ <!-- Dense table wrapper — override Table's default padding with compact styles -->
97
+ <div class="overflow-x-auto border border-card-line rounded-xl">
98
+ <Table
99
+ ref="tableRef"
100
+ :endpoint="endpoint"
101
+ :columns="columns"
102
+ :name="name"
103
+ :params="mergedParams"
104
+ :search="search"
105
+ :cached="cached"
106
+ :show-reload-button="false"
107
+ class="[&_td]:py-1 [&_td]:px-2 [&_th]:py-1.5 [&_th]:px-2 [&_td]:text-xs [&_th]:text-xs"
108
+ @row-click="emit('row-click', $event)"
109
+ >
110
+ <!-- Override each cell slot to support inline editing -->
111
+ <template
112
+ v-for="col in columns"
113
+ :key="col.key"
114
+ #[`cell-${col.key}`]="{ row }"
115
+ >
116
+ <!-- Editing state -->
117
+ <div v-if="isEditing(row.id, col.key)" class="flex items-center gap-1 -mx-1">
118
+ <select
119
+ v-if="col.type === 'select' && col.options"
120
+ v-model="editingValue"
121
+ :data-cell-input="`${row.id}-${col.key}`"
122
+ @blur="saveEdit(row, col)"
123
+ @keydown.enter="saveEdit(row, col)"
124
+ @keydown.escape="cancelEdit"
125
+ class="flex-1 min-w-0 rounded border border-indigo-400 bg-card text-foreground py-0.5 px-1.5 text-xs focus:outline-none focus:ring-1 focus:ring-indigo-500"
126
+ >
127
+ <option v-for="opt in col.options" :key="opt.value" :value="opt.value">{{ opt.label }}</option>
128
+ </select>
129
+ <input
130
+ v-else
131
+ v-model="editingValue"
132
+ :type="col.type === 'number' ? 'number' : 'text'"
133
+ :data-cell-input="`${row.id}-${col.key}`"
134
+ @blur="saveEdit(row, col)"
135
+ @keydown.enter="saveEdit(row, col)"
136
+ @keydown.escape="cancelEdit"
137
+ class="flex-1 min-w-0 rounded border border-indigo-400 bg-card text-foreground py-0.5 px-1.5 text-xs focus:outline-none focus:ring-1 focus:ring-indigo-500"
138
+ />
139
+ </div>
140
+
141
+ <!-- Saving state -->
142
+ <div v-else-if="isSaving(row.id, col.key)" class="flex items-center gap-1 text-slate-400">
143
+ <IconLoader2 class="size-3 animate-spin shrink-0" stroke="1.5" />
144
+ <span class="truncate">{{ row[col.key] }}</span>
145
+ </div>
146
+
147
+ <!-- Error state -->
148
+ <div
149
+ v-else-if="cellError?.rowId === row.id && cellError?.key === col.key"
150
+ :title="cellError.message"
151
+ class="flex items-center gap-1 text-red-500 cursor-pointer"
152
+ @click="startEdit(row, col)"
153
+ >
154
+ <IconX class="size-3 shrink-0" stroke="2" />
155
+ <span class="truncate text-xs">{{ row[col.key] }}</span>
156
+ </div>
157
+
158
+ <!-- View state -->
159
+ <div
160
+ v-else
161
+ :class="[
162
+ 'truncate',
163
+ col.editable ? 'cursor-text hover:bg-indigo-50 dark:hover:bg-indigo-900/20 rounded px-1 -mx-1 group relative' : ''
164
+ ]"
165
+ @click="col.editable ? startEdit(row, col) : emit('row-click', row)"
166
+ >
167
+ <slot :name="`cell-${col.key}`" :row="row">
168
+ {{ row[col.key] }}
169
+ </slot>
170
+ <span
171
+ v-if="col.editable"
172
+ class="absolute inset-y-0 right-0 flex items-center opacity-0 group-hover:opacity-100 pr-1"
173
+ >
174
+ <svg class="size-3 text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
175
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
176
+ </svg>
177
+ </span>
178
+ </div>
179
+ </template>
180
+ </Table>
181
+ </div>
182
+ </div>
183
+ </template>
@@ -0,0 +1,111 @@
1
+ <script setup>
2
+ import {
3
+ IconFileTypeXls,
4
+ IconCodeDots,
5
+ IconFileTypePdf,
6
+ IconFileTypeCsv,
7
+ IconDownload,
8
+ } from "@tabler/icons-vue";
9
+
10
+ const props = defineProps({
11
+ tableRef: { type: Object, required: true },
12
+ });
13
+
14
+ const exportAllPages = ref(true);
15
+ const exportFilteredRows = ref(true);
16
+
17
+ const exportTable = (format) => {
18
+ if (props.tableRef) {
19
+ props.tableRef.exportTable(
20
+ format,
21
+ exportAllPages.value,
22
+ exportFilteredRows.value
23
+ );
24
+ }
25
+ };
26
+ </script>
27
+ <template>
28
+ <div
29
+ class="hs-dropdown [--auto-close:inside] [--placement:bottom-right] relative inline-block"
30
+ >
31
+ <button
32
+ id="hs-as-table-table-export-dropdown"
33
+ type="button"
34
+ class="py-1.5 sm:py-2 px-2.5 inline-flex items-center gap-x-1.5 text-sm sm:text-xs font-medium rounded-lg border border-card-line bg-card text-foreground shadow-2xs hover:bg-muted-hover disabled:opacity-50 disabled:pointer-events-none focus:outline-hidden focus:bg-muted-hover"
35
+ aria-haspopup="menu"
36
+ aria-expanded="false"
37
+ aria-label="Dropdown"
38
+ >
39
+ <IconDownload class="shrink-0 size-4" stroke="1.5" />
40
+ Exportar
41
+ </button>
42
+ <div
43
+ class="hs-dropdown-menu transition-[opacity,margin] duration hs-dropdown-open:opacity-100 opacity-0 hidden divide-y divide-card-line min-w-48 z-10 bg-dropdown shadow-md rounded-lg p-2 mt-2 dark:border dark:border-dropdown-line border-t border-card-line"
44
+ role="menu"
45
+ aria-orientation="vertical"
46
+ aria-labelledby="hs-as-table-table-export-dropdown"
47
+ >
48
+ <div class="py-2 first:pt-0 last:pb-0">
49
+ <a
50
+ class="flex items-center gap-x-3 py-2 px-3 rounded-lg text-sm text-foreground hover:bg-muted-hover focus:outline-hidden focus:bg-muted-hover"
51
+ href="#"
52
+ @click="exportTable('xlsx')"
53
+ >
54
+ <IconFileTypeXls class="shrink-0 size-5" stroke="1.5" />
55
+ Excel
56
+ </a>
57
+ <a
58
+ class="flex items-center gap-x-3 py-2 px-3 rounded-lg text-sm text-foreground hover:bg-muted-hover focus:outline-hidden focus:bg-muted-hover"
59
+ href="#"
60
+ @click="exportTable('csv')"
61
+ >
62
+ <IconFileTypeCsv class="shrink-0 size-5" stroke="1.5" />
63
+ CSV
64
+ </a>
65
+ <a
66
+ class="flex items-center gap-x-3 py-2 px-3 rounded-lg text-sm text-foreground hover:bg-muted-hover focus:outline-hidden focus:bg-muted-hover"
67
+ href="#"
68
+ @click="exportTable('pdf')"
69
+ >
70
+ <IconFileTypePdf class="shrink-0 size-5" stroke="1.5" />
71
+ PDF
72
+ </a>
73
+ <a
74
+ class="flex items-center gap-x-3 py-2 px-3 rounded-lg text-sm text-foreground hover:bg-muted-hover focus:outline-hidden focus:bg-muted-hover"
75
+ href="#"
76
+ @click="exportTable('json')"
77
+ >
78
+ <IconCodeDots class="shrink-0 size-5" stroke="1.5" />
79
+ JSON
80
+ </a>
81
+ </div>
82
+ <!-- checkbox - todas las paginas -->
83
+ <div
84
+ class="flex flex-col gap-y-2 py-2 px-3"
85
+ data-hs-dropdown-ignore-click
86
+ >
87
+ <label
88
+ class="inline-flex items-center gap-2 text-sm text-foreground"
89
+ >
90
+ <input
91
+ type="checkbox"
92
+ v-model="exportAllPages"
93
+ class="shrink-0 border-card-line rounded-sm text-blue-600 focus:ring-blue-500 dark:bg-card"
94
+ />
95
+ Todas las páginas
96
+ </label>
97
+
98
+ <label
99
+ class="inline-flex items-center gap-2 text-sm text-foreground"
100
+ >
101
+ <input
102
+ type="checkbox"
103
+ v-model="exportFilteredRows"
104
+ class="shrink-0 border-card-line rounded-sm text-blue-600 focus:ring-blue-500 dark:bg-card"
105
+ />
106
+ Solo filas filtradas
107
+ </label>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </template>