fix(objects): refresh event editor on selection and fix docs-export manifest
This commit is contained in:
1 parent
c1888a2364
commit
eaad88cc80
6 files changed
+25
-13
No files matched your search
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from "vue";
|
import { computed, onMounted, ref, watch } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import type { CalendarDateTime } from "@internationalized/date";
|
import type { CalendarDateTime } from "@internationalized/date";
|
||||||
import { Check, Clock, Eye, Loader2, X } from "@lucide/vue";
|
import { Check, Clock, Eye, Loader2, X } from "@lucide/vue";
|
||||||
@@ -19,6 +19,7 @@ const loading = ref(false),
|
|||||||
saving = ref(false),
|
saving = ref(false),
|
||||||
error = ref(""),
|
error = ref(""),
|
||||||
preview = ref("");
|
preview = ref("");
|
||||||
|
let loadRequestId = 0;
|
||||||
const activeTab = ref<"definition" | "schedule" | "comment" | "preview">("definition");
|
const activeTab = ref<"definition" | "schedule" | "comment" | "preview">("definition");
|
||||||
const draft = ref<any>({ name: props.name || "", schema: props.schema, schedule: { mode: "every", intervalValue: "1", intervalUnit: "DAY" }, preserve: true, enabled: true, body: "" });
|
const draft = ref<any>({ name: props.name || "", schema: props.schema, schedule: { mode: "every", intervalValue: "1", intervalUnit: "DAY" }, preserve: true, enabled: true, body: "" });
|
||||||
const existing = computed(() => !!props.name);
|
const existing = computed(() => !!props.name);
|
||||||
@@ -63,6 +64,8 @@ function refreshPreview() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function load() {
|
async function load() {
|
||||||
|
const requestId = ++loadRequestId;
|
||||||
|
error.value = "";
|
||||||
if (!props.name) {
|
if (!props.name) {
|
||||||
refreshPreview();
|
refreshPreview();
|
||||||
return;
|
return;
|
||||||
@@ -70,6 +73,7 @@ async function load() {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const info: MysqlEventInfo = await api.getEventInfo(props.connection.id, props.database, props.schema, props.name);
|
const info: MysqlEventInfo = await api.getEventInfo(props.connection.id, props.database, props.schema, props.name);
|
||||||
|
if (requestId !== loadRequestId) return;
|
||||||
draft.value = {
|
draft.value = {
|
||||||
name: info.name,
|
name: info.name,
|
||||||
schema: info.schema,
|
schema: info.schema,
|
||||||
@@ -83,9 +87,10 @@ async function load() {
|
|||||||
};
|
};
|
||||||
refreshPreview();
|
refreshPreview();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (requestId !== loadRequestId) return;
|
||||||
error.value = e instanceof Error ? e.message : String(e);
|
error.value = e instanceof Error ? e.message : String(e);
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
if (requestId === loadRequestId) loading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function save() {
|
async function save() {
|
||||||
@@ -111,12 +116,13 @@ async function save() {
|
|||||||
saving.value = false;
|
saving.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
watch(() => props.name, load);
|
||||||
onMounted(load);
|
onMounted(load);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="flex min-h-0 flex-1 flex-col gap-3 overflow-auto p-4" data-mysql-event-editor>
|
<div class="flex min-h-0 flex-1 flex-col gap-3 overflow-auto p-4" data-mysql-event-editor>
|
||||||
<div class="flex items-center justify-between border-b pb-2">
|
<div class="flex items-center justify-between border-b pb-2">
|
||||||
<div class="flex items-center gap-2 text-sm font-semibold"><Clock class="h-4 w-4 text-orange-300" /> {{ t("contextMenu.eventEditorTitle") }}</div>
|
<div class="flex items-center gap-2 text-sm font-semibold"><Clock class="h-4 w-4 text-orange-400" /> {{ t("contextMenu.eventEditorTitle") }}</div>
|
||||||
<Button variant="ghost" size="icon" class="h-6 w-6" :aria-label="t('contextMenu.eventCancel')" @click="emit('close')"><X class="h-4 w-4" /></Button>
|
<Button variant="ghost" size="icon" class="h-6 w-6" :aria-label="t('contextMenu.eventCancel')" @click="emit('close')"><X class="h-4 w-4" /></Button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="loading" class="flex items-center gap-2 text-sm text-muted-foreground"><Loader2 class="h-4 w-4 animate-spin" /> {{ t("contextMenu.eventLoading") }}</div>
|
<div v-if="loading" class="flex items-center gap-2 text-sm text-muted-foreground"><Loader2 class="h-4 w-4 animate-spin" /> {{ t("contextMenu.eventLoading") }}</div>
|
||||||
|
|||||||
@@ -982,6 +982,10 @@ function runRowClickAction(clickDetail: number, requestId: number) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (node.type === "event") {
|
if (node.type === "event") {
|
||||||
|
void openObjectBrowser(false, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (node.type === "group-events") {
|
||||||
void openObjectBrowser();
|
void openObjectBrowser();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1343,7 +1347,7 @@ function requestDeleteSelectedNode(): boolean {
|
|||||||
function onDoubleClick(event: MouseEvent) {
|
function onDoubleClick(event: MouseEvent) {
|
||||||
if (dataTabOpenModeFromTreeClick(activeNode.value.type, event, settingsStore.editorSettings.shortcuts.openDataInNewTab) === "new-tab") return;
|
if (dataTabOpenModeFromTreeClick(activeNode.value.type, event, settingsStore.editorSettings.shortcuts.openDataInNewTab) === "new-tab") return;
|
||||||
if (activeNode.value.type === "event") {
|
if (activeNode.value.type === "event") {
|
||||||
void openObjectBrowser();
|
void openObjectBrowser(false, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (activeNode.value.type === "group-events") {
|
if (activeNode.value.type === "group-events") {
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ function getIconInfo(node: TreeNode): { icon: any; colorClass: string } | null {
|
|||||||
case "trigger":
|
case "trigger":
|
||||||
return { icon: Zap, colorClass: "text-orange-300" };
|
return { icon: Zap, colorClass: "text-orange-300" };
|
||||||
case "event":
|
case "event":
|
||||||
return { icon: Clock, colorClass: "text-orange-300" };
|
return { icon: Clock, colorClass: "text-orange-400" };
|
||||||
case "redis-db":
|
case "redis-db":
|
||||||
return { icon: Database, colorClass: "text-red-400" };
|
return { icon: Database, colorClass: "text-red-400" };
|
||||||
case "mq-tenant":
|
case "mq-tenant":
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export function getTreeNodeIconInfo(node: TreeNode): TreeNodeIconInfo | null {
|
|||||||
case "group-events":
|
case "group-events":
|
||||||
return { icon: Clock, colorClass: "text-orange-400" };
|
return { icon: Clock, colorClass: "text-orange-400" };
|
||||||
case "event":
|
case "event":
|
||||||
return { icon: Clock, colorClass: "text-orange-300" };
|
return { icon: Clock, colorClass: "text-orange-400" };
|
||||||
case "group-constraints":
|
case "group-constraints":
|
||||||
case "constraint":
|
case "constraint":
|
||||||
return { icon: Key, colorClass: "text-amber-500" };
|
return { icon: Key, colorClass: "text-amber-500" };
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import tailwindcss from "@tailwindcss/vite";
|
|||||||
import { connectionTypesPlugin } from "./viteConnectionTypesPlugin.ts";
|
import { connectionTypesPlugin } from "./viteConnectionTypesPlugin.ts";
|
||||||
|
|
||||||
const repoRoot = path.resolve(__dirname, "../..");
|
const repoRoot = path.resolve(__dirname, "../..");
|
||||||
|
const normalizedRepoRoot = repoRoot.replaceAll(path.sep, "/");
|
||||||
const assetsDir = path.join(repoRoot, "crates/dbx-core/assets");
|
const assetsDir = path.join(repoRoot, "crates/dbx-core/assets");
|
||||||
const fontPath = path.join(__dirname, "public/fonts/geist-latin-wght-normal.woff2");
|
const fontPath = path.join(__dirname, "public/fonts/geist-latin-wght-normal.woff2");
|
||||||
|
|
||||||
@@ -86,25 +87,26 @@ function exportBundlePlugin() {
|
|||||||
// Vite tags CSS the same way. The file on disk is the part before the
|
// Vite tags CSS the same way. The file on disk is the part before the
|
||||||
// query; without stripping it every SFC would be missing from the
|
// query; without stripping it every SFC would be missing from the
|
||||||
// manifest, which is the failure this whole derivation exists to avoid.
|
// manifest, which is the failure this whole derivation exists to avoid.
|
||||||
const id = rawId.split("?")[0];
|
const id = path.resolve(rawId.split("?")[0]);
|
||||||
if (!path.isAbsolute(id)) return;
|
if (!path.isAbsolute(id)) return;
|
||||||
|
const normalizedId = id.replaceAll(path.sep, "/");
|
||||||
|
|
||||||
// `lastIndexOf`: under pnpm a real path is
|
// `lastIndexOf`: under pnpm a real path is
|
||||||
// `<root>/node_modules/.pnpm/marked@18.0.4/node_modules/marked/…`, and
|
// `<root>/node_modules/.pnpm/marked@18.0.4/node_modules/marked/…`, and
|
||||||
// the FIRST occurrence yields the package name `.pnpm`.
|
// the FIRST occurrence yields the package name `.pnpm`.
|
||||||
const nodeModules = id.lastIndexOf("/node_modules/");
|
const nodeModules = normalizedId.lastIndexOf("/node_modules/");
|
||||||
if (nodeModules !== -1) {
|
if (nodeModules !== -1) {
|
||||||
const after = id.slice(nodeModules + "/node_modules/".length);
|
const after = normalizedId.slice(nodeModules + "/node_modules/".length);
|
||||||
const name = after.startsWith("@") ? after.split("/").slice(0, 2).join("/") : after.split("/")[0];
|
const name = after.startsWith("@") ? after.split("/").slice(0, 2).join("/") : after.split("/")[0];
|
||||||
if (name in deps) return;
|
if (name in deps) return;
|
||||||
const manifest = path.join(id.slice(0, nodeModules), "node_modules", name, "package.json");
|
const manifest = path.join(normalizedId.slice(0, nodeModules), "node_modules", name, "package.json");
|
||||||
if (!isFile(manifest)) return;
|
if (!isFile(manifest)) return;
|
||||||
deps[name] = JSON.parse(readFileSync(manifest, "utf8")).version;
|
deps[name] = JSON.parse(readFileSync(manifest, "utf8")).version;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!id.startsWith(repoRoot) || !isFile(id)) return;
|
if (!normalizedId.startsWith(normalizedRepoRoot) || !isFile(id)) return;
|
||||||
sources[path.relative(repoRoot, id)] = sha256(readFileSync(id));
|
sources[path.relative(repoRoot, id).replaceAll(path.sep, "/")] = sha256(readFileSync(id));
|
||||||
};
|
};
|
||||||
|
|
||||||
const stylesheets = new Set<string>();
|
const stylesheets = new Set<string>();
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
"apps/desktop/src/lib/diagram/erDiagram.ts": "1b9522b41cd2ee15ef04dc11e0f855459287fca2d5e0f551263dfc6f8de5764e",
|
"apps/desktop/src/lib/diagram/erDiagram.ts": "1b9522b41cd2ee15ef04dc11e0f855459287fca2d5e0f551263dfc6f8de5764e",
|
||||||
"apps/desktop/src/styles/tokens.css": "b8852ce50865ad98ebc992971f7a06670c5d830ca82efcee956f8d3eb7c2e9a3",
|
"apps/desktop/src/styles/tokens.css": "b8852ce50865ad98ebc992971f7a06670c5d830ca82efcee956f8d3eb7c2e9a3",
|
||||||
"apps/desktop/tsconfig.json": "5ad521941835ad8529e9b81ba79c11650e5f020f0e376b7a9f17e22a9306fa76",
|
"apps/desktop/tsconfig.json": "5ad521941835ad8529e9b81ba79c11650e5f020f0e376b7a9f17e22a9306fa76",
|
||||||
"apps/desktop/vite.docs-export.config.ts": "edfb240d8b73e7dfca309f25bc03ea7612dfc8239f11f971d57daeadd8347161"
|
"apps/desktop/vite.docs-export.config.ts": "833bc2cbd99f9c0740cbc1584640458492bad75c9518db4d1ce0edd6958881ec"
|
||||||
},
|
},
|
||||||
"deps": {
|
"deps": {
|
||||||
"@vue/reactivity": "3.5.35",
|
"@vue/reactivity": "3.5.35",
|
||||||
|
|||||||
Reference in new issue
Block a user