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);
});
const isLive = $derived(
!!editor && editor.configId !== null && editor.configId === liveConfigId,
);
const selectedMetrics = $derived({
gender: metrics.gender,
age: metrics.age ?? 0,
@@ -158,32 +162,39 @@
editor.data = res.data.data;
}
async function persist(mode: "update" | "create") {
if (!editor || !editor.name) return;
async function persist(mode: "update" | "create", nameOverride?: string) {
if (!editor) return;
const name = nameOverride ?? editor.name;
if (!name) return;
if (mode === "update" && editor.configId && editor.configId === liveConfigId)
return;
const target = editor;
saveError = null;
saving = true;
try {
if (mode === "update" && target.configId) {
const res = await api.standards.configs({ id: target.configId }).put({
name: target.name,
name,
datasetId: target.datasetId,
params: target.params,
});
if (res.error) throw res.error;
} else {
const res = await api.standards.configs.post({
name: target.name,
name,
datasetId: target.datasetId,
params: target.params,
});
if (res.error) throw res.error;
// only stamp the new id if the user hasn't switched away in the meantime
if (editor === target) editor.configId = res.data.id;
// only stamp the new id/name if the user hasn't switched away in the meantime
if (editor === target) {
editor.configId = res.data.id;
editor.name = name;
}
}
if (editor === target) {
savedSnapshot = {
name: target.name,
name,
datasetId: target.datasetId,
params: $state.snapshot(target.params),
};
@@ -201,8 +212,16 @@
return persist(editor?.configId ? "update" : "create");
}
function saveAsNew() {
return persist("create");
function promptSaveAsNew() {
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() {
@@ -213,6 +232,12 @@
async function setLive() {
if (!editor?.configId || isDirty) return;
const configId = editor.configId;
if (
!confirm(
`Set "${editor.name || "(untitled)"}" as the live configuration? This will immediately affect live calculations.`,
)
)
return;
saveError = null;
settingLive = true;
try {
@@ -313,11 +338,27 @@
</option>
{/each}
</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}
<span class="badge badge-warning badge-sm">Unsaved</span>
{:else if currentEditor.configId === liveConfigId}
<span class="badge badge-success badge-sm">Live</span>
{/if}
<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}
<div class="flex-1"></div>
@@ -329,27 +370,21 @@
>
Discard changes
</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
class="btn btn-primary btn-sm"
disabled={saving || !isDirty}
hidden={isLive}
disabled={saving || !isDirty || isLive}
onclick={save}
>
{saving ? "Saving..." : "Save"}
</button>
<button
class="btn btn-outline btn-sm"
disabled={saving || !currentEditor.name}
onclick={saveAsNew}
hidden={currentEditor.configId === liveConfigId}
disabled={settingLive || isDirty}
onclick={setLive}
>
Save as new
{settingLive ? "Setting live..." : "Set as live"}
</button>
</div>
@@ -359,6 +394,7 @@
</div>
{/if}
<section class="w-full flex gap-4 flex-wrap mt-6">
<fieldset class="fieldset bg-base-200 p-4">
<label class="flex flex-col gap-1">
@@ -374,6 +410,7 @@
<select
class="select select-sm w-36"
value={currentEditor.datasetId}
disabled={isLive}
onchange={(e) => onDatasetChange(e.currentTarget.value)}
>
{#each datasets as dataset}
@@ -385,6 +422,7 @@
<span class="label text-xs">Max level</span>
<input
class="input input-sm w-36"
disabled={isLive}
type="number"
min="1"
max="100"
@@ -414,7 +452,7 @@
</label>
<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"
>Modifiers</legend
>
@@ -429,7 +467,7 @@
/>
</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"
>Generation</legend
>