Better restrict editing of the live configuration

This commit is contained in:
Dominic Ferrando
2026-07-10 11:21:48 -04:00
parent baaf7b9581
commit 3febec9a3b
@@ -73,6 +73,10 @@
return JSON.stringify(current) !== JSON.stringify(savedSnapshot); return JSON.stringify(current) !== JSON.stringify(savedSnapshot);
}); });
const isLive = $derived(
!!editor && editor.configId !== null && editor.configId === liveConfigId,
);
const selectedMetrics = $derived({ const selectedMetrics = $derived({
gender: metrics.gender, gender: metrics.gender,
age: metrics.age ?? 0, age: metrics.age ?? 0,
@@ -158,32 +162,39 @@
editor.data = res.data.data; editor.data = res.data.data;
} }
async function persist(mode: "update" | "create") { async function persist(mode: "update" | "create", nameOverride?: string) {
if (!editor || !editor.name) return; if (!editor) return;
const name = nameOverride ?? editor.name;
if (!name) return;
if (mode === "update" && editor.configId && editor.configId === liveConfigId)
return;
const target = editor; const target = editor;
saveError = null; saveError = null;
saving = true; saving = true;
try { try {
if (mode === "update" && target.configId) { if (mode === "update" && target.configId) {
const res = await api.standards.configs({ id: target.configId }).put({ const res = await api.standards.configs({ id: target.configId }).put({
name: target.name, name,
datasetId: target.datasetId, datasetId: target.datasetId,
params: target.params, params: target.params,
}); });
if (res.error) throw res.error; if (res.error) throw res.error;
} else { } else {
const res = await api.standards.configs.post({ const res = await api.standards.configs.post({
name: target.name, name,
datasetId: target.datasetId, datasetId: target.datasetId,
params: target.params, params: target.params,
}); });
if (res.error) throw res.error; if (res.error) throw res.error;
// only stamp the new id if the user hasn't switched away in the meantime // only stamp the new id/name if the user hasn't switched away in the meantime
if (editor === target) editor.configId = res.data.id; if (editor === target) {
editor.configId = res.data.id;
editor.name = name;
}
} }
if (editor === target) { if (editor === target) {
savedSnapshot = { savedSnapshot = {
name: target.name, name,
datasetId: target.datasetId, datasetId: target.datasetId,
params: $state.snapshot(target.params), params: $state.snapshot(target.params),
}; };
@@ -201,8 +212,16 @@
return persist(editor?.configId ? "update" : "create"); return persist(editor?.configId ? "update" : "create");
} }
function saveAsNew() { function promptSaveAsNew() {
return persist("create"); if (!editor) return;
const name = prompt(
"Name for the new configuration:",
editor.name ? `${editor.name} copy` : "",
);
if (name === null) return;
const trimmed = name.trim();
if (!trimmed) return;
return persist("create", trimmed);
} }
async function discard() { async function discard() {
@@ -213,6 +232,12 @@
async function setLive() { async function setLive() {
if (!editor?.configId || isDirty) return; if (!editor?.configId || isDirty) return;
const configId = editor.configId; const configId = editor.configId;
if (
!confirm(
`Set "${editor.name || "(untitled)"}" as the live configuration? This will immediately affect live calculations.`,
)
)
return;
saveError = null; saveError = null;
settingLive = true; settingLive = true;
try { try {
@@ -313,11 +338,27 @@
</option> </option>
{/each} {/each}
</select> </select>
<button
class="btn btn-ghost btn-sm btn-square"
title="Save as new configuration"
aria-label="Save as new configuration"
disabled={saving}
onclick={promptSaveAsNew}
>
<span class="text-lg leading-none">+</span>
</button>
{#if isDirty} {#if isDirty}
<span class="badge badge-warning badge-sm">Unsaved</span> <span class="badge badge-warning badge-sm">Unsaved</span>
{:else if currentEditor.configId === liveConfigId} {/if}
<span class="badge badge-success badge-sm">Live</span>
<div class="flex-1"></div>
{#if isLive}
<p class="text-sm text-info opacity-90">
This is the live configuration and is read-only — click "+"
to create an editable copy.
</p>
{/if} {/if}
<div class="flex-1"></div> <div class="flex-1"></div>
@@ -329,27 +370,21 @@
> >
Discard changes Discard changes
</button> </button>
<button
class="btn btn-outline btn-sm"
hidden={currentEditor.configId === liveConfigId || isDirty}
disabled={settingLive || isDirty}
onclick={setLive}
>
{settingLive ? "Setting live..." : "Set as live"}
</button>
<button <button
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"
disabled={saving || !isDirty} hidden={isLive}
disabled={saving || !isDirty || isLive}
onclick={save} onclick={save}
> >
{saving ? "Saving..." : "Save"} {saving ? "Saving..." : "Save"}
</button> </button>
<button <button
class="btn btn-outline btn-sm" class="btn btn-outline btn-sm"
disabled={saving || !currentEditor.name} hidden={currentEditor.configId === liveConfigId}
onclick={saveAsNew} disabled={settingLive || isDirty}
onclick={setLive}
> >
Save as new {settingLive ? "Setting live..." : "Set as live"}
</button> </button>
</div> </div>
@@ -359,6 +394,7 @@
</div> </div>
{/if} {/if}
<section class="w-full flex gap-4 flex-wrap mt-6"> <section class="w-full flex gap-4 flex-wrap mt-6">
<fieldset class="fieldset bg-base-200 p-4"> <fieldset class="fieldset bg-base-200 p-4">
<label class="flex flex-col gap-1"> <label class="flex flex-col gap-1">
@@ -374,6 +410,7 @@
<select <select
class="select select-sm w-36" class="select select-sm w-36"
value={currentEditor.datasetId} value={currentEditor.datasetId}
disabled={isLive}
onchange={(e) => onDatasetChange(e.currentTarget.value)} onchange={(e) => onDatasetChange(e.currentTarget.value)}
> >
{#each datasets as dataset} {#each datasets as dataset}
@@ -385,6 +422,7 @@
<span class="label text-xs">Max level</span> <span class="label text-xs">Max level</span>
<input <input
class="input input-sm w-36" class="input input-sm w-36"
disabled={isLive}
type="number" type="number"
min="1" min="1"
max="100" max="100"
@@ -414,7 +452,7 @@
</label> </label>
<div class="flex gap-3 flex-wrap"> <div class="flex gap-3 flex-wrap">
<fieldset class="fieldset bg-base-300 p-2"> <fieldset class="fieldset bg-base-300 p-2" disabled={isLive}>
<legend class="fieldset-legend text-sm font-semibold" <legend class="fieldset-legend text-sm font-semibold"
>Modifiers</legend >Modifiers</legend
> >
@@ -429,7 +467,7 @@
/> />
</fieldset> </fieldset>
<fieldset class="fieldset bg-base-300 p-2"> <fieldset class="fieldset bg-base-300 p-2" disabled={isLive}>
<legend class="fieldset-legend text-sm font-semibold" <legend class="fieldset-legend text-sm font-semibold"
>Generation</legend >Generation</legend
> >