feat: collapsible chart, paginator controls, localized template slots
- Dashboard chart collapsible with state persisted in localStorage - Events per page user-controlled (5/10/20/50) via select, persisted - Paginator rendered both above and below event list (shared snippet) - Removed viewport-based page size calculation - Template slot descriptions localized (templateSlot.* i18n keys) - Preview As target selector expanded: email, discord, slack added - Tighter event item spacing
This commit is contained in:
@@ -44,6 +44,12 @@
|
||||
let slotErrorLines = $state<Record<string, number | null>>({});
|
||||
let slotErrorTypes = $state<Record<string, string>>({});
|
||||
let validateTimers: Record<string, ReturnType<typeof setTimeout>> = {};
|
||||
/** Clean up validate timers on unmount */
|
||||
onMount(() => {
|
||||
return () => {
|
||||
for (const timer of Object.values(validateTimers)) clearTimeout(timer);
|
||||
};
|
||||
});
|
||||
let dateFormatPreview = $state<Record<string, string | null>>({});
|
||||
let expandedSlots = $state<Set<string>>(new Set());
|
||||
let slotFilter = $state('');
|
||||
@@ -166,14 +172,20 @@
|
||||
);
|
||||
|
||||
// Group slots into event messages vs scheduled messages based on slot name prefix
|
||||
function slotDescription(s: {name: string, description: string}): string {
|
||||
const key = `templateSlot.${s.name}`;
|
||||
const localized = t(key);
|
||||
return localized !== key ? localized : s.description;
|
||||
}
|
||||
|
||||
let templateSlots = $derived([
|
||||
{ group: 'eventMessages', slots: notificationSlots
|
||||
.filter(s => s.name.startsWith('message_'))
|
||||
.map(s => ({ key: s.name, label: s.name.replace('message_', '').replace(/_/g, ' '), description: s.description, rows: s.name === 'message_assets_added' ? 10 : 3, isDateFormat: false }))
|
||||
.map(s => ({ key: s.name, label: s.name.replace('message_', '').replace(/_/g, ' '), description: slotDescription(s), rows: s.name === 'message_assets_added' ? 10 : 3, isDateFormat: false }))
|
||||
},
|
||||
{ group: 'scheduledMessages', slots: notificationSlots
|
||||
.filter(s => !s.name.startsWith('message_'))
|
||||
.map(s => ({ key: s.name, label: s.name.replace(/_/g, ' '), description: s.description, rows: 6, isDateFormat: false }))
|
||||
.map(s => ({ key: s.name, label: s.name.replace(/_/g, ' '), description: slotDescription(s), rows: 6, isDateFormat: false }))
|
||||
},
|
||||
{ group: 'settings', slots: [
|
||||
{ key: 'date_format', label: 'dateFormat', description: 'Date+time format', rows: 1, isDateFormat: true },
|
||||
@@ -418,7 +430,7 @@
|
||||
<p class="font-medium">{config.name}</p>
|
||||
<span class="text-xs px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)]">{config.provider_type}</span>
|
||||
{#if config.user_id === 0}
|
||||
<span class="text-xs px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)]">System</span>
|
||||
<span class="text-xs px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)]">{t('common.system')}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#if config.description}
|
||||
|
||||
Reference in New Issue
Block a user