feat: UX improvements — secure webhooks, locale fixes, dynamic languages, UI polish
- Remove top paginator from dashboard events, keep only bottom - Fix test message locale: pass UI locale to email/matrix bot tests - Convert webhook auth mode from text input to icon grid selector - Generate secure UUID tokens for webhook URLs instead of sequential IDs - Move Recent Payloads into per-provider expandable container (lazy-loaded) - Make template config languages dynamic via app settings instead of hardcoded - Change default dev port to 5175
This commit is contained in:
@@ -14,7 +14,9 @@
|
||||
import IconButton from '$lib/components/IconButton.svelte';
|
||||
import ErrorBanner from '$lib/components/ErrorBanner.svelte';
|
||||
import IconGridSelect from '$lib/components/IconGridSelect.svelte';
|
||||
import { providerTypeItems, providerDefaultIcon } from '$lib/grid-items';
|
||||
import { providerTypeItems, providerDefaultIcon, webhookAuthModeItems } from '$lib/grid-items';
|
||||
|
||||
const gridItemSources: Record<string, () => any[]> = { webhookAuthModeItems };
|
||||
import { globalProviderFilter } from '$lib/stores/provider-filter.svelte';
|
||||
import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
|
||||
import { highlightFromUrl } from '$lib/highlight';
|
||||
@@ -189,7 +191,9 @@
|
||||
{t(editing && field.editLabel ? field.editLabel : field.label)}
|
||||
{#if field.optional}<span class="text-xs text-[var(--color-muted-foreground)]">({t('providers.optional')})</span>{/if}
|
||||
</label>
|
||||
{#if field.type === 'number'}
|
||||
{#if field.type === 'grid-select' && field.gridItems}
|
||||
<IconGridSelect items={gridItemSources[field.gridItems]()} bind:value={form[field.key]} columns={field.gridColumns ?? 2} compact />
|
||||
{:else if field.type === 'number'}
|
||||
<input id="prv-{field.key}" type="number" bind:value={form[field.key]}
|
||||
min={field.min} max={field.max}
|
||||
class="w-full px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
||||
@@ -207,7 +211,7 @@
|
||||
{#if descriptor?.webhookUrlPattern && editing}
|
||||
<div class="bg-[var(--color-muted)] rounded-md p-3">
|
||||
<label class="block text-sm font-medium mb-1">{t('providers.webhookUrl')}</label>
|
||||
<code class="text-xs select-all break-all">{descriptor.webhookUrlPattern.replace('{id}', String(editing))}</code>
|
||||
<code class="text-xs select-all break-all">{descriptor.webhookUrlPattern.replace('{token}', providers.find(p => p.id === editing)?.webhook_token ?? '')}</code>
|
||||
<p class="text-xs text-[var(--color-muted-foreground)] mt-1">{t('providers.webhookUrlHint')}</p>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -254,7 +258,7 @@
|
||||
<p class="text-xs text-[var(--color-muted-foreground)] font-mono">{provider.config.host}:{provider.config.port || 3493}</p>
|
||||
{/if}
|
||||
{#if provDesc?.webhookUrlPattern}
|
||||
<p class="text-xs text-[var(--color-muted-foreground)] font-mono mt-0.5">{t('providers.webhookUrl')}: <span class="select-all">{provDesc.webhookUrlPattern.replace('{id}', String(provider.id))}</span></p>
|
||||
<p class="text-xs text-[var(--color-muted-foreground)] font-mono mt-0.5">{t('providers.webhookUrl')}: <span class="select-all">{provDesc.webhookUrlPattern.replace('{token}', provider.webhook_token)}</span></p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -263,10 +267,10 @@
|
||||
<IconButton icon="mdiDelete" title={t('common.delete')} onclick={() => startDelete(provider)} variant="danger" />
|
||||
</div>
|
||||
</div>
|
||||
{#if provDesc?.payloadHistory && !showForm}
|
||||
<WebhookPayloadHistory providerId={provider.id} />
|
||||
{/if}
|
||||
</Card>
|
||||
{#if provDesc?.payloadHistory && !showForm}
|
||||
<WebhookPayloadHistory providerId={provider.id} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user