@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.
- package/LICENSE +190 -0
- package/README.md +53 -0
- package/dist/automation/index.d.ts +2 -0
- package/dist/automation/index.js +13 -0
- package/dist/automation/index.js.map +1 -0
- package/dist/automation/tableWatch.d.ts +17 -0
- package/dist/automation/tableWatch.js +189 -0
- package/dist/automation/tableWatch.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +12 -0
- package/dist/config.js.map +1 -0
- package/dist/db/index.d.ts +2 -0
- package/dist/db/index.js +19 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/models/diagram_layouts.model.d.ts +25 -0
- package/dist/db/models/diagram_layouts.model.js +29 -0
- package/dist/db/models/diagram_layouts.model.js.map +1 -0
- package/dist/db/models/diagram_notes.model.d.ts +23 -0
- package/dist/db/models/diagram_notes.model.js +15 -0
- package/dist/db/models/diagram_notes.model.js.map +1 -0
- package/dist/db/models/index.d.ts +4 -0
- package/dist/db/models/index.js +21 -0
- package/dist/db/models/index.js.map +1 -0
- package/dist/db/models/query_history.model.d.ts +25 -0
- package/dist/db/models/query_history.model.js +33 -0
- package/dist/db/models/query_history.model.js.map +1 -0
- package/dist/db/models/saved_queries.model.d.ts +24 -0
- package/dist/db/models/saved_queries.model.js +21 -0
- package/dist/db/models/saved_queries.model.js.map +1 -0
- package/dist/db/tables/diagram_layouts.table.d.ts +7 -0
- package/dist/db/tables/diagram_layouts.table.js +35 -0
- package/dist/db/tables/diagram_layouts.table.js.map +1 -0
- package/dist/db/tables/diagram_notes.table.d.ts +13 -0
- package/dist/db/tables/diagram_notes.table.js +59 -0
- package/dist/db/tables/diagram_notes.table.js.map +1 -0
- package/dist/db/tables/index.d.ts +4 -0
- package/dist/db/tables/index.js +21 -0
- package/dist/db/tables/index.js.map +1 -0
- package/dist/db/tables/query_history.table.d.ts +11 -0
- package/dist/db/tables/query_history.table.js +54 -0
- package/dist/db/tables/query_history.table.js.map +1 -0
- package/dist/db/tables/saved_queries.table.d.ts +12 -0
- package/dist/db/tables/saved_queries.table.js +58 -0
- package/dist/db/tables/saved_queries.table.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/pages/data.d.ts +9 -0
- package/dist/pages/data.js +29 -0
- package/dist/pages/data.js.map +1 -0
- package/dist/pages/index.d.ts +4 -0
- package/dist/pages/index.js +21 -0
- package/dist/pages/index.js.map +1 -0
- package/dist/pages/overview.d.ts +9 -0
- package/dist/pages/overview.js +29 -0
- package/dist/pages/overview.js.map +1 -0
- package/dist/pages/query.d.ts +9 -0
- package/dist/pages/query.js +29 -0
- package/dist/pages/query.js.map +1 -0
- package/dist/pages/schemas.d.ts +9 -0
- package/dist/pages/schemas.js +29 -0
- package/dist/pages/schemas.js.map +1 -0
- package/dist/routes/data.d.ts +25 -0
- package/dist/routes/data.js +324 -0
- package/dist/routes/data.js.map +1 -0
- package/dist/routes/diagram.d.ts +47 -0
- package/dist/routes/diagram.js +200 -0
- package/dist/routes/diagram.js.map +1 -0
- package/dist/routes/health.d.ts +6 -0
- package/dist/routes/health.js +35 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/index.d.ts +6 -0
- package/dist/routes/index.js +23 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/query.d.ts +26 -0
- package/dist/routes/query.js +245 -0
- package/dist/routes/query.js.map +1 -0
- package/dist/routes/schemas.d.ts +8 -0
- package/dist/routes/schemas.js +35 -0
- package/dist/routes/schemas.js.map +1 -0
- package/dist/routes/tables.d.ts +23 -0
- package/dist/routes/tables.js +94 -0
- package/dist/routes/tables.js.map +1 -0
- package/dist/service/fieldDescriptor.d.ts +66 -0
- package/dist/service/fieldDescriptor.js +156 -0
- package/dist/service/fieldDescriptor.js.map +1 -0
- package/dist/service/health.d.ts +20 -0
- package/dist/service/health.js +58 -0
- package/dist/service/health.js.map +1 -0
- package/dist/service/introspect.d.ts +6 -0
- package/dist/service/introspect.js +312 -0
- package/dist/service/introspect.js.map +1 -0
- package/dist/service/schemaLabels.d.ts +7 -0
- package/dist/service/schemaLabels.js +39 -0
- package/dist/service/schemaLabels.js.map +1 -0
- package/dist/service/schemaRegistry.d.ts +2 -0
- package/dist/service/schemaRegistry.js +8 -0
- package/dist/service/schemaRegistry.js.map +1 -0
- package/dist/service/stagedSerialization.d.ts +6 -0
- package/dist/service/stagedSerialization.js +54 -0
- package/dist/service/stagedSerialization.js.map +1 -0
- package/dist/service/types.d.ts +67 -0
- package/dist/service/types.js +3 -0
- package/dist/service/types.js.map +1 -0
- package/dist/types/constants.d.ts +3 -0
- package/dist/types/constants.js +7 -0
- package/dist/types/constants.js.map +1 -0
- package/dist/types/responses.d.ts +14 -0
- package/dist/types/responses.js +3 -0
- package/dist/types/responses.js.map +1 -0
- package/dist/utils/query.d.ts +17 -0
- package/dist/utils/query.js +116 -0
- package/dist/utils/query.js.map +1 -0
- package/dist/utils/requestValidation.d.ts +2 -0
- package/dist/utils/requestValidation.js +14 -0
- package/dist/utils/requestValidation.js.map +1 -0
- package/frontend-vue/.editorconfig +12 -0
- package/frontend-vue/.prettierrc +11 -0
- package/frontend-vue/app/components/DataBrowserSidebar.vue +254 -0
- package/frontend-vue/app/components/DataBrowserTabBar.vue +166 -0
- package/frontend-vue/app/components/DataGrid.vue +735 -0
- package/frontend-vue/app/components/DataGridCell.vue +147 -0
- package/frontend-vue/app/components/DataGridHeaderCell.vue +175 -0
- package/frontend-vue/app/components/DiagramNoteNode.vue +100 -0
- package/frontend-vue/app/components/DiagramRelationEdge.vue +62 -0
- package/frontend-vue/app/components/DiagramStubNode.vue +37 -0
- package/frontend-vue/app/components/DiagramTableNode.vue +94 -0
- package/frontend-vue/app/components/JsonCellPanel.vue +93 -0
- package/frontend-vue/app/components/QueryBuilderPanel.vue +501 -0
- package/frontend-vue/app/components/QueryFavoritesPanel.vue +127 -0
- package/frontend-vue/app/components/QueryHistoryPanel.vue +134 -0
- package/frontend-vue/app/components/QueryResultPanel.vue +204 -0
- package/frontend-vue/app/components/SchemaDiagram.vue +1150 -0
- package/frontend-vue/app/components/SchemaTableInspector.vue +278 -0
- package/frontend-vue/app/composables/useDataBrowserGrid.ts +162 -0
- package/frontend-vue/app/composables/useDataBrowserTabs.ts +255 -0
- package/frontend-vue/app/composables/useDatabaseHealth.ts +50 -0
- package/frontend-vue/app/composables/useDatabaseSchemas.ts +92 -0
- package/frontend-vue/app/composables/useDiagramPersistence.ts +99 -0
- package/frontend-vue/app/composables/useQueryRuntime.ts +118 -0
- package/frontend-vue/app/composables/useQueryStore.ts +117 -0
- package/frontend-vue/app/composables/useTableViewRowClick.ts +22 -0
- package/frontend-vue/app/custom-pages/database/data.vue +42 -0
- package/frontend-vue/app/custom-pages/database/overview.vue +367 -0
- package/frontend-vue/app/custom-pages/database/query.vue +432 -0
- package/frontend-vue/app/custom-pages/database/schemas.vue +216 -0
- package/frontend-vue/app/utils/databaseFieldModifiers.ts +33 -0
- package/frontend-vue/app/utils/databaseFieldTypeIcons.ts +224 -0
- package/frontend-vue/app/utils/dmsTableView.ts +83 -0
- package/frontend-vue/app/utils/relativeTime.ts +15 -0
- package/frontend-vue/app/utils/stagedSerialization.ts +91 -0
- package/frontend-vue/dms.frontend.ts +38 -0
- package/frontend-vue/eslint.config.mjs +16 -0
- package/frontend-vue/i18n/locales/database-en-GB.json +239 -0
- package/frontend-vue/i18n/locales/database-fr-FR.json +239 -0
- package/frontend-vue/package.json +42 -0
- package/frontend-vue/pnpm-lock.yaml +5819 -0
- package/frontend-vue/pnpm-workspace.yaml +2 -0
- package/frontend-vue/tests/persistence.test.ts +70 -0
- package/frontend-vue/tests/queryRuntime.test.ts +53 -0
- package/frontend-vue/tests/registration.test.ts +33 -0
- package/frontend-vue/tsconfig.json +13 -0
- package/frontend-vue/vitest.config.ts +5 -0
- 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>
|