Use combobox for target type selector, refresh previews on change
All checks were successful
Validate / Hassfest (push) Successful in 3s

- Replace toggle buttons with <select> dropdown for target type
- Add refreshAllPreviews() that immediately re-renders all open
  previews when target type changes (no 800ms debounce)
- validateSlot() now accepts optional immediate flag

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 21:13:53 +03:00
parent 4babaddd87
commit c5a3521b14

View File

@@ -28,7 +28,7 @@
let slotErrorTypes = $state<Record<string, string>>({}); let slotErrorTypes = $state<Record<string, string>>({});
let validateTimers: Record<string, ReturnType<typeof setTimeout>> = {}; let validateTimers: Record<string, ReturnType<typeof setTimeout>> = {};
function validateSlot(slotKey: string, template: string) { function validateSlot(slotKey: string, template: string, immediate = false) {
// Clear previous timer // Clear previous timer
if (validateTimers[slotKey]) clearTimeout(validateTimers[slotKey]); if (validateTimers[slotKey]) clearTimeout(validateTimers[slotKey]);
if (!template) { if (!template) {
@@ -40,8 +40,7 @@
return; return;
} }
// Debounce 800ms const doValidate = async () => {
validateTimers[slotKey] = setTimeout(async () => {
try { try {
const res = await api('/template-configs/preview-raw', { method: 'POST', body: JSON.stringify({ template, target_type: previewTargetType }) }); const res = await api('/template-configs/preview-raw', { method: 'POST', body: JSON.stringify({ template, target_type: previewTargetType }) });
slotErrors = { ...slotErrors, [slotKey]: res.error || '' }; slotErrors = { ...slotErrors, [slotKey]: res.error || '' };
@@ -60,7 +59,21 @@
slotErrorLines = { ...slotErrorLines, [slotKey]: null }; slotErrorLines = { ...slotErrorLines, [slotKey]: null };
slotErrorTypes = { ...slotErrorTypes, [slotKey]: '' }; slotErrorTypes = { ...slotErrorTypes, [slotKey]: '' };
} }
}, 800); };
if (immediate) { doValidate(); }
else { validateTimers[slotKey] = setTimeout(doValidate, 800); }
}
function refreshAllPreviews() {
// Re-validate and re-preview all slots that have content (immediate, no debounce)
for (const group of templateSlots) {
for (const slot of group.slots) {
const template = (form as any)[slot.key];
if (template && slot.key !== 'date_format') {
validateSlot(slot.key, template, true);
}
}
}
} }
const defaultForm = () => ({ const defaultForm = () => ({
@@ -181,17 +194,12 @@
<!-- Target type selector for preview --> <!-- Target type selector for preview -->
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<label class="text-sm font-medium">{t('templateConfig.previewAs')}:</label> <label for="preview-target" class="text-sm font-medium">{t('templateConfig.previewAs')}:</label>
<div class="flex gap-1"> <select id="preview-target" bind:value={previewTargetType} onchange={refreshAllPreviews}
<button type="button" onclick={() => previewTargetType = 'telegram'} class="px-2 py-1 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]">
class="px-3 py-1 text-xs rounded-md transition-colors {previewTargetType === 'telegram' ? 'bg-[var(--color-primary)] text-[var(--color-primary-foreground)]' : 'bg-[var(--color-muted)] text-[var(--color-muted-foreground)]'}"> <option value="telegram">Telegram</option>
Telegram <option value="webhook">Webhook</option>
</button> </select>
<button type="button" onclick={() => previewTargetType = 'webhook'}
class="px-3 py-1 text-xs rounded-md transition-colors {previewTargetType === 'webhook' ? 'bg-[var(--color-primary)] text-[var(--color-primary-foreground)]' : 'bg-[var(--color-muted)] text-[var(--color-muted-foreground)]'}">
Webhook
</button>
</div>
</div> </div>
{#each templateSlots as group} {#each templateSlots as group}