@antelopejs/dms-database 0.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.
Files changed (164) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +53 -0
  3. package/dist/automation/index.d.ts +2 -0
  4. package/dist/automation/index.js +13 -0
  5. package/dist/automation/index.js.map +1 -0
  6. package/dist/automation/tableWatch.d.ts +17 -0
  7. package/dist/automation/tableWatch.js +189 -0
  8. package/dist/automation/tableWatch.js.map +1 -0
  9. package/dist/config.d.ts +7 -0
  10. package/dist/config.js +12 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/db/index.d.ts +2 -0
  13. package/dist/db/index.js +19 -0
  14. package/dist/db/index.js.map +1 -0
  15. package/dist/db/models/diagram_layouts.model.d.ts +25 -0
  16. package/dist/db/models/diagram_layouts.model.js +29 -0
  17. package/dist/db/models/diagram_layouts.model.js.map +1 -0
  18. package/dist/db/models/diagram_notes.model.d.ts +23 -0
  19. package/dist/db/models/diagram_notes.model.js +15 -0
  20. package/dist/db/models/diagram_notes.model.js.map +1 -0
  21. package/dist/db/models/index.d.ts +4 -0
  22. package/dist/db/models/index.js +21 -0
  23. package/dist/db/models/index.js.map +1 -0
  24. package/dist/db/models/query_history.model.d.ts +25 -0
  25. package/dist/db/models/query_history.model.js +33 -0
  26. package/dist/db/models/query_history.model.js.map +1 -0
  27. package/dist/db/models/saved_queries.model.d.ts +24 -0
  28. package/dist/db/models/saved_queries.model.js +21 -0
  29. package/dist/db/models/saved_queries.model.js.map +1 -0
  30. package/dist/db/tables/diagram_layouts.table.d.ts +7 -0
  31. package/dist/db/tables/diagram_layouts.table.js +35 -0
  32. package/dist/db/tables/diagram_layouts.table.js.map +1 -0
  33. package/dist/db/tables/diagram_notes.table.d.ts +13 -0
  34. package/dist/db/tables/diagram_notes.table.js +59 -0
  35. package/dist/db/tables/diagram_notes.table.js.map +1 -0
  36. package/dist/db/tables/index.d.ts +4 -0
  37. package/dist/db/tables/index.js +21 -0
  38. package/dist/db/tables/index.js.map +1 -0
  39. package/dist/db/tables/query_history.table.d.ts +11 -0
  40. package/dist/db/tables/query_history.table.js +54 -0
  41. package/dist/db/tables/query_history.table.js.map +1 -0
  42. package/dist/db/tables/saved_queries.table.d.ts +12 -0
  43. package/dist/db/tables/saved_queries.table.js +58 -0
  44. package/dist/db/tables/saved_queries.table.js.map +1 -0
  45. package/dist/index.d.ts +8 -0
  46. package/dist/index.js +56 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/pages/data.d.ts +9 -0
  49. package/dist/pages/data.js +29 -0
  50. package/dist/pages/data.js.map +1 -0
  51. package/dist/pages/index.d.ts +4 -0
  52. package/dist/pages/index.js +21 -0
  53. package/dist/pages/index.js.map +1 -0
  54. package/dist/pages/overview.d.ts +9 -0
  55. package/dist/pages/overview.js +29 -0
  56. package/dist/pages/overview.js.map +1 -0
  57. package/dist/pages/query.d.ts +9 -0
  58. package/dist/pages/query.js +29 -0
  59. package/dist/pages/query.js.map +1 -0
  60. package/dist/pages/schemas.d.ts +9 -0
  61. package/dist/pages/schemas.js +29 -0
  62. package/dist/pages/schemas.js.map +1 -0
  63. package/dist/routes/data.d.ts +25 -0
  64. package/dist/routes/data.js +324 -0
  65. package/dist/routes/data.js.map +1 -0
  66. package/dist/routes/diagram.d.ts +47 -0
  67. package/dist/routes/diagram.js +200 -0
  68. package/dist/routes/diagram.js.map +1 -0
  69. package/dist/routes/health.d.ts +6 -0
  70. package/dist/routes/health.js +35 -0
  71. package/dist/routes/health.js.map +1 -0
  72. package/dist/routes/index.d.ts +6 -0
  73. package/dist/routes/index.js +23 -0
  74. package/dist/routes/index.js.map +1 -0
  75. package/dist/routes/query.d.ts +26 -0
  76. package/dist/routes/query.js +245 -0
  77. package/dist/routes/query.js.map +1 -0
  78. package/dist/routes/schemas.d.ts +8 -0
  79. package/dist/routes/schemas.js +35 -0
  80. package/dist/routes/schemas.js.map +1 -0
  81. package/dist/routes/tables.d.ts +23 -0
  82. package/dist/routes/tables.js +94 -0
  83. package/dist/routes/tables.js.map +1 -0
  84. package/dist/service/fieldDescriptor.d.ts +66 -0
  85. package/dist/service/fieldDescriptor.js +156 -0
  86. package/dist/service/fieldDescriptor.js.map +1 -0
  87. package/dist/service/health.d.ts +20 -0
  88. package/dist/service/health.js +58 -0
  89. package/dist/service/health.js.map +1 -0
  90. package/dist/service/introspect.d.ts +6 -0
  91. package/dist/service/introspect.js +312 -0
  92. package/dist/service/introspect.js.map +1 -0
  93. package/dist/service/schemaLabels.d.ts +7 -0
  94. package/dist/service/schemaLabels.js +39 -0
  95. package/dist/service/schemaLabels.js.map +1 -0
  96. package/dist/service/schemaRegistry.d.ts +2 -0
  97. package/dist/service/schemaRegistry.js +8 -0
  98. package/dist/service/schemaRegistry.js.map +1 -0
  99. package/dist/service/stagedSerialization.d.ts +6 -0
  100. package/dist/service/stagedSerialization.js +54 -0
  101. package/dist/service/stagedSerialization.js.map +1 -0
  102. package/dist/service/types.d.ts +67 -0
  103. package/dist/service/types.js +3 -0
  104. package/dist/service/types.js.map +1 -0
  105. package/dist/types/constants.d.ts +3 -0
  106. package/dist/types/constants.js +7 -0
  107. package/dist/types/constants.js.map +1 -0
  108. package/dist/types/responses.d.ts +14 -0
  109. package/dist/types/responses.js +3 -0
  110. package/dist/types/responses.js.map +1 -0
  111. package/dist/utils/query.d.ts +17 -0
  112. package/dist/utils/query.js +116 -0
  113. package/dist/utils/query.js.map +1 -0
  114. package/dist/utils/requestValidation.d.ts +2 -0
  115. package/dist/utils/requestValidation.js +14 -0
  116. package/dist/utils/requestValidation.js.map +1 -0
  117. package/frontend-vue/.editorconfig +12 -0
  118. package/frontend-vue/.prettierrc +11 -0
  119. package/frontend-vue/app/components/DataBrowserSidebar.vue +254 -0
  120. package/frontend-vue/app/components/DataBrowserTabBar.vue +166 -0
  121. package/frontend-vue/app/components/DataGrid.vue +735 -0
  122. package/frontend-vue/app/components/DataGridCell.vue +147 -0
  123. package/frontend-vue/app/components/DataGridHeaderCell.vue +175 -0
  124. package/frontend-vue/app/components/DiagramNoteNode.vue +100 -0
  125. package/frontend-vue/app/components/DiagramRelationEdge.vue +62 -0
  126. package/frontend-vue/app/components/DiagramStubNode.vue +37 -0
  127. package/frontend-vue/app/components/DiagramTableNode.vue +94 -0
  128. package/frontend-vue/app/components/JsonCellPanel.vue +93 -0
  129. package/frontend-vue/app/components/QueryBuilderPanel.vue +501 -0
  130. package/frontend-vue/app/components/QueryFavoritesPanel.vue +127 -0
  131. package/frontend-vue/app/components/QueryHistoryPanel.vue +134 -0
  132. package/frontend-vue/app/components/QueryResultPanel.vue +204 -0
  133. package/frontend-vue/app/components/SchemaDiagram.vue +1150 -0
  134. package/frontend-vue/app/components/SchemaTableInspector.vue +278 -0
  135. package/frontend-vue/app/composables/useDataBrowserGrid.ts +162 -0
  136. package/frontend-vue/app/composables/useDataBrowserTabs.ts +255 -0
  137. package/frontend-vue/app/composables/useDatabaseHealth.ts +50 -0
  138. package/frontend-vue/app/composables/useDatabaseSchemas.ts +92 -0
  139. package/frontend-vue/app/composables/useDiagramPersistence.ts +99 -0
  140. package/frontend-vue/app/composables/useQueryRuntime.ts +118 -0
  141. package/frontend-vue/app/composables/useQueryStore.ts +117 -0
  142. package/frontend-vue/app/composables/useTableViewRowClick.ts +22 -0
  143. package/frontend-vue/app/custom-pages/database/data.vue +42 -0
  144. package/frontend-vue/app/custom-pages/database/overview.vue +367 -0
  145. package/frontend-vue/app/custom-pages/database/query.vue +432 -0
  146. package/frontend-vue/app/custom-pages/database/schemas.vue +216 -0
  147. package/frontend-vue/app/utils/databaseFieldModifiers.ts +33 -0
  148. package/frontend-vue/app/utils/databaseFieldTypeIcons.ts +224 -0
  149. package/frontend-vue/app/utils/dmsTableView.ts +83 -0
  150. package/frontend-vue/app/utils/relativeTime.ts +15 -0
  151. package/frontend-vue/app/utils/stagedSerialization.ts +91 -0
  152. package/frontend-vue/dms.frontend.ts +38 -0
  153. package/frontend-vue/eslint.config.mjs +16 -0
  154. package/frontend-vue/i18n/locales/database-en-GB.json +239 -0
  155. package/frontend-vue/i18n/locales/database-fr-FR.json +239 -0
  156. package/frontend-vue/package.json +42 -0
  157. package/frontend-vue/pnpm-lock.yaml +5819 -0
  158. package/frontend-vue/pnpm-workspace.yaml +2 -0
  159. package/frontend-vue/tests/persistence.test.ts +70 -0
  160. package/frontend-vue/tests/queryRuntime.test.ts +53 -0
  161. package/frontend-vue/tests/registration.test.ts +33 -0
  162. package/frontend-vue/tsconfig.json +13 -0
  163. package/frontend-vue/vitest.config.ts +5 -0
  164. package/package.json +78 -0
@@ -0,0 +1,367 @@
1
+ <script setup lang="ts">
2
+ import type { HistoryEntry } from "../../composables/useQueryStore";
3
+ import { buildReadonlyColumns } from "../../utils/dmsTableView";
4
+
5
+ const SCHEMAS_PATH = "/modules/database/schemas";
6
+ const DATA_PATH = "/modules/database/data";
7
+ const QUERY_PATH = "/modules/database/query";
8
+ const TABLES_LOCATION = "/api/database/tables";
9
+ const COMPONENT_ID = "database-overview-tables";
10
+ const PAGE_ID = "database.overview";
11
+ const RECENT_HISTORY_LIMIT = 6;
12
+ const STORAGE_TOP_N = 5;
13
+ // A recent query slower than this (ms) is flagged with a "warning" tone.
14
+ const SLOW_QUERY_MS = 100;
15
+
16
+ const router = useDmsRouter();
17
+
18
+ const { t } = useI18n();
19
+ const { $authFetch } = useAuthFetch();
20
+ const { health } = useDatabaseHealth();
21
+
22
+ const fullFormatter = new Intl.NumberFormat("en-US");
23
+
24
+ // --- health hero metrics (driver / latency / pool / collections) ---
25
+ const heroMetrics = computed(() => {
26
+ const h = health.value;
27
+ const dash = "—";
28
+ return [
29
+ { label: t("dms_database.overview.health.driver"), value: h.driver ?? dash },
30
+ {
31
+ label: t("dms_database.overview.health.latency"),
32
+ value: h.latencyMs == null ? dash : String(h.latencyMs),
33
+ unit: h.latencyMs == null ? undefined : "ms",
34
+ },
35
+ { label: t("dms_database.overview.health.pool"), value: h.pool ?? dash },
36
+ {
37
+ label: t("dms_database.overview.health.collections"),
38
+ value: h.collections,
39
+ },
40
+ ];
41
+ });
42
+
43
+ // Beta banner body: the reference design bolds a few key phrases. The i18n
44
+ // string marks them with **…** (per-locale, so each translation owns its own
45
+ // emphasis); we split on those markers into normal/bold segments — no v-html.
46
+ const betaSegments = computed(() =>
47
+ t("dms_database.overview.beta.body")
48
+ .split(/\*\*(.+?)\*\*/g)
49
+ .map((text, index) => ({ text, bold: index % 2 === 1 })),
50
+ );
51
+
52
+ const heroStatus = computed<"ok" | "down">(() =>
53
+ health.value.status === "ok" ? "ok" : "down",
54
+ );
55
+ const heroValue = computed(() =>
56
+ heroStatus.value === "ok"
57
+ ? t("dms_database.overview.health.healthy")
58
+ : t("dms_database.overview.health.down"),
59
+ );
60
+
61
+ // --- stat cards (Schemas / Tables / Total rows / Indexes) ---
62
+ const statCards = computed(() => {
63
+ const h = health.value;
64
+ return [
65
+ {
66
+ label: t("dms_database.overview.kpis.schemas"),
67
+ value: h.schemaCount,
68
+ format: "compact" as const,
69
+ icon: "i-ph-stack",
70
+ },
71
+ {
72
+ label: t("dms_database.overview.kpis.tables"),
73
+ value: h.tableCount,
74
+ format: "compact" as const,
75
+ icon: "i-ph-table",
76
+ },
77
+ {
78
+ label: t("dms_database.overview.kpis.rows"),
79
+ value: h.totalRows,
80
+ icon: "i-ph-database",
81
+ },
82
+ {
83
+ // Byte-size storage is not exposed by interface-database. Rather than
84
+ // duplicate the row count under a "Size" label, the fourth KPI surfaces
85
+ // the total declared index count — a real, distinct schema statistic.
86
+ label: t("dms_database.overview.kpis.indexes"),
87
+ value: h.indexCount,
88
+ icon: "i-ph-key",
89
+ },
90
+ ];
91
+ });
92
+
93
+ // --- storage by table (row counts as a proxy; byte size unavailable) ---
94
+ const storageBars = computed(() => {
95
+ const top = health.value.storage.slice(0, STORAGE_TOP_N);
96
+ const max = top.reduce((m, s) => Math.max(m, s.rows), 0) || 1;
97
+ const COLORS = [
98
+ "bg-primary",
99
+ "bg-info",
100
+ "bg-success",
101
+ "bg-warning",
102
+ "bg-dimmed",
103
+ ];
104
+ return top.map((s, idx) => ({
105
+ key: `${s.schema}::${s.table}`,
106
+ table: s.table,
107
+ rows: s.rows,
108
+ rowsLabel: fullFormatter.format(s.rows),
109
+ pct: Math.max(2, Math.round((s.rows / max) * 100)),
110
+ color: COLORS[idx] ?? "bg-dimmed",
111
+ }));
112
+ });
113
+
114
+ // --- recent queries (from query history) ---
115
+ const recentQueries = ref<HistoryEntry[]>([]);
116
+ const recentLoading = ref(true);
117
+
118
+ async function loadRecent() {
119
+ recentLoading.value = true;
120
+ try {
121
+ const res = await $authFetch<{ items: HistoryEntry[] }>(
122
+ "/api/database/query/history",
123
+ { query: { limit: RECENT_HISTORY_LIMIT, offset: 0 } },
124
+ );
125
+ recentQueries.value = res.items ?? [];
126
+ } catch {
127
+ recentQueries.value = [];
128
+ } finally {
129
+ recentLoading.value = false;
130
+ }
131
+ }
132
+
133
+ function recentTone(entry: HistoryEntry): "success" | "warning" {
134
+ return entry.durationMs > SLOW_QUERY_MS ? "warning" : "success";
135
+ }
136
+
137
+ // --- tables panel (shared DmsTableView, read-only) ---
138
+ // One table summary row as returned by /api/database/tables/list.
139
+ interface TableListRow {
140
+ id: string;
141
+ schema: string;
142
+ name: string;
143
+ }
144
+
145
+ const tableColumns = computed(() =>
146
+ buildReadonlyColumns([
147
+ { key: "name", header: t("dms_database.overview.tables.cols.table") },
148
+ { key: "elementCount", header: t("dms_database.overview.tables.cols.rows") },
149
+ { key: "columnCount", header: t("dms_database.overview.tables.cols.columns") },
150
+ { key: "indexCount", header: t("dms_database.overview.tables.cols.indexes") },
151
+ ]),
152
+ );
153
+
154
+ function openTableData(row: TableListRow) {
155
+ router.push(
156
+ `${DATA_PATH}?schema=${encodeURIComponent(row.schema)}&table=${encodeURIComponent(row.name)}`,
157
+ );
158
+ }
159
+
160
+ // A row double-click navigates to that table in the data browser.
161
+ useTableViewRowClick<TableListRow>(COMPONENT_ID, openTableData);
162
+
163
+ onMounted(loadRecent);
164
+ </script>
165
+
166
+ <template>
167
+ <div class="space-y-5 pb-6">
168
+ <DmsBanner
169
+ color="warning"
170
+ icon="i-ph-info"
171
+ :title="$t('dms_database.overview.beta.title')"
172
+ class="mt-6"
173
+ >
174
+ <template #description>
175
+ <span>
176
+ <template v-for="(seg, i) in betaSegments" :key="i"
177
+ ><strong v-if="seg.bold" class="font-semibold text-toned">{{
178
+ seg.text
179
+ }}</strong
180
+ ><template v-else>{{ seg.text }}</template></template
181
+ >
182
+ </span>
183
+ </template>
184
+ </DmsBanner>
185
+
186
+ <section class="flex flex-wrap items-center gap-4 pb-2">
187
+ <div
188
+ class="rounded-lg bg-primary/10 shadow-sm shrink-0 ring ring-primary/20 flex items-center justify-center size-12"
189
+ >
190
+ <UIcon name="i-ph-gauge" class="text-primary" size="1.75rem" />
191
+ </div>
192
+ <div class="flex-1 min-w-0">
193
+ <h1 class="text-highlighted font-semibold">
194
+ {{ $t("dms_database.overview.title") }}
195
+ </h1>
196
+ <p class="text-muted text-sm">
197
+ {{ $t("dms_database.overview.description") }}
198
+ </p>
199
+ </div>
200
+ <div class="flex flex-wrap gap-2 ml-auto">
201
+ <UButton
202
+ :to="SCHEMAS_PATH"
203
+ color="neutral"
204
+ variant="outline"
205
+ icon="i-ph-stack"
206
+ :label="$t('dms_database.overview.actions.schema')"
207
+ />
208
+ <UButton
209
+ :to="DATA_PATH"
210
+ color="primary"
211
+ icon="i-ph-table"
212
+ :label="$t('dms_database.overview.actions.openData')"
213
+ />
214
+ </div>
215
+ </section>
216
+
217
+ <DmsStatusSummary
218
+ :status="heroStatus"
219
+ :status-value="heroValue"
220
+ :status-label="$t('dms_database.overview.health.connection')"
221
+ :metrics="heroMetrics"
222
+ />
223
+
224
+ <div class="grid grid-cols-2 gap-4 sm:grid-cols-4">
225
+ <DmsKpiCard
226
+ v-for="card in statCards"
227
+ :key="card.label"
228
+ variant="stat"
229
+ :title="card.label"
230
+ :icon="card.icon"
231
+ :static-value="card.value"
232
+ value-format="compact"
233
+ :show-delta="false"
234
+ page-id="database.overview"
235
+ :component-id="`overview-kpi-${card.label}`"
236
+ />
237
+ </div>
238
+
239
+ <div class="grid grid-cols-1 gap-5 lg:grid-cols-2 items-stretch">
240
+ <!-- Storage by table -->
241
+ <DmsCard :padded="false" class="flex flex-col">
242
+ <div
243
+ class="flex items-center justify-between gap-3 border-b border-default px-5 py-4 sm:px-6"
244
+ >
245
+ <div class="min-w-0">
246
+ <h2 class="text-highlighted font-semibold leading-tight">
247
+ {{ $t("dms_database.overview.storage.title") }}
248
+ </h2>
249
+ <p class="text-dimmed mt-0.5 text-xs">
250
+ {{ $t("dms_database.overview.storage.subtitle") }}
251
+ </p>
252
+ </div>
253
+ <UButton
254
+ :to="SCHEMAS_PATH"
255
+ variant="link"
256
+ color="primary"
257
+ size="sm"
258
+ trailing-icon="i-ph-arrow-up-right"
259
+ :label="$t('dms_database.overview.storage.viewSchema')"
260
+ class="font-mono"
261
+ />
262
+ </div>
263
+ <div class="flex-1 px-5 py-4 sm:px-6">
264
+ <div
265
+ v-if="storageBars.length === 0"
266
+ class="text-dimmed py-6 text-center text-sm"
267
+ >
268
+ {{ $t("dms_database.overview.storage.empty") }}
269
+ </div>
270
+ <div v-for="bar in storageBars" :key="bar.key" class="mb-3.5 last:mb-0">
271
+ <div class="mb-1.5 flex items-center justify-between gap-2">
272
+ <span class="text-toned font-mono text-[12.5px] truncate">
273
+ {{ bar.table }}
274
+ </span>
275
+ <span class="text-dimmed font-mono text-xs tabular-nums">
276
+ {{ bar.rowsLabel }}
277
+ {{ $t("dms_database.overview.storage.rowsUnit") }}
278
+ </span>
279
+ </div>
280
+ <div class="h-[9px] w-full overflow-hidden rounded-full bg-elevated">
281
+ <span
282
+ class="block h-full rounded-full"
283
+ :class="bar.color"
284
+ :style="{ width: `${bar.pct}%` }"
285
+ />
286
+ </div>
287
+ </div>
288
+ </div>
289
+ </DmsCard>
290
+
291
+ <!-- Recent queries -->
292
+ <DmsCard :padded="false" class="flex flex-col">
293
+ <div
294
+ class="flex items-center justify-between gap-3 border-b border-default px-5 py-4 sm:px-6"
295
+ >
296
+ <h2 class="text-highlighted font-semibold leading-tight">
297
+ {{ $t("dms_database.overview.recent.title") }}
298
+ </h2>
299
+ <UButton
300
+ :to="QUERY_PATH"
301
+ variant="link"
302
+ color="primary"
303
+ size="sm"
304
+ trailing-icon="i-ph-arrow-up-right"
305
+ :label="$t('dms_database.overview.recent.console')"
306
+ class="font-mono"
307
+ />
308
+ </div>
309
+ <div class="flex-1 divide-y divide-default">
310
+ <div
311
+ v-if="recentLoading"
312
+ class="text-dimmed px-5 py-6 text-center text-sm"
313
+ >
314
+ <UProgress size="sm" />
315
+ </div>
316
+ <div
317
+ v-else-if="recentQueries.length === 0"
318
+ class="text-dimmed px-5 py-6 text-center text-sm"
319
+ >
320
+ {{ $t("dms_database.overview.recent.empty") }}
321
+ </div>
322
+ <!-- A row links to the query console with the query prefilled
323
+ (the console reads ?source= once on mount). -->
324
+ <NuxtLink
325
+ v-for="entry in recentQueries"
326
+ v-else
327
+ :key="entry.id"
328
+ :to="{ path: QUERY_PATH, query: { source: entry.source } }"
329
+ class="block"
330
+ >
331
+ <DmsActivityItem
332
+ icon="i-ph-code"
333
+ :icon-color="recentTone(entry)"
334
+ mono
335
+ :title="entry.source"
336
+ :trailing="`${entry.durationMs} ms · ${entry.rowCount} ${$t('dms_database.overview.recent.rowsUnit')}`"
337
+ />
338
+ </NuxtLink>
339
+ </div>
340
+ </DmsCard>
341
+ </div>
342
+
343
+ <!-- Tables panel (shared DmsTableView; double-click a row to open it in the
344
+ data browser) -->
345
+ <div class="space-y-2">
346
+ <div class="px-1">
347
+ <h2 class="text-highlighted font-semibold leading-tight">
348
+ {{ $t("dms_database.overview.tables.title") }}
349
+ </h2>
350
+ <p class="text-dimmed mt-0.5 text-xs">
351
+ {{ $t("dms_database.overview.tables.subtitle") }}
352
+ </p>
353
+ </div>
354
+ <DmsTableView
355
+ :location="TABLES_LOCATION"
356
+ :columns="tableColumns"
357
+ :form-components="{}"
358
+ :form-container="{ type: 'drawer' }"
359
+ :row-actions="{ details: true }"
360
+ :default-sort="{ field: 'elementCount', desc: true }"
361
+ :component-id="COMPONENT_ID"
362
+ :page-id="PAGE_ID"
363
+ row-id-key="id"
364
+ />
365
+ </div>
366
+ </div>
367
+ </template>