@svadmin/ui 0.76.0 → 0.78.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.css +439 -72
- package/dist/app.theme.css +439 -72
- package/dist/component-registry.d.ts +300 -0
- package/dist/component-registry.js +2 -0
- package/dist/components/AboutSettings.svelte +4 -3
- package/dist/components/AboutSettings.svelte.d.ts +1 -0
- package/dist/components/AdminApp.svelte +30 -15
- package/dist/components/ApiSettings.svelte +39 -2
- package/dist/components/AppearanceSettings.svelte +35 -9
- package/dist/components/AppearanceSettings.svelte.d.ts +4 -1
- package/dist/components/AutoTable.svelte +10 -6
- package/dist/components/AutoTable.svelte.d.ts +2 -0
- package/dist/components/IntegrationsSettings.svelte +5 -3
- package/dist/components/IntegrationsSettings.svelte.d.ts +1 -0
- package/dist/components/Layout.svelte +38 -5
- package/dist/components/Layout.svelte.d.ts +4 -3
- package/dist/components/NotificationsSettings.svelte +18 -12
- package/dist/components/NotificationsSettings.svelte.d.ts +1 -0
- package/dist/components/ProfilePage.svelte +3 -3
- package/dist/components/ProfilePage.svelte.d.ts +4 -1
- package/dist/components/ResourceOverview.svelte +36 -0
- package/dist/components/ResourceOverview.svelte.d.ts +6 -0
- package/dist/components/ResourceOverviewCard.svelte +50 -0
- package/dist/components/ResourceOverviewCard.svelte.d.ts +7 -0
- package/dist/components/SecuritySettings.svelte +9 -6
- package/dist/components/SecuritySettings.svelte.d.ts +4 -1
- package/dist/components/Sidebar.svelte +1 -1
- package/dist/components/account/SettingsEnterprisePage.svelte +19 -19
- package/dist/components/account/SettingsPlainPage.svelte +7 -3
- package/dist/components/account/SettingsSidebarPage.svelte +3 -3
- package/dist/components/content/ContentPageHeader.svelte +20 -4
- package/dist/components/content/ContentPageHeader.svelte.d.ts +2 -0
- package/dist/components/content/ContentPageShell.svelte +12 -3
- package/dist/components/content/ContentPageShell.svelte.d.ts +1 -0
- package/dist/components/content/DashboardPage.svelte +35 -0
- package/dist/components/content/DashboardPage.svelte.d.ts +13 -0
- package/dist/components/content/FeedbackNotice.svelte +1 -1
- package/dist/components/content/MetricBlock.svelte +1 -1
- package/dist/components/content/PageSection.svelte +28 -0
- package/dist/components/content/PageSection.svelte.d.ts +10 -0
- package/dist/components/content/SettingsFieldRow.svelte +10 -5
- package/dist/components/content/SettingsFieldRow.svelte.d.ts +1 -0
- package/dist/components/content/SettingsGroup.svelte +3 -3
- package/dist/components/content/SettingsGroup.svelte.d.ts +1 -0
- package/dist/components/content/index.d.ts +2 -0
- package/dist/components/content/index.js +2 -0
- package/dist/components/ui/button/button.svelte +17 -1
- package/dist/components/ui/button/button.svelte.d.ts +1 -0
- package/dist/components/ui/sheet/sheet.svelte +3 -0
- package/dist/default-theme.d.ts +3 -0
- package/dist/default-theme.js +4 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/page-chrome.css +4 -4
- package/dist/recipes.d.ts +77 -38
- package/dist/recipes.js +33 -8
- package/package.json +3 -3
|
@@ -22,6 +22,7 @@ const registryEntries = [
|
|
|
22
22
|
description: { kind: "string" },
|
|
23
23
|
pageId: { kind: "string" },
|
|
24
24
|
width: variant("contentPageRecipe", "width"),
|
|
25
|
+
density: variant("contentPageRecipe", "density"),
|
|
25
26
|
class: { kind: "string" },
|
|
26
27
|
},
|
|
27
28
|
slots: { children: { kind: "snippet", required: true }, actions: { kind: "snippet" } },
|
|
@@ -37,6 +38,7 @@ const registryEntries = [
|
|
|
37
38
|
eyebrow: { kind: "string" },
|
|
38
39
|
description: { kind: "string" },
|
|
39
40
|
breadcrumbs: { kind: "string[]" },
|
|
41
|
+
density: variant("contentHeaderRecipe", "density"),
|
|
40
42
|
class: { kind: "string" },
|
|
41
43
|
},
|
|
42
44
|
slots: { actions: { kind: "snippet" } },
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
import { Badge } from './ui/badge/index.js';
|
|
5
5
|
import DescriptionList from './content/DescriptionList.svelte';
|
|
6
6
|
import SettingsGroup from './content/SettingsGroup.svelte';
|
|
7
|
+
import ContentPageHeader from './content/ContentPageHeader.svelte';
|
|
7
8
|
|
|
8
9
|
const i18n = useTranslation();
|
|
9
10
|
const adminContext = captureAdminContext();
|
|
10
11
|
const resources = $derived(adminContext.resources);
|
|
11
12
|
const providerNames = $derived(adminContext.getDataProviderNames());
|
|
12
|
-
let { version: suppliedVersion }: { version?: string } = $props();
|
|
13
|
+
let { version: suppliedVersion, headingLevel = 'h1' }: { version?: string; headingLevel?: 'h1' | 'h2' } = $props();
|
|
13
14
|
const buildVersion = '__SVADMIN_VERSION__';
|
|
14
15
|
const version = $derived(suppliedVersion ?? (
|
|
15
16
|
buildVersion.startsWith('__')
|
|
@@ -19,8 +20,8 @@
|
|
|
19
20
|
</script>
|
|
20
21
|
|
|
21
22
|
<div class="svadmin-u-b3542e058833">
|
|
22
|
-
<
|
|
23
|
-
<SettingsGroup title="svadmin" description={i18n.t('settings.currentEnvironment')} bodyClass="space-y-6">
|
|
23
|
+
<ContentPageHeader title={i18n.t('settings.about')} {headingLevel} />
|
|
24
|
+
<SettingsGroup title="svadmin" description={i18n.t('settings.currentEnvironment')} bodyClass="space-y-6" headingLevel={headingLevel === 'h1' ? 'h2' : 'h3'}>
|
|
24
25
|
<DescriptionList columns={2} items={[
|
|
25
26
|
{ label: i18n.t('settings.version'), value: version },
|
|
26
27
|
{ label: i18n.t('settings.language'), value: i18n.locale },
|
|
@@ -62,6 +62,10 @@
|
|
|
62
62
|
import { setComponentRegistry, type ComponentRegistry } from '../component-registry.svelte.js';
|
|
63
63
|
import Layout, { type LayoutAIAssistantProps } from './Layout.svelte';
|
|
64
64
|
import AutoTable from './LazyAutoTable.svelte';
|
|
65
|
+
import ResourceOverview from './ResourceOverview.svelte';
|
|
66
|
+
import DataState from './content/DataState.svelte';
|
|
67
|
+
import { parseContractRouteId } from '@svadmin/core/schema';
|
|
68
|
+
import { resolveAdminThemeConfig } from '../default-theme.js';
|
|
65
69
|
import AutoForm from './LazyAutoForm.svelte';
|
|
66
70
|
import ShowPage from './LazyShowPage.svelte';
|
|
67
71
|
import Toast from './Toast.svelte';
|
|
@@ -270,11 +274,13 @@
|
|
|
270
274
|
});
|
|
271
275
|
|
|
272
276
|
let themeOwner = $state.raw<ReturnType<typeof registerThemeOwner>>(undefined);
|
|
277
|
+
const resolvedThemeConfig = $derived(userThemeConfig === undefined ? undefined : resolveAdminThemeConfig(userThemeConfig));
|
|
273
278
|
|
|
274
279
|
onMount(() => {
|
|
275
280
|
themeOwner = registerThemeOwner(definedOptions({
|
|
276
281
|
defaultTheme,
|
|
277
|
-
themeConfig:
|
|
282
|
+
themeConfig: resolvedThemeConfig,
|
|
283
|
+
fallbackThemeConfig: resolveAdminThemeConfig(),
|
|
278
284
|
}));
|
|
279
285
|
|
|
280
286
|
return () => {
|
|
@@ -287,7 +293,8 @@
|
|
|
287
293
|
const owner = themeOwner;
|
|
288
294
|
const ownerOptions = definedOptions({
|
|
289
295
|
defaultTheme,
|
|
290
|
-
themeConfig:
|
|
296
|
+
themeConfig: resolvedThemeConfig,
|
|
297
|
+
fallbackThemeConfig: resolveAdminThemeConfig(),
|
|
291
298
|
});
|
|
292
299
|
untrack(() => updateThemeOwner(owner, ownerOptions));
|
|
293
300
|
});
|
|
@@ -364,6 +371,15 @@
|
|
|
364
371
|
const renderedRoute = $derived(route);
|
|
365
372
|
const renderedParams = $derived(params);
|
|
366
373
|
const renderedResourceName = $derived(renderedParams['resource']);
|
|
374
|
+
const renderedRecord = $derived.by(() => {
|
|
375
|
+
const id = renderedParams['id'];
|
|
376
|
+
const contract = resources.find(resource => resource.name === renderedResourceName)?.contract;
|
|
377
|
+
try {
|
|
378
|
+
return { id: id === undefined || !contract ? id : parseContractRouteId(contract, id), invalid: false };
|
|
379
|
+
} catch {
|
|
380
|
+
return { id: undefined, invalid: true };
|
|
381
|
+
}
|
|
382
|
+
});
|
|
367
383
|
const renderedHasRouteResource = $derived(renderedResourceName !== undefined && resourceNames.has(renderedResourceName));
|
|
368
384
|
const renderedResourcePages = $derived(renderedParams['resource'] ? resourcePages?.[renderedParams['resource']] : undefined);
|
|
369
385
|
const documentTitle = $derived.by(() => {
|
|
@@ -616,10 +632,7 @@
|
|
|
616
632
|
{#if dashboard}
|
|
617
633
|
{@render dashboard()}
|
|
618
634
|
{:else}
|
|
619
|
-
<
|
|
620
|
-
<h1 class="svadmin-u-d5c9b0001e7e svadmin-u-e83a7042bc91 svadmin-u-d4108abe6359">{translation.t('common.welcome', { title })}</h1>
|
|
621
|
-
<p class="svadmin-u-bfa603190748">{translation.t('common.dashboardHint')}</p>
|
|
622
|
-
</div>
|
|
635
|
+
<ResourceOverview />
|
|
623
636
|
{/if}
|
|
624
637
|
{:else if (renderedRoute === '/:resource' || renderedRoute === '/:parent/:parentId/:resource') && renderedHasRouteResource && renderedResourceName !== undefined}
|
|
625
638
|
{#key renderedParams['resource']}
|
|
@@ -635,25 +648,27 @@
|
|
|
635
648
|
<Comp resourceName={renderedResourceName} mode="create" />
|
|
636
649
|
</ResourceAccessGuard>
|
|
637
650
|
{/key}
|
|
638
|
-
{:else if
|
|
651
|
+
{:else if renderedHasRouteResource && renderedRecord.invalid}
|
|
652
|
+
<DataState state="error" title={translation.t('common.operationFailed')} />
|
|
653
|
+
{:else if (renderedRoute === '/:resource/edit/:id' || renderedRoute === '/:resource/:id/edit' || renderedRoute === '/:parent/:parentId/:resource/edit/:id') && renderedHasRouteResource && renderedResourceName !== undefined && renderedRecord.id !== undefined}
|
|
639
654
|
{#key `${renderedParams['resource']}-${renderedParams['id']}`}
|
|
640
655
|
{@const Comp = renderedResourcePages?.edit ?? mergedComponents.AutoForm}
|
|
641
|
-
<ResourceAccessGuard resourceName={renderedResourceName} action="edit" {
|
|
642
|
-
<Comp resourceName={renderedResourceName} mode="edit" {
|
|
656
|
+
<ResourceAccessGuard resourceName={renderedResourceName} action="edit" id={renderedRecord.id}>
|
|
657
|
+
<Comp resourceName={renderedResourceName} mode="edit" id={renderedRecord.id} />
|
|
643
658
|
</ResourceAccessGuard>
|
|
644
659
|
{/key}
|
|
645
|
-
{:else if (renderedRoute === '/:resource/show/:id' || renderedRoute === '/:resource/:id' || renderedRoute === '/:parent/:parentId/:resource/show/:id') && renderedHasRouteResource && renderedResourceName !== undefined &&
|
|
660
|
+
{:else if (renderedRoute === '/:resource/show/:id' || renderedRoute === '/:resource/:id' || renderedRoute === '/:parent/:parentId/:resource/show/:id') && renderedHasRouteResource && renderedResourceName !== undefined && renderedRecord.id !== undefined}
|
|
646
661
|
{#key `${renderedParams['resource']}-${renderedParams['id']}`}
|
|
647
662
|
{@const Comp = renderedResourcePages?.show ?? mergedComponents.ShowPage}
|
|
648
|
-
<ResourceAccessGuard resourceName={renderedResourceName} action="show" {
|
|
649
|
-
<Comp resourceName={renderedResourceName} id={
|
|
663
|
+
<ResourceAccessGuard resourceName={renderedResourceName} action="show" id={renderedRecord.id}>
|
|
664
|
+
<Comp resourceName={renderedResourceName} id={renderedRecord.id} />
|
|
650
665
|
</ResourceAccessGuard>
|
|
651
666
|
{/key}
|
|
652
|
-
{:else if (renderedRoute === '/:resource/clone/:id' || renderedRoute === '/:parent/:parentId/:resource/clone/:id') && renderedHasRouteResource && renderedResourceName !== undefined &&
|
|
667
|
+
{:else if (renderedRoute === '/:resource/clone/:id' || renderedRoute === '/:parent/:parentId/:resource/clone/:id') && renderedHasRouteResource && renderedResourceName !== undefined && renderedRecord.id !== undefined}
|
|
653
668
|
{#key `${renderedParams['resource']}-clone-${renderedParams['id']}`}
|
|
654
669
|
{@const Comp = renderedResourcePages?.clone ?? mergedComponents.AutoForm}
|
|
655
|
-
<ResourceAccessGuard resourceName={renderedResourceName} action="create" {
|
|
656
|
-
<Comp resourceName={renderedResourceName} mode="clone" {
|
|
670
|
+
<ResourceAccessGuard resourceName={renderedResourceName} action="create" id={renderedRecord.id} requireSourceRead>
|
|
671
|
+
<Comp resourceName={renderedResourceName} mode="clone" id={renderedRecord.id} />
|
|
657
672
|
</ResourceAccessGuard>
|
|
658
673
|
{/key}
|
|
659
674
|
{:else}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import WorkspaceLayout from './content/WorkspaceLayout.svelte';
|
|
17
17
|
|
|
18
18
|
const i18n = useTranslation();
|
|
19
|
+
const controlId = $props.id();
|
|
19
20
|
const adminContext = captureAdminContext();
|
|
20
21
|
const credentialProvider = $derived(adminContext.credentialProvider);
|
|
21
22
|
const isZh = $derived(i18n.locale === 'zh-CN');
|
|
@@ -221,14 +222,50 @@
|
|
|
221
222
|
{/if}
|
|
222
223
|
</SettingsGroup>
|
|
223
224
|
<SettingsGroup title={i18n.t('api.webhooks')} description={i18n.t('api.webhooksDesc')} bodyClass="space-y-5">
|
|
224
|
-
<form class="svadmin-u-f3c543ad5fe9 svadmin-u-7e0b7cdf1a94 svadmin-u-0c3bc98565dd svadmin-u-e4d6f343b9ff" onsubmit={handleAddWebhook}
|
|
225
|
+
<form class="svadmin-u-f3c543ad5fe9 svadmin-u-7e0b7cdf1a94 svadmin-u-0c3bc98565dd svadmin-u-e4d6f343b9ff" onsubmit={handleAddWebhook}>
|
|
226
|
+
<div class="svadmin-u-6f7e013d6499">
|
|
227
|
+
<Label for={`${controlId}-webhook-name`}>{i18n.t('api.webhookName')}</Label>
|
|
228
|
+
<Input id={`${controlId}-webhook-name`} bind:value={newWebhookName} placeholder="Order Events" disabled={!credentialProvider || submitting} />
|
|
229
|
+
</div>
|
|
230
|
+
<div class="svadmin-u-6f7e013d6499 svadmin-u-eea04c60c0ca">
|
|
231
|
+
<Label for={`${controlId}-webhook-url`}>{i18n.t('api.webhookUrl')}</Label>
|
|
232
|
+
<Input id={`${controlId}-webhook-url`} type="url" bind:value={newWebhookUrl} placeholder="https://api.example.com/hooks" disabled={!credentialProvider || submitting} />
|
|
233
|
+
</div>
|
|
234
|
+
<div class="svadmin-u-6f7e013d6499">
|
|
235
|
+
<Label for={`${controlId}-webhook-event`}>{i18n.t('api.eventType')}</Label>
|
|
236
|
+
<select id={`${controlId}-webhook-event`} bind:value={newWebhookEvent} disabled={!credentialProvider || submitting} class="svadmin-u-60fbb7713999 svadmin-u-e7a768f922d2 svadmin-u-6da6a3c3f741 svadmin-u-421ac2be5045 svadmin-u-ca6bcd4b6f3f svadmin-u-e5795dad4d22 svadmin-u-e6f9e383a762 svadmin-u-0e17f2bd9074 svadmin-u-03b4dd7f172b svadmin-u-fc7473ca09eb svadmin-u-582e6ef4b245 svadmin-u-55d048ebfb1c svadmin-u-608dd26cd5ba svadmin-u-80b9d0ae125f svadmin-u-6b22a22a9752 svadmin-u-b29d8adbad2e">
|
|
237
|
+
<option value="resource.created">resource.created</option>
|
|
238
|
+
<option value="resource.updated">resource.updated</option>
|
|
239
|
+
<option value="resource.deleted">resource.deleted</option>
|
|
240
|
+
<option value="auth.login">auth.login</option>
|
|
241
|
+
</select>
|
|
242
|
+
</div>
|
|
243
|
+
<div class="svadmin-u-60fbb7713999 svadmin-u-6f27f4f79e55">
|
|
244
|
+
<Button type="submit" class="svadmin-u-6da6a3c3f741" disabled={!credentialProvider || submitting || !newWebhookName.trim() || !newWebhookUrl.trim()}><Plus class="svadmin-u-f7b5fa971871" />{i18n.t('api.addWebhook')}</Button>
|
|
245
|
+
</div>
|
|
246
|
+
</form>
|
|
225
247
|
{#if webhooks.length === 0}<DataState state="empty" title={i18n.t('api.noWebhooks')} description={credentialProvider ? i18n.t('api.webhooksDesc') : (isZh ? '配置 CredentialProvider 后可管理真实 Webhook。' : 'Configure CredentialProvider to manage real webhooks.')} />{:else}<div class="svadmin-u-fa6acbf81d74 svadmin-u-e783642739e3 svadmin-u-b950dda299d3 svadmin-u-18049387f0af">{#each webhooks as hook (hook.id)}<div class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-1004c0c3954c svadmin-u-cb11fec3bb46 svadmin-u-020ba687fa12 svadmin-u-9f76a62f4f44 svadmin-u-3b9871a0bf93"><div class="svadmin-u-7e0b7cdf1a94"><p class="svadmin-u-fc7473ca09eb svadmin-u-2689f3958069 svadmin-u-d4108abe6359">{hook.name}</p><p class="svadmin-u-f283ea9bea0e svadmin-u-0e65706bcccd svadmin-u-359090c2d529 svadmin-u-bfa603190748">{hook.url}</p></div><div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-8ef2268efbbc svadmin-u-1004c0c3954c svadmin-u-1f51d781e606"><span class="svadmin-u-07389a777c1f svadmin-u-2ef11f1cb219 svadmin-u-d5eab218aa34 svadmin-u-660d2effb880 svadmin-u-0e65706bcccd svadmin-u-359090c2d529 svadmin-u-bfa603190748">{hook.eventType}</span><Button variant="ghost" size="icon-sm" onclick={() => deleteWebhook(hook.id)} aria-label={i18n.t('common.delete') + ' ' + hook.name}><Trash2 class="svadmin-u-f7b5fa971871" /></Button></div></div>{/each}</div>{/if}
|
|
226
248
|
</SettingsGroup>
|
|
227
249
|
</div>
|
|
228
250
|
{/snippet}
|
|
229
251
|
{#snippet secondary()}
|
|
230
252
|
<SettingsGroup title={i18n.t('api.generateTitle')} description={i18n.t('api.generateDesc')}>
|
|
231
|
-
<form onsubmit={handleGenerateKey} class="svadmin-u-b43b4c086d9a"
|
|
253
|
+
<form onsubmit={handleGenerateKey} class="svadmin-u-b43b4c086d9a">
|
|
254
|
+
<div class="svadmin-u-6f7e013d6499">
|
|
255
|
+
<Label for={`${controlId}-api-key-name`}>{i18n.t('api.keyName')}</Label>
|
|
256
|
+
<Input id={`${controlId}-api-key-name`} placeholder="CI deployment token" bind:value={newKeyName} required disabled={!credentialProvider || submitting} />
|
|
257
|
+
</div>
|
|
258
|
+
<fieldset class="svadmin-u-6ed543e2fbbb" disabled={!credentialProvider || submitting}>
|
|
259
|
+
<legend class="svadmin-u-fc7473ca09eb svadmin-u-2689f3958069 svadmin-u-d4108abe6359">{i18n.t('api.permissions')}</legend>
|
|
260
|
+
{#each Object.keys(newKeyPermissions) as permission (permission)}
|
|
261
|
+
<div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-77a2a20e90d4">
|
|
262
|
+
<Checkbox id={`${controlId}-perm-${permission}`} bind:checked={newKeyPermissions[permission as keyof typeof newKeyPermissions]} />
|
|
263
|
+
<Label for={`${controlId}-perm-${permission}`} class="svadmin-u-34516836730d svadmin-u-fc7473ca09eb svadmin-u-8ecebc9f80e6">{permission}</Label>
|
|
264
|
+
</div>
|
|
265
|
+
{/each}
|
|
266
|
+
</fieldset>
|
|
267
|
+
<Button type="submit" class="svadmin-u-6da6a3c3f741" disabled={!credentialProvider || submitting || !newKeyName.trim()}>{i18n.t('api.generateButton')}</Button>
|
|
268
|
+
</form>
|
|
232
269
|
</SettingsGroup>
|
|
233
270
|
{/snippet}
|
|
234
271
|
</WorkspaceLayout>
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
import { Check, Monitor, Moon, Sun } from '@lucide/svelte';
|
|
5
5
|
import SettingsGroup from './content/SettingsGroup.svelte';
|
|
6
6
|
import SettingsFieldRow from './content/SettingsFieldRow.svelte';
|
|
7
|
+
import ContentPageHeader from './content/ContentPageHeader.svelte';
|
|
7
8
|
|
|
9
|
+
let { headingLevel = 'h1' }: { headingLevel?: 'h1' | 'h2' } = $props();
|
|
10
|
+
const controlId = $props.id();
|
|
8
11
|
const i18n = useTranslation();
|
|
9
12
|
const themeOptions = [
|
|
10
13
|
{ value: 'light' as const, icon: Sun, label: 'settings.light' },
|
|
@@ -17,18 +20,20 @@
|
|
|
17
20
|
const availableLocales = i18n.getAvailableLocales();
|
|
18
21
|
const presets = getColorPresets();
|
|
19
22
|
const DENSITY_KEY = 'svadmin-sidebar-density';
|
|
20
|
-
|
|
23
|
+
const storedDensity = typeof window !== 'undefined' ? localStorage.getItem(DENSITY_KEY) : null;
|
|
24
|
+
let density = $state<'compact' | 'standard'>(storedDensity === 'compact' ? 'compact' : 'standard');
|
|
21
25
|
const PAGE_SIZE_KEY = 'svadmin-default-page-size';
|
|
22
26
|
const pageSizeOptions = [10, 20, 50];
|
|
23
|
-
|
|
27
|
+
const storedPageSize = typeof window !== 'undefined' ? Number(localStorage.getItem(PAGE_SIZE_KEY) ?? '10') : 10;
|
|
28
|
+
let pageSize = $state(pageSizeOptions.includes(storedPageSize) ? storedPageSize : 10);
|
|
24
29
|
const localeNames: Record<string, string> = { 'zh-CN': '中文(简体)', en: 'English', ja: '日本語', ko: '한국어' };
|
|
25
30
|
function setDensity(value: 'compact' | 'standard') { density = value; if (typeof window !== 'undefined') { localStorage.setItem(DENSITY_KEY, value); window.dispatchEvent(new CustomEvent('svadmin-density-change', { detail: value })); } }
|
|
26
31
|
function setPageSize(size: number) { pageSize = size; if (typeof window !== 'undefined') localStorage.setItem(PAGE_SIZE_KEY, String(size)); }
|
|
27
32
|
</script>
|
|
28
33
|
|
|
29
34
|
<div class="svadmin-u-b3542e058833">
|
|
30
|
-
<
|
|
31
|
-
<SettingsGroup title={i18n.t('settings.themeMode')}>
|
|
35
|
+
<ContentPageHeader title={i18n.t('settings.appearance')} description={i18n.t('settings.settingsDescription')} {headingLevel} />
|
|
36
|
+
<SettingsGroup title={i18n.t('settings.themeMode')} headingLevel={headingLevel === 'h1' ? 'h2' : 'h3'}>
|
|
32
37
|
<div class="svadmin-u-f3c543ad5fe9 svadmin-u-be2e831be5dd svadmin-u-1004c0c3954c">
|
|
33
38
|
{#each themeOptions as option (option.value)}
|
|
34
39
|
{@const active = currentTheme === option.value}
|
|
@@ -40,7 +45,7 @@
|
|
|
40
45
|
{/each}
|
|
41
46
|
</div>
|
|
42
47
|
</SettingsGroup>
|
|
43
|
-
<SettingsGroup title={i18n.t('settings.colorAccent')}>
|
|
48
|
+
<SettingsGroup title={i18n.t('settings.colorAccent')} headingLevel={headingLevel === 'h1' ? 'h2' : 'h3'}>
|
|
44
49
|
<div class="svadmin-u-60fbb7713999 svadmin-u-1eb5c6df38c1 svadmin-u-1004c0c3954c">
|
|
45
50
|
{#each presets as preset (preset.name)}
|
|
46
51
|
{@const active = currentColor === preset.name}
|
|
@@ -48,11 +53,32 @@
|
|
|
48
53
|
{/each}
|
|
49
54
|
</div>
|
|
50
55
|
</SettingsGroup>
|
|
51
|
-
<SettingsGroup title={i18n.t('settings.interface')}>
|
|
56
|
+
<SettingsGroup title={i18n.t('settings.interface')} headingLevel={headingLevel === 'h1' ? 'h2' : 'h3'}>
|
|
52
57
|
<div class="svadmin-u-fa6acbf81d74 svadmin-u-e783642739e3">
|
|
53
|
-
<SettingsFieldRow label={i18n.t('settings.language')} description={localeNames[currentLocale] ?? currentLocale}
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
<SettingsFieldRow label={i18n.t('settings.language')} description={localeNames[currentLocale] ?? currentLocale} controlId={`${controlId}-language`}>
|
|
59
|
+
{#snippet control()}
|
|
60
|
+
<select id={`${controlId}-language`} class="svadmin-u-e7a768f922d2 svadmin-u-3713c4c7843d svadmin-u-421ac2be5045 svadmin-u-ca6bcd4b6f3f svadmin-u-e5795dad4d22 svadmin-u-e6f9e383a762 svadmin-u-0e17f2bd9074 svadmin-u-fc7473ca09eb svadmin-u-582e6ef4b245 svadmin-u-55d048ebfb1c svadmin-u-608dd26cd5ba svadmin-u-80b9d0ae125f svadmin-u-6b22a22a9752" value={currentLocale} onchange={(event) => i18n.setLocale((event.target as HTMLSelectElement).value)}>
|
|
61
|
+
{#each availableLocales as locale (locale)}<option value={locale}>{localeNames[locale] ?? locale}</option>{/each}
|
|
62
|
+
</select>
|
|
63
|
+
{/snippet}
|
|
64
|
+
</SettingsFieldRow>
|
|
65
|
+
<SettingsFieldRow label={i18n.t('settings.sidebarDensity')}>
|
|
66
|
+
{#snippet control()}
|
|
67
|
+
<div class="svadmin-u-60fbb7713999 svadmin-u-2cd02d11d1af svadmin-u-421ac2be5045 svadmin-u-ca6bcd4b6f3f svadmin-u-e5795dad4d22">
|
|
68
|
+
<button type="button" aria-pressed={density === 'compact'} class="svadmin-u-0e17f2bd9074 svadmin-u-ec0091ee009b svadmin-u-359090c2d529 svadmin-u-2689f3958069 {density === 'compact' ? 'svadmin-u-75b1bec3ea0e svadmin-u-30ca335ae9c2' : 'svadmin-u-e6f9e383a762 svadmin-u-bfa603190748 svadmin-u-0557b88819cd'}" onclick={() => setDensity('compact')}>{i18n.t('settings.compact')}</button>
|
|
69
|
+
<button type="button" aria-pressed={density === 'standard'} class="svadmin-u-0e17f2bd9074 svadmin-u-ec0091ee009b svadmin-u-359090c2d529 svadmin-u-2689f3958069 {density === 'standard' ? 'svadmin-u-75b1bec3ea0e svadmin-u-30ca335ae9c2' : 'svadmin-u-e6f9e383a762 svadmin-u-bfa603190748 svadmin-u-0557b88819cd'}" onclick={() => setDensity('standard')}>{i18n.t('settings.standard')}</button>
|
|
70
|
+
</div>
|
|
71
|
+
{/snippet}
|
|
72
|
+
</SettingsFieldRow>
|
|
73
|
+
<SettingsFieldRow label={i18n.t('settings.defaultPageSize')}>
|
|
74
|
+
{#snippet control()}
|
|
75
|
+
<div class="svadmin-u-60fbb7713999 svadmin-u-2cd02d11d1af svadmin-u-421ac2be5045 svadmin-u-ca6bcd4b6f3f svadmin-u-e5795dad4d22">
|
|
76
|
+
{#each pageSizeOptions as size (size)}
|
|
77
|
+
<button type="button" aria-pressed={pageSize === size} class="svadmin-u-0e17f2bd9074 svadmin-u-ec0091ee009b svadmin-u-0e65706bcccd svadmin-u-359090c2d529 svadmin-u-2689f3958069 {pageSize === size ? 'svadmin-u-75b1bec3ea0e svadmin-u-30ca335ae9c2' : 'svadmin-u-e6f9e383a762 svadmin-u-bfa603190748 svadmin-u-0557b88819cd'}" onclick={() => setPageSize(size)}>{size}</button>
|
|
78
|
+
{/each}
|
|
79
|
+
</div>
|
|
80
|
+
{/snippet}
|
|
81
|
+
</SettingsFieldRow>
|
|
56
82
|
</div>
|
|
57
83
|
</SettingsGroup>
|
|
58
84
|
</div>
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
headingLevel?: 'h1' | 'h2';
|
|
3
|
+
};
|
|
4
|
+
declare const AppearanceSettings: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
2
5
|
type AppearanceSettings = ReturnType<typeof AppearanceSettings>;
|
|
3
6
|
export default AppearanceSettings;
|
|
@@ -123,6 +123,8 @@
|
|
|
123
123
|
// ─── Props with Snippet composability ─────────────────────────
|
|
124
124
|
interface Props {
|
|
125
125
|
resourceName: string;
|
|
126
|
+
/** 嵌入仪表盘的多个表格应关闭 URL 同步,避免互相覆盖筛选与分页。 */
|
|
127
|
+
syncWithLocation?: boolean;
|
|
126
128
|
rendering?: ResourceRendering | undefined;
|
|
127
129
|
/** 可选主体渲染器:不替换资源、权限、工具栏和持久化逻辑。 */
|
|
128
130
|
gridBody?: Snippet<[AutoTableGridState]>;
|
|
@@ -170,6 +172,7 @@
|
|
|
170
172
|
|
|
171
173
|
let {
|
|
172
174
|
resourceName,
|
|
175
|
+
syncWithLocation = true,
|
|
173
176
|
rendering,
|
|
174
177
|
gridBody,
|
|
175
178
|
selectable = true,
|
|
@@ -254,7 +257,7 @@
|
|
|
254
257
|
}
|
|
255
258
|
|
|
256
259
|
// ─── URL state + server-side state ────────────────────────────
|
|
257
|
-
const urlState = readURLState(adminContext);
|
|
260
|
+
const urlState: ReturnType<typeof readURLState> = untrack(() => syncWithLocation ? readURLState(adminContext) : {});
|
|
258
261
|
|
|
259
262
|
const savedViewColumnIds = $derived(new Set([
|
|
260
263
|
...resource.fields.map((field) => field.key),
|
|
@@ -504,9 +507,8 @@
|
|
|
504
507
|
}
|
|
505
508
|
return result;
|
|
506
509
|
});
|
|
507
|
-
|
|
508
|
-
// URL sync
|
|
509
510
|
$effect(() => {
|
|
511
|
+
if (!syncWithLocation) return;
|
|
510
512
|
writeURLState({
|
|
511
513
|
page: pagination.current,
|
|
512
514
|
pageSize: pagination.pageSize,
|
|
@@ -1448,7 +1450,9 @@
|
|
|
1448
1450
|
&& left.canDelete === right.canDelete
|
|
1449
1451
|
&& left.variables === right.variables;
|
|
1450
1452
|
}
|
|
1453
|
+
let localDetailId = $state<string | number>();
|
|
1451
1454
|
const detailState = $derived.by(() => {
|
|
1455
|
+
if (!syncWithLocation) return { id: canRead ? localDetailId : undefined, invalid: false };
|
|
1452
1456
|
const route = parsed.params['detail'];
|
|
1453
1457
|
if (route === undefined || !canRead) return { id: undefined, invalid: false };
|
|
1454
1458
|
try { return { id: parseContractRouteId(binding.resource, route), invalid: false }; }
|
|
@@ -1470,6 +1474,7 @@
|
|
|
1470
1474
|
rowSelection = {};
|
|
1471
1475
|
expandedAtom.set({});
|
|
1472
1476
|
detailOpenedInHistory = false;
|
|
1477
|
+
localDetailId = undefined;
|
|
1473
1478
|
quickEditId = undefined;
|
|
1474
1479
|
if (searchDebounceTimer) clearTimeout(searchDebounceTimer);
|
|
1475
1480
|
searchDebounceTimer = undefined;
|
|
@@ -1563,14 +1568,14 @@
|
|
|
1563
1568
|
await tick();
|
|
1564
1569
|
}
|
|
1565
1570
|
}
|
|
1566
|
-
|
|
1567
1571
|
function openDetail(id: string | number): void {
|
|
1568
1572
|
if (!canRead || !canShow) return;
|
|
1573
|
+
if (!syncWithLocation) { localDetailId = id; return; }
|
|
1569
1574
|
detailOpenedInHistory = true;
|
|
1570
1575
|
writeURLState({ detailId: formatContractRouteId(binding.resource, id) }, adminContext, 'push');
|
|
1571
1576
|
}
|
|
1572
|
-
|
|
1573
1577
|
function closeDetail(): void {
|
|
1578
|
+
if (!syncWithLocation) { localDetailId = undefined; return; }
|
|
1574
1579
|
if (detailOpenedInHistory && adminContext.routerProvider) {
|
|
1575
1580
|
detailOpenedInHistory = false;
|
|
1576
1581
|
adminContext.routerProvider.back();
|
|
@@ -1579,7 +1584,6 @@
|
|
|
1579
1584
|
detailOpenedInHistory = false;
|
|
1580
1585
|
writeURLState({ detailId: undefined }, adminContext);
|
|
1581
1586
|
}
|
|
1582
|
-
|
|
1583
1587
|
// ─── CSV Export ───────────────────────────────────────────────
|
|
1584
1588
|
function exportCSV() {
|
|
1585
1589
|
if (!canRead || !canExport || !pageRecords.ok || query.isError || query.isFetching || !pageRecords.data.length) return;
|
|
@@ -7,6 +7,8 @@ import { type SavedListViewProvider } from './saved-list-views.js';
|
|
|
7
7
|
import type { Snippet } from 'svelte';
|
|
8
8
|
interface Props {
|
|
9
9
|
resourceName: string;
|
|
10
|
+
/** 嵌入仪表盘的多个表格应关闭 URL 同步,避免互相覆盖筛选与分页。 */
|
|
11
|
+
syncWithLocation?: boolean;
|
|
10
12
|
rendering?: ResourceRendering | undefined;
|
|
11
13
|
/** 可选主体渲染器:不替换资源、权限、工具栏和持久化逻辑。 */
|
|
12
14
|
gridBody?: Snippet<[AutoTableGridState]>;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import SettingsGroup from './content/SettingsGroup.svelte';
|
|
7
7
|
import SettingsFieldRow from './content/SettingsFieldRow.svelte';
|
|
8
8
|
import StatusBadge from './content/StatusBadge.svelte';
|
|
9
|
+
import ContentPageHeader from './content/ContentPageHeader.svelte';
|
|
9
10
|
|
|
10
11
|
const i18n = useTranslation();
|
|
11
12
|
|
|
@@ -18,11 +19,12 @@
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
interface Props {
|
|
22
|
+
headingLevel?: 'h1' | 'h2';
|
|
21
23
|
integrations?: Integration[];
|
|
22
24
|
onConnectionChange?: (id: string, connected: boolean) => void | Promise<void>;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
let { integrations, onConnectionChange }: Props = $props();
|
|
27
|
+
let { integrations, onConnectionChange, headingLevel = 'h1' }: Props = $props();
|
|
26
28
|
const defaultIntegrations = $derived<Integration[]>([
|
|
27
29
|
{ id: 'source-control', name: i18n.t('integrations.sourceControl'), description: i18n.t('integrations.sourceControlDescription'), Icon: FolderGit },
|
|
28
30
|
{ id: 'chatops', name: 'ChatOps', description: i18n.t('integrations.chatOpsDescription'), Icon: MessageCircle },
|
|
@@ -33,8 +35,8 @@
|
|
|
33
35
|
</script>
|
|
34
36
|
|
|
35
37
|
<div class="svadmin-u-b3542e058833">
|
|
36
|
-
<
|
|
37
|
-
<SettingsGroup title={i18n.t('settings.integrations')} description={i18n.t('integrations.addMoreHint')}>
|
|
38
|
+
<ContentPageHeader title={i18n.t('settings.integrations')} description={i18n.t('settings.integrationsDescription')} {headingLevel} />
|
|
39
|
+
<SettingsGroup title={i18n.t('settings.integrations')} description={i18n.t('integrations.addMoreHint')} headingLevel={headingLevel === 'h1' ? 'h2' : 'h3'}>
|
|
38
40
|
<div class="svadmin-u-fa6acbf81d74 svadmin-u-e783642739e3">
|
|
39
41
|
{#each displayedIntegrations as integration (integration.id)}
|
|
40
42
|
<SettingsFieldRow label={integration.name} description={integration.description}>
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
import { Menu } from '@lucide/svelte';
|
|
28
28
|
import { getComponentRegistry } from '../component-registry.svelte.js';
|
|
29
29
|
import { Button } from './ui/button/index.js';
|
|
30
|
+
import { cn } from '../utils.js';
|
|
30
31
|
|
|
31
32
|
const i18n = useTranslation();
|
|
32
33
|
|
|
@@ -34,7 +35,25 @@
|
|
|
34
35
|
let shortcutsOpen = $state(false);
|
|
35
36
|
let mobileMenuOpen = $state(false);
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
interface Props {
|
|
39
|
+
children: Snippet;
|
|
40
|
+
title?: string;
|
|
41
|
+
menu?: MenuItem[];
|
|
42
|
+
siteUrl?: string;
|
|
43
|
+
routeMode?: 'hash' | 'path' | 'auto';
|
|
44
|
+
aiAssistant?: Snippet<[LayoutAIAssistantProps]>;
|
|
45
|
+
class?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let {
|
|
49
|
+
children,
|
|
50
|
+
title = 'Admin',
|
|
51
|
+
menu,
|
|
52
|
+
siteUrl,
|
|
53
|
+
routeMode = 'auto',
|
|
54
|
+
aiAssistant,
|
|
55
|
+
class: className = '',
|
|
56
|
+
}: Props = $props();
|
|
38
57
|
const layoutId = $props.id();
|
|
39
58
|
const layoutScope = `svadmin-layout-${layoutId}`;
|
|
40
59
|
const mainContentId = `${layoutScope}-main`;
|
|
@@ -150,7 +169,14 @@
|
|
|
150
169
|
ontouchend={handleTouchEnd}
|
|
151
170
|
/>
|
|
152
171
|
{#if loading}
|
|
153
|
-
<div
|
|
172
|
+
<div
|
|
173
|
+
data-svadmin-layout-scope={layoutScope}
|
|
174
|
+
data-svadmin-layout
|
|
175
|
+
data-svadmin-layout-state="loading"
|
|
176
|
+
aria-busy="true"
|
|
177
|
+
class={cn('svadmin-u-60fbb7713999 svadmin-u-ef114b5f5ad1', className)}
|
|
178
|
+
in:fade={{ duration: 150 }}
|
|
179
|
+
>
|
|
154
180
|
<div class="svadmin-u-99d72c7fc3e2 svadmin-u-9d60be3a6d80 svadmin-u-e2fe2012a697 svadmin-u-ac402894e356 svadmin-u-8e63407b5ceb svadmin-u-3e7ce58d64fa">
|
|
155
181
|
<Skeleton class="svadmin-u-ed8a5df7b2fb svadmin-u-516b03df0b7c" />
|
|
156
182
|
<div class="svadmin-u-6f7e013d6499 svadmin-u-31f2553311b6">
|
|
@@ -172,7 +198,14 @@
|
|
|
172
198
|
<DevTools docked />
|
|
173
199
|
</div>
|
|
174
200
|
{:else}
|
|
175
|
-
<div
|
|
201
|
+
<div
|
|
202
|
+
data-svadmin-layout-scope={layoutScope}
|
|
203
|
+
data-svadmin-layout
|
|
204
|
+
data-svadmin-layout-state="ready"
|
|
205
|
+
aria-busy="false"
|
|
206
|
+
class={cn('svadmin-u-60fbb7713999 svadmin-u-ef114b5f5ad1 svadmin-u-e6f9e383a762', className)}
|
|
207
|
+
in:fade={{ duration: 200, delay: 50 }}
|
|
208
|
+
>
|
|
176
209
|
<button
|
|
177
210
|
type="button"
|
|
178
211
|
data-svadmin-skip-link={mainContentId}
|
|
@@ -241,7 +274,7 @@
|
|
|
241
274
|
|
|
242
275
|
<!-- Content area: responsive padding + centered max-width container
|
|
243
276
|
so wide screens don't stretch content indefinitely (avoids sparse layouts) -->
|
|
244
|
-
<main id={mainContentId} tabindex="-1" data-svadmin-main class="svadmin-u-36e579c0b41c svadmin-u-92bf82f493b1 svadmin-u-2859c861d7de svadmin-u-f0faeb26d656 svadmin-u-c9b99cd93450 svadmin-u-cc06a6575385 svadmin-u-daf5dc5fac2b svadmin-u-e86fadb84483">
|
|
277
|
+
<main id={mainContentId} tabindex="-1" data-svadmin-main data-svadmin-layout-content class="svadmin-u-36e579c0b41c svadmin-u-92bf82f493b1 svadmin-u-2859c861d7de svadmin-u-f0faeb26d656 svadmin-u-c9b99cd93450 svadmin-u-cc06a6575385 svadmin-u-daf5dc5fac2b svadmin-u-e86fadb84483">
|
|
245
278
|
<div class="svadmin-u-0e12dc7de920 svadmin-u-6da6a3c3f741 svadmin-u-9e3dc30c26a3">
|
|
246
279
|
{#key getPath()}
|
|
247
280
|
<!-- 路由切换立即释放旧页面,避免退出期间继续读取已失效的路由派生状态。 -->
|
|
@@ -252,7 +285,7 @@
|
|
|
252
285
|
</div>
|
|
253
286
|
</main>
|
|
254
287
|
|
|
255
|
-
<footer class="svadmin-u-60fbb7713999 svadmin-u-0cfe3fb5e434 svadmin-u-012fbd121f37 svadmin-u-3960ffc248d9 svadmin-u-77c08e015d14 svadmin-u-77a2a20e90d4 svadmin-u-b950dda299d3 svadmin-u-05faf5c801ff svadmin-u-e6f9e383a762 svadmin-u-f0faeb26d656 svadmin-u-472f43d3a27b">
|
|
288
|
+
<footer data-svadmin-layout-footer class="svadmin-u-60fbb7713999 svadmin-u-0cfe3fb5e434 svadmin-u-012fbd121f37 svadmin-u-3960ffc248d9 svadmin-u-77c08e015d14 svadmin-u-77a2a20e90d4 svadmin-u-b950dda299d3 svadmin-u-05faf5c801ff svadmin-u-e6f9e383a762 svadmin-u-f0faeb26d656 svadmin-u-472f43d3a27b">
|
|
256
289
|
<DevTools docked />
|
|
257
290
|
{@render aiAssistant?.({ docked: true, scope: chatScope, ownerScope: layoutScope })}
|
|
258
291
|
</footer>
|
|
@@ -5,14 +5,15 @@ export interface LayoutAIAssistantProps {
|
|
|
5
5
|
}
|
|
6
6
|
import type { Snippet } from 'svelte';
|
|
7
7
|
import type { MenuItem } from '@svadmin/core';
|
|
8
|
-
|
|
8
|
+
interface Props {
|
|
9
9
|
children: Snippet;
|
|
10
10
|
title?: string;
|
|
11
11
|
menu?: MenuItem[];
|
|
12
12
|
siteUrl?: string;
|
|
13
13
|
routeMode?: 'hash' | 'path' | 'auto';
|
|
14
14
|
aiAssistant?: Snippet<[LayoutAIAssistantProps]>;
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
declare const Layout: import("svelte").Component<Props, {}, "">;
|
|
17
18
|
type Layout = ReturnType<typeof Layout>;
|
|
18
19
|
export default Layout;
|
|
@@ -20,8 +20,10 @@
|
|
|
20
20
|
import SettingsFieldRow from './content/SettingsFieldRow.svelte';
|
|
21
21
|
import DataState from './content/DataState.svelte';
|
|
22
22
|
import FeedbackNotice from './content/FeedbackNotice.svelte';
|
|
23
|
+
import ContentPageHeader from './content/ContentPageHeader.svelte';
|
|
23
24
|
|
|
24
|
-
let { preferencesProvider }: { preferencesProvider?: NotificationPreferencesProvider } = $props();
|
|
25
|
+
let { preferencesProvider, headingLevel = 'h1' }: { preferencesProvider?: NotificationPreferencesProvider; headingLevel?: 'h1' | 'h2' } = $props();
|
|
26
|
+
const controlId = $props.id();
|
|
25
27
|
const i18n = useTranslation();
|
|
26
28
|
const context = captureAdminContext();
|
|
27
29
|
const notification = useNotification();
|
|
@@ -72,6 +74,11 @@
|
|
|
72
74
|
</script>
|
|
73
75
|
|
|
74
76
|
<div class="svadmin-u-b3542e058833">
|
|
77
|
+
<ContentPageHeader title={i18n.t('settings.notifications')} description={i18n.t('settings.notificationsDescription')} {headingLevel}>
|
|
78
|
+
{#snippet actions()}
|
|
79
|
+
<Button disabled={!loaded || loading || saving} onclick={saveSettings}>{i18n.t('common.save')}</Button>
|
|
80
|
+
{/snippet}
|
|
81
|
+
</ContentPageHeader>
|
|
75
82
|
{#if preferencesProvider?.description}
|
|
76
83
|
<p role="note">{preferencesProvider.description}</p>
|
|
77
84
|
{/if}
|
|
@@ -81,23 +88,22 @@
|
|
|
81
88
|
{#if loading}<DataState state="loading" />{/if}
|
|
82
89
|
{#if error}<p role="alert">{error}</p>{#if !loaded}<Button onclick={load}>{i18n.t('common.retry')}</Button>{/if}{/if}
|
|
83
90
|
<fieldset disabled={!loaded || loading || saving} class="svadmin-u-b3542e058833">
|
|
84
|
-
<
|
|
85
|
-
<SettingsGroup title={i18n.t('notifications.emailTitle')} description={i18n.t('notifications.emailDescription')}>
|
|
91
|
+
<SettingsGroup title={i18n.t('notifications.emailTitle')} description={i18n.t('notifications.emailDescription')} headingLevel={headingLevel === 'h1' ? 'h2' : 'h3'}>
|
|
86
92
|
<div class="svadmin-u-fa6acbf81d74 svadmin-u-e783642739e3">
|
|
87
|
-
<SettingsFieldRow label={i18n.t('notifications.securityAlerts')} description={i18n.t('notifications.securityAlertsDesc')}>{#snippet control()}<Switch id=
|
|
88
|
-
<SettingsFieldRow label={i18n.t('notifications.activityLogs')} description={i18n.t('notifications.activityLogsDesc')}>{#snippet control()}<Switch id=
|
|
89
|
-
<SettingsFieldRow label={i18n.t('notifications.systemAlerts')} description={i18n.t('notifications.systemAlertsDesc')}>{#snippet control()}<Switch id=
|
|
93
|
+
<SettingsFieldRow controlId={`${controlId}-email-security`} label={i18n.t('notifications.securityAlerts')} description={i18n.t('notifications.securityAlertsDesc')}>{#snippet control()}<Switch id={`${controlId}-email-security`} bind:checked={emailAlerts.security} />{/snippet}</SettingsFieldRow>
|
|
94
|
+
<SettingsFieldRow controlId={`${controlId}-email-activity`} label={i18n.t('notifications.activityLogs')} description={i18n.t('notifications.activityLogsDesc')}>{#snippet control()}<Switch id={`${controlId}-email-activity`} bind:checked={emailAlerts.activity} />{/snippet}</SettingsFieldRow>
|
|
95
|
+
<SettingsFieldRow controlId={`${controlId}-email-reports`} label={i18n.t('notifications.systemAlerts')} description={i18n.t('notifications.systemAlertsDesc')}>{#snippet control()}<Switch id={`${controlId}-email-reports`} bind:checked={emailAlerts.reports} />{/snippet}</SettingsFieldRow>
|
|
90
96
|
</div>
|
|
91
97
|
</SettingsGroup>
|
|
92
|
-
<SettingsGroup title={i18n.t('notifications.pushTitle')} description={i18n.t('notifications.pushDescription')}>
|
|
98
|
+
<SettingsGroup title={i18n.t('notifications.pushTitle')} description={i18n.t('notifications.pushDescription')} headingLevel={headingLevel === 'h1' ? 'h2' : 'h3'}>
|
|
93
99
|
<div class="svadmin-u-fa6acbf81d74 svadmin-u-e783642739e3">
|
|
94
|
-
<SettingsFieldRow label={i18n.t('notifications.securityAlerts')} description={i18n.t('notifications.securityPushDesc')}>{#snippet control()}<Switch id=
|
|
95
|
-
<SettingsFieldRow label={i18n.t('notifications.activityLogs')} description={i18n.t('notifications.activityPushDesc')}>{#snippet control()}<Switch id=
|
|
96
|
-
<SettingsFieldRow label={i18n.t('notifications.systemAlerts')} description={i18n.t('notifications.systemPushDesc')}>{#snippet control()}<Switch id=
|
|
100
|
+
<SettingsFieldRow controlId={`${controlId}-push-security`} label={i18n.t('notifications.securityAlerts')} description={i18n.t('notifications.securityPushDesc')}>{#snippet control()}<Switch id={`${controlId}-push-security`} bind:checked={pushAlerts.security} />{/snippet}</SettingsFieldRow>
|
|
101
|
+
<SettingsFieldRow controlId={`${controlId}-push-activity`} label={i18n.t('notifications.activityLogs')} description={i18n.t('notifications.activityPushDesc')}>{#snippet control()}<Switch id={`${controlId}-push-activity`} bind:checked={pushAlerts.activity} />{/snippet}</SettingsFieldRow>
|
|
102
|
+
<SettingsFieldRow controlId={`${controlId}-push-reports`} label={i18n.t('notifications.systemAlerts')} description={i18n.t('notifications.systemPushDesc')}>{#snippet control()}<Switch id={`${controlId}-push-reports`} bind:checked={pushAlerts.reports} />{/snippet}</SettingsFieldRow>
|
|
97
103
|
</div>
|
|
98
104
|
</SettingsGroup>
|
|
99
|
-
<SettingsGroup title={i18n.t('notifications.smsTitle')} description={i18n.t('notifications.smsDescription')}>
|
|
100
|
-
<SettingsFieldRow label={i18n.t('notifications.criticalSecurity')} description={i18n.t('notifications.criticalSecurityDesc')}>{#snippet control()}<Switch id=
|
|
105
|
+
<SettingsGroup title={i18n.t('notifications.smsTitle')} description={i18n.t('notifications.smsDescription')} headingLevel={headingLevel === 'h1' ? 'h2' : 'h3'}>
|
|
106
|
+
<SettingsFieldRow controlId={`${controlId}-sms-security`} label={i18n.t('notifications.criticalSecurity')} description={i18n.t('notifications.criticalSecurityDesc')}>{#snippet control()}<Switch id={`${controlId}-sms-security`} bind:checked={smsAlerts.security} />{/snippet}</SettingsFieldRow>
|
|
101
107
|
</SettingsGroup>
|
|
102
108
|
</fieldset>
|
|
103
109
|
</div>
|