feat: yoloVisibility config, alwaysShowLabel on groups, site settings panel
- AppConfig gains yoloVisibility: 'auto' | 'never' (default: auto) - Group gains alwaysShowLabel?: boolean - YOLO toggle only renders when showYoloToggle is true (auto + >1 group) - Group divider shows when >1 group OR alwaysShowLabel is set - New /admin/api/config GET+PUT endpoint for yolo settings - Admin: alwaysShowLabel checkbox in group form - Admin: ⚙ Settings panel with YOLO label, description, and visibility controls
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
|
||||
// ── State ────────────────────────────────────────────────────────────────────
|
||||
let unitsData = $state<UnitsData>({ units: [], groups: [] });
|
||||
let selectedItem = $state<{ type: 'unit' | 'group'; id: string } | null>(null);
|
||||
let selectedItem = $state<{ type: 'unit' | 'group' | 'settings'; id: string } | null>(null);
|
||||
let checkedUnitIds = $state<Set<string>>(new Set());
|
||||
let bulkAction = $state<string>(''); // '' | 'move' | 'delete'
|
||||
let bulkMoveTargetGroupId = $state<string>('');
|
||||
@@ -32,6 +32,50 @@
|
||||
let groupFormSnapshot = $state<Partial<Group>>({});
|
||||
let groupFieldError = $state<string | null>(null); // which field has error
|
||||
|
||||
// Site config state
|
||||
let configYoloLabel = $state('YOLO mode');
|
||||
let configYoloDescription = $state('(cross-group conversions)');
|
||||
let configYoloVisibility = $state<'auto' | 'never'>('auto');
|
||||
let configSaving = $state(false);
|
||||
let configError = $state<string | null>(null);
|
||||
let configSuccess = $state<string | null>(null);
|
||||
|
||||
async function loadConfig() {
|
||||
const res = await fetch(`${api}/config`);
|
||||
if (!res.ok) return;
|
||||
const c = await res.json();
|
||||
configYoloLabel = c.yoloLabel ?? 'YOLO mode';
|
||||
configYoloDescription = c.yoloDescription ?? '(cross-group conversions)';
|
||||
configYoloVisibility = c.yoloVisibility ?? 'auto';
|
||||
}
|
||||
|
||||
async function saveConfig() {
|
||||
configSaving = true;
|
||||
configError = null;
|
||||
configSuccess = null;
|
||||
try {
|
||||
const res = await fetch(`${api}/config`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
yoloLabel: configYoloLabel,
|
||||
yoloDescription: configYoloDescription,
|
||||
yoloVisibility: configYoloVisibility,
|
||||
})
|
||||
});
|
||||
if (!res.ok) { configError = 'Save failed'; }
|
||||
else { configSuccess = 'Saved.'; }
|
||||
} catch { configError = 'Network error'; }
|
||||
configSaving = false;
|
||||
}
|
||||
|
||||
function onConfigKeydown(e: KeyboardEvent) {
|
||||
if (e.key === 'Enter' && (e.target as HTMLElement).tagName !== 'TEXTAREA') {
|
||||
e.preventDefault();
|
||||
saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
// ── Auto-ID state ─────────────────────────────────────────────────────────────
|
||||
let unitIdManuallyEdited = $state(false);
|
||||
let groupIdManuallyEdited = $state(false);
|
||||
@@ -195,7 +239,7 @@
|
||||
expandedGroups = new Set([...expandedGroups, ...groups.map((g) => g.id)]);
|
||||
}
|
||||
|
||||
$effect(() => { loadData(); });
|
||||
$effect(() => { loadData(); loadConfig(); });
|
||||
|
||||
async function extractError(res: Response, fallback: string): Promise<string> {
|
||||
const body = await res.json().catch(() => ({}));
|
||||
@@ -842,6 +886,7 @@
|
||||
<div class="tree-footer">
|
||||
<button onclick={() => startNewUnit()}>+ Add Unit</button>
|
||||
<button class="outline" onclick={startNewGroup}>+ Add Group</button>
|
||||
<button class="outline secondary" onclick={() => { selectedItem = { type: 'settings', id: 'settings' }; }}>⚙ Settings</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@@ -953,6 +998,10 @@
|
||||
<option value="alpha">Alphabetical</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" bind:checked={groupForm.alwaysShowLabel} />
|
||||
<span>Always show group label</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" bind:checked={groupForm.hidden} />
|
||||
<span>Don't show on front end</span>
|
||||
@@ -971,6 +1020,35 @@
|
||||
</div>
|
||||
</div><!-- /onkeydown group form -->
|
||||
|
||||
{:else if selectedItem?.type === 'settings'}
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div onkeydown={onConfigKeydown}>
|
||||
<h3>Site Settings</h3>
|
||||
{#if configError}<p class="form-msg form-error" role="alert">{configError}</p>{/if}
|
||||
{#if configSuccess}<p class="form-msg form-success" role="status">{configSuccess}</p>{/if}
|
||||
<fieldset>
|
||||
<legend>YOLO Mode Toggle</legend>
|
||||
<label>
|
||||
Label
|
||||
<input type="text" bind:value={configYoloLabel} placeholder="YOLO mode" />
|
||||
</label>
|
||||
<label>
|
||||
Description <small class="muted">(shown inline after label)</small>
|
||||
<input type="text" bind:value={configYoloDescription} placeholder="(cross-group conversions)" />
|
||||
</label>
|
||||
<label>
|
||||
Visibility
|
||||
<select bind:value={configYoloVisibility}>
|
||||
<option value="auto">Auto — show when more than one group is present</option>
|
||||
<option value="never">Never — always hidden</option>
|
||||
</select>
|
||||
</label>
|
||||
</fieldset>
|
||||
<div class="form-actions">
|
||||
<button onclick={saveConfig} disabled={configSaving}>{configSaving ? 'Saving…' : 'Save'}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{:else}
|
||||
<p class="select-hint">Select a unit or group to edit, or add a new one.</p>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user