diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index 7d8d07e..f268317 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -157,6 +157,9 @@ export const app = new Elysia() params: t.Object({ id: t.String() }), body: t.Object({ name: t.String(), datasetId: t.String(), params: StandardsParamsSchema }) }) + .delete("/configs/:id", async ({ params: { id } }) => { + await s.Standards.Configs.delete(id); + }, { params: t.Object({ id: t.String() }) }) .get("/datasets", async () => { return await s.Standards.Datasets.list(); }) diff --git a/apps/api/src/services/standards.ts b/apps/api/src/services/standards.ts index 60fbcfc..2ace0cc 100644 --- a/apps/api/src/services/standards.ts +++ b/apps/api/src/services/standards.ts @@ -61,6 +61,18 @@ class StandardsConfigService { .execute(); if (result[0]?.numUpdatedRows === 0n) throw new Error(`No config with id "${id}"`); } + + async delete(id: string) { + const { count } = await db.selectFrom("standards_configs") + .select(db.fn.countAll().as("count")) + .executeTakeFirstOrThrow(); + if (Number(count) <= 1) throw new Error("Cannot delete the last remaining standards configuration"); + + const result = await db.deleteFrom("standards_configs") + .where("id", "=", id) + .execute(); + if (result[0]?.numDeletedRows === 0n) throw new Error(`No config with id "${id}"`); + } } class StandardsDatasetService { diff --git a/apps/portal/src/routes/(app)/calculator/+page.svelte b/apps/portal/src/routes/(app)/calculator/+page.svelte index e7f31a3..e62ed1b 100644 --- a/apps/portal/src/routes/(app)/calculator/+page.svelte +++ b/apps/portal/src/routes/(app)/calculator/+page.svelte @@ -54,6 +54,7 @@ let saveError = $state(null); let saving = $state(false); let settingLive = $state(false); + let deleting = $state(false); const allStandards = $derived( editor @@ -229,6 +230,26 @@ await loadConfig(editor.configId, { skipDirtyCheck: true }); } + async function deleteConfig() { + if (!editor?.configId || isLive) return; + if (!confirm(`Delete "${editor.name || "(untitled)"}"? This cannot be undone.`)) + return; + const configId = editor.configId; + saveError = null; + deleting = true; + try { + const res = await api.standards.configs({ id: configId }).delete(); + if (res.error) throw res.error; + localStorage.removeItem(DRAFT_KEY); + await refreshLists(); + if (liveConfigId) await loadConfig(liveConfigId, { skipDirtyCheck: true }); + } catch (err) { + saveError = errorMessage(err); + } finally { + deleting = false; + } + } + async function setLive() { if (!editor?.configId || isDirty) return; const configId = editor.configId; @@ -346,7 +367,7 @@ class="btn btn-ghost btn-sm btn-square" title="Save as new configuration" aria-label="Save as new configuration" - disabled={saving} + disabled={saving || deleting} onclick={promptSaveAsNew} > + @@ -377,7 +398,7 @@ + + {#if saveError}