@svadmin/ui 0.42.0 → 0.42.2
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/components/AdminApp.svelte +154 -54
- package/package.json +7 -7
|
@@ -200,8 +200,7 @@
|
|
|
200
200
|
const routerState = provideRouterState(untrack(() => resolvedRouter));
|
|
201
201
|
const scopedRouterProvider: RouterProvider = {
|
|
202
202
|
go(options) {
|
|
203
|
-
|
|
204
|
-
routerState.sync();
|
|
203
|
+
return navigateAfterAuthCheck(options);
|
|
205
204
|
},
|
|
206
205
|
back() {
|
|
207
206
|
resolvedRouter.back();
|
|
@@ -303,9 +302,8 @@
|
|
|
303
302
|
// Reactive getters for route state
|
|
304
303
|
const route = $derived(routerState.route);
|
|
305
304
|
const params = $derived(routerState.params);
|
|
305
|
+
const path = $derived(routerState.path);
|
|
306
306
|
const resourceNames = $derived(new Set(resources.map((resource) => resource.name)));
|
|
307
|
-
const hasRouteResource = $derived(!params.resource || resourceNames.has(params.resource));
|
|
308
|
-
const currentResourcePages = $derived(params.resource ? resourcePages?.[params.resource] : undefined);
|
|
309
307
|
const isStandaloneTwoFactor = $derived(route === '/2fa' || route === '/authentication/branded/2fa');
|
|
310
308
|
const standaloneErrorStatus = $derived.by((): '404' | '500' | undefined => {
|
|
311
309
|
if (route === '/authentication/error-404' || route === '/404' || (route === '/:resource' && params.resource === '404')) return '404';
|
|
@@ -316,9 +314,82 @@
|
|
|
316
314
|
// Auth check
|
|
317
315
|
let isAuthenticated = $state(untrack(() => !resolvedAuthProvider));
|
|
318
316
|
let authChecked = $state(untrack(() => !resolvedAuthProvider));
|
|
317
|
+
let checkedAuthProvider = $state.raw<AuthProvider | null>(null);
|
|
318
|
+
let checkedAuthPath = $state<string | null>(null);
|
|
319
|
+
let navigationAuthPending = $state(false);
|
|
320
|
+
let navigationCheckId = 0;
|
|
321
|
+
const authProviderPending = $derived(!!resolvedAuthProvider && checkedAuthProvider !== resolvedAuthProvider);
|
|
322
|
+
const authRechecking = $derived(
|
|
323
|
+
navigationAuthPending
|
|
324
|
+
|| (!!resolvedAuthProvider
|
|
325
|
+
&& !authProviderPending
|
|
326
|
+
&& authChecked
|
|
327
|
+
&& isAuthenticated
|
|
328
|
+
&& checkedAuthPath !== path),
|
|
329
|
+
);
|
|
330
|
+
const renderedRoute = $derived(route);
|
|
331
|
+
const renderedParams = $derived(params);
|
|
332
|
+
const renderedHasRouteResource = $derived(!renderedParams.resource || resourceNames.has(renderedParams.resource));
|
|
333
|
+
const renderedResourcePages = $derived(renderedParams.resource ? resourcePages?.[renderedParams.resource] : undefined);
|
|
334
|
+
|
|
335
|
+
async function navigateWithinApp(path: string, options?: { replaceState?: boolean }) {
|
|
336
|
+
await adminContext.navigate(path, options);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async function commitNavigation(options: Parameters<RouterProvider['go']>[0]): Promise<Awaited<ReturnType<RouterProvider['go']>>> {
|
|
340
|
+
const navigated = await resolvedRouter.go(options);
|
|
341
|
+
if (navigated === false) return false;
|
|
342
|
+
routerState.sync();
|
|
343
|
+
return navigated;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function recordCheckedRoute(provider: AuthProvider) {
|
|
347
|
+
checkedAuthProvider = provider;
|
|
348
|
+
checkedAuthPath = routerState.path;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async function navigateAfterAuthCheck(options: Parameters<RouterProvider['go']>[0]): Promise<Awaited<ReturnType<RouterProvider['go']>>> {
|
|
352
|
+
const provider = resolvedAuthProvider;
|
|
353
|
+
if (!provider || checkedAuthProvider !== provider || !authChecked || !isAuthenticated) {
|
|
354
|
+
return commitNavigation(options);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const checkId = ++navigationCheckId;
|
|
358
|
+
navigationAuthPending = true;
|
|
359
|
+
let result: Awaited<ReturnType<AuthProvider['check']>>;
|
|
360
|
+
try {
|
|
361
|
+
result = await provider.check();
|
|
362
|
+
} catch (err) {
|
|
363
|
+
if (checkId !== navigationCheckId || provider !== resolvedAuthProvider) return false;
|
|
364
|
+
console.warn('Auth check failed:', err);
|
|
365
|
+
isAuthenticated = false;
|
|
366
|
+
authChecked = true;
|
|
367
|
+
try {
|
|
368
|
+
await navigateWithinApp('/login', { replaceState: true });
|
|
369
|
+
recordCheckedRoute(provider);
|
|
370
|
+
return false;
|
|
371
|
+
} finally {
|
|
372
|
+
if (checkId === navigationCheckId) navigationAuthPending = false;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
319
375
|
|
|
320
|
-
|
|
321
|
-
|
|
376
|
+
if (checkId !== navigationCheckId || provider !== resolvedAuthProvider) return false;
|
|
377
|
+
try {
|
|
378
|
+
if (!result.authenticated) {
|
|
379
|
+
isAuthenticated = false;
|
|
380
|
+
authChecked = true;
|
|
381
|
+
await navigateWithinApp(result.redirectTo ?? '/login', { replaceState: true });
|
|
382
|
+
recordCheckedRoute(provider);
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
const navigated = await commitNavigation(options);
|
|
386
|
+
if (navigated === false) return false;
|
|
387
|
+
if (checkId !== navigationCheckId || provider !== resolvedAuthProvider) return false;
|
|
388
|
+
recordCheckedRoute(provider);
|
|
389
|
+
return navigated;
|
|
390
|
+
} finally {
|
|
391
|
+
if (checkId === navigationCheckId) navigationAuthPending = false;
|
|
392
|
+
}
|
|
322
393
|
}
|
|
323
394
|
|
|
324
395
|
function isPublicSystemRoute(currentRoute: string): boolean {
|
|
@@ -332,19 +403,35 @@
|
|
|
332
403
|
let cancelled = false;
|
|
333
404
|
// Explicitly track route so auth is re-checked on navigation
|
|
334
405
|
const _route = route;
|
|
406
|
+
const _path = path;
|
|
407
|
+
const provider = resolvedAuthProvider;
|
|
408
|
+
navigationCheckId += 1;
|
|
409
|
+
navigationAuthPending = false;
|
|
335
410
|
|
|
336
|
-
if (!
|
|
411
|
+
if (!provider) {
|
|
337
412
|
if (!cancelled) {
|
|
338
413
|
isAuthenticated = true;
|
|
339
414
|
authChecked = true;
|
|
415
|
+
checkedAuthProvider = null;
|
|
416
|
+
checkedAuthPath = _path;
|
|
340
417
|
}
|
|
341
418
|
return;
|
|
342
419
|
}
|
|
343
|
-
|
|
344
|
-
|
|
420
|
+
if (
|
|
421
|
+
untrack(() => checkedAuthProvider) === provider
|
|
422
|
+
&& untrack(() => checkedAuthPath) === _path
|
|
423
|
+
) return;
|
|
424
|
+
const providerChanged = untrack(() => checkedAuthProvider) !== provider;
|
|
425
|
+
if (providerChanged) {
|
|
426
|
+
isAuthenticated = false;
|
|
427
|
+
authChecked = false;
|
|
428
|
+
}
|
|
429
|
+
provider.check().then(result => {
|
|
345
430
|
if (cancelled) return;
|
|
346
431
|
isAuthenticated = result.authenticated;
|
|
347
432
|
authChecked = true;
|
|
433
|
+
checkedAuthProvider = provider;
|
|
434
|
+
checkedAuthPath = _path;
|
|
348
435
|
if (!result.authenticated && _route !== '/login' && _route !== '/register' && _route !== '/forgot-password' && _route !== '/update-password' && !isPublicSystemRoute(_route)) {
|
|
349
436
|
void navigateWithinApp(result.redirectTo ?? '/login');
|
|
350
437
|
}
|
|
@@ -353,6 +440,8 @@
|
|
|
353
440
|
console.warn('Auth check failed:', err);
|
|
354
441
|
isAuthenticated = false;
|
|
355
442
|
authChecked = true;
|
|
443
|
+
checkedAuthProvider = provider;
|
|
444
|
+
checkedAuthPath = _path;
|
|
356
445
|
if (_route !== '/login' && _route !== '/register' && _route !== '/forgot-password' && _route !== '/update-password' && !isPublicSystemRoute(_route)) {
|
|
357
446
|
void navigateWithinApp('/login');
|
|
358
447
|
}
|
|
@@ -365,7 +454,7 @@
|
|
|
365
454
|
<QueryClientProvider client={queryClient}>
|
|
366
455
|
{#if !resolvedDataProvider}
|
|
367
456
|
<ConfigErrorScreen title="{title} — DataProvider is required" />
|
|
368
|
-
{:else if !authChecked}
|
|
457
|
+
{:else if !authChecked || authProviderPending}
|
|
369
458
|
<div class="flex h-screen items-center justify-center">
|
|
370
459
|
<div class="space-y-4 text-center">
|
|
371
460
|
<div class="h-8 w-8 animate-spin rounded-full border-4 border-muted border-t-primary mx-auto"></div>
|
|
@@ -390,7 +479,7 @@
|
|
|
390
479
|
<UpdatePasswordPage {title} />
|
|
391
480
|
{:else if route === '/login' || route === '/register' || route === '/forgot-password' || route === '/update-password'}
|
|
392
481
|
<ConfigErrorScreen title="{title} — {translation.t('common.configRequired')}" />
|
|
393
|
-
{:else if (isAuthenticated || !resolvedAuthProvider) && isStandaloneTwoFactor}
|
|
482
|
+
{:else if (isAuthenticated || !resolvedAuthProvider) && isStandaloneTwoFactor && !authRechecking}
|
|
394
483
|
<SystemPageShell {title}>
|
|
395
484
|
<LazyPage loader={loadTwoFactorAuthPage} props={{}} />
|
|
396
485
|
</SystemPageShell>
|
|
@@ -401,47 +490,56 @@
|
|
|
401
490
|
</SystemPageShell>
|
|
402
491
|
{:else if isAuthenticated || !resolvedAuthProvider}
|
|
403
492
|
<Layout {title} {menu} {siteUrl} routeMode={resolvedRouteMode}>
|
|
404
|
-
|
|
493
|
+
<div class="relative min-h-40" aria-busy={authRechecking}>
|
|
494
|
+
{#if authRechecking}
|
|
495
|
+
<div class="absolute inset-0 z-10 flex min-h-40 items-center justify-center bg-background/80" role="status" aria-live="polite">
|
|
496
|
+
<div class="space-y-3 text-center">
|
|
497
|
+
<div class="h-6 w-6 animate-spin rounded-full border-4 border-muted border-t-primary mx-auto"></div>
|
|
498
|
+
<p class="text-sm text-muted-foreground">Loading...</p>
|
|
499
|
+
</div>
|
|
500
|
+
</div>
|
|
501
|
+
{:else}
|
|
502
|
+
{#key renderedRoute + (renderedParams.resource ?? '') + (renderedParams.id ?? '') + (renderedParams.variant ?? '') + (renderedParams.columns ?? '')}
|
|
405
503
|
<div class="svadmin-page-enter">
|
|
406
|
-
{#if
|
|
504
|
+
{#if renderedRoute === '/public-profile'}
|
|
407
505
|
<LazyPage loader={loadPublicProfilePage} props={{ variant: 'default', initialTab: 'projects' }} />
|
|
408
|
-
{:else if
|
|
409
|
-
<LazyPage loader={loadPublicProfilePage} props={{ variant: 'default', initialTab: 'projects', columns:
|
|
410
|
-
{:else if
|
|
506
|
+
{:else if renderedRoute === '/public-profile/projects/:columns'}
|
|
507
|
+
<LazyPage loader={loadPublicProfilePage} props={{ variant: 'default', initialTab: 'projects', columns: renderedParams.columns?.includes('3') ? 3 : 2 }} />
|
|
508
|
+
{:else if renderedRoute === '/public-profile/activity'}
|
|
411
509
|
<LazyPage loader={loadPublicProfilePage} props={{ variant: 'default', initialTab: 'activity' }} />
|
|
412
|
-
{:else if
|
|
510
|
+
{:else if renderedRoute === '/public-profile/teams'}
|
|
413
511
|
<LazyPage loader={loadPublicProfilePage} props={{ variant: 'default', initialTab: 'teams' }} />
|
|
414
|
-
{:else if
|
|
415
|
-
<LazyPage loader={loadPublicProfilePage} props={{ variant: (
|
|
416
|
-
{:else if
|
|
512
|
+
{:else if renderedRoute === '/public-profile/profiles/:variant'}
|
|
513
|
+
<LazyPage loader={loadPublicProfilePage} props={{ variant: (renderedParams.variant === 'company' || renderedParams.variant === 'gamer' || renderedParams.variant === 'default' ? renderedParams.variant : 'default') as 'company' | 'gamer' | 'default', showSections: true }} />
|
|
514
|
+
{:else if renderedRoute === '/account/get-started' || renderedRoute === '/account/home/get-started'}
|
|
417
515
|
<LazyPage loader={loadGetStartedPage} props={{}} />
|
|
418
|
-
{:else if
|
|
516
|
+
{:else if renderedRoute === '/account/home/user-profile'}
|
|
419
517
|
<LazyPage loader={loadUserProfilePage} props={{}} />
|
|
420
|
-
{:else if
|
|
518
|
+
{:else if renderedRoute === '/account/company-profile' || renderedRoute === '/account/home/company-profile'}
|
|
421
519
|
<LazyPage loader={loadCompanyProfilePage} props={{}} />
|
|
422
|
-
{:else if
|
|
520
|
+
{:else if renderedRoute === '/account/settings-plain' || renderedRoute === '/account/home/settings-plain'}
|
|
423
521
|
<LazyPage loader={loadSettingsPlainPage} props={{}} />
|
|
424
|
-
{:else if
|
|
522
|
+
{:else if renderedRoute === '/account/settings-sidebar' || renderedRoute === '/account/home/settings-sidebar'}
|
|
425
523
|
<LazyPage loader={loadSettingsSidebarPage} props={{}} />
|
|
426
|
-
{:else if
|
|
524
|
+
{:else if renderedRoute === '/account/settings-enterprise' || renderedRoute === '/account/home/settings-enterprise'}
|
|
427
525
|
<LazyPage loader={loadSettingsEnterprisePage} props={{}} />
|
|
428
|
-
{:else if
|
|
526
|
+
{:else if renderedRoute === '/account/:tab'}
|
|
429
527
|
<SettingsPage />
|
|
430
|
-
{:else if
|
|
528
|
+
{:else if renderedRoute === '/account/import-members' || renderedRoute === '/account/members/import-members'}
|
|
431
529
|
<LazyPage loader={loadImportMembersPage} props={{}} />
|
|
432
|
-
{:else if
|
|
530
|
+
{:else if renderedRoute === '/account/members-starter' || renderedRoute === '/account/members/members-starter'}
|
|
433
531
|
<LazyPage loader={loadMembersStarterPage} props={{}} />
|
|
434
|
-
{:else if
|
|
532
|
+
{:else if renderedRoute === '/account/team-members' || renderedRoute === '/account/members/team-members'}
|
|
435
533
|
<LazyPage loader={loadTeamMembersPage} props={{}} />
|
|
436
|
-
{:else if
|
|
534
|
+
{:else if renderedRoute === '/account/security-log' || renderedRoute === '/account/security/security-log'}
|
|
437
535
|
<LazyPage loader={loadSecurityLogPage} props={{}} />
|
|
438
|
-
{:else if
|
|
536
|
+
{:else if renderedRoute === '/network/user-cards' || renderedRoute === '/network/user-cards/nft'}
|
|
439
537
|
<LazyPage loader={loadUserCardsNFTPage} props={{}} />
|
|
440
|
-
{:else if
|
|
538
|
+
{:else if renderedRoute === '/network/team-crew' || renderedRoute === '/network/user-table/team-crew'}
|
|
441
539
|
<LazyPage loader={loadTeamCrewTablePage} props={{}} />
|
|
442
|
-
{:else if
|
|
540
|
+
{:else if renderedRoute.startsWith('/settings')}
|
|
443
541
|
<SettingsPage />
|
|
444
|
-
{:else if
|
|
542
|
+
{:else if renderedRoute === '/' || renderedRoute === '' || renderedRoute === '/inventory-dashboard'}
|
|
445
543
|
{#if dashboard}
|
|
446
544
|
{@render dashboard()}
|
|
447
545
|
{:else}
|
|
@@ -450,30 +548,30 @@
|
|
|
450
548
|
<p class="text-muted-foreground">{translation.t('common.dashboardHint')}</p>
|
|
451
549
|
</div>
|
|
452
550
|
{/if}
|
|
453
|
-
{:else if (
|
|
454
|
-
{#key
|
|
455
|
-
{@const Comp =
|
|
456
|
-
<Comp resourceName={
|
|
551
|
+
{:else if (renderedRoute === '/:resource' || renderedRoute === '/:parent/:parentId/:resource') && renderedHasRouteResource}
|
|
552
|
+
{#key renderedParams.resource}
|
|
553
|
+
{@const Comp = renderedResourcePages?.list ?? mergedComponents.AutoTable}
|
|
554
|
+
<Comp resourceName={renderedParams.resource} />
|
|
457
555
|
{/key}
|
|
458
|
-
{:else if (
|
|
459
|
-
{#key
|
|
460
|
-
{@const Comp =
|
|
461
|
-
<Comp resourceName={
|
|
556
|
+
{:else if (renderedRoute === '/:resource/create' || renderedRoute === '/:parent/:parentId/:resource/create') && renderedHasRouteResource}
|
|
557
|
+
{#key renderedParams.resource}
|
|
558
|
+
{@const Comp = renderedResourcePages?.create ?? mergedComponents.AutoForm}
|
|
559
|
+
<Comp resourceName={renderedParams.resource} mode="create" />
|
|
462
560
|
{/key}
|
|
463
|
-
{:else if (
|
|
464
|
-
{#key `${
|
|
465
|
-
{@const Comp =
|
|
466
|
-
<Comp resourceName={
|
|
561
|
+
{:else if (renderedRoute === '/:resource/edit/:id' || renderedRoute === '/:parent/:parentId/:resource/edit/:id') && renderedHasRouteResource}
|
|
562
|
+
{#key `${renderedParams.resource}-${renderedParams.id}`}
|
|
563
|
+
{@const Comp = renderedResourcePages?.edit ?? mergedComponents.AutoForm}
|
|
564
|
+
<Comp resourceName={renderedParams.resource} mode="edit" id={renderedParams.id} />
|
|
467
565
|
{/key}
|
|
468
|
-
{:else if (
|
|
469
|
-
{#key `${
|
|
470
|
-
{@const Comp =
|
|
471
|
-
<Comp resourceName={
|
|
566
|
+
{:else if (renderedRoute === '/:resource/show/:id' || renderedRoute === '/:parent/:parentId/:resource/show/:id') && renderedHasRouteResource}
|
|
567
|
+
{#key `${renderedParams.resource}-${renderedParams.id}`}
|
|
568
|
+
{@const Comp = renderedResourcePages?.show ?? mergedComponents.ShowPage}
|
|
569
|
+
<Comp resourceName={renderedParams.resource} id={renderedParams.id} />
|
|
472
570
|
{/key}
|
|
473
|
-
{:else if (
|
|
474
|
-
{#key `${
|
|
475
|
-
{@const Comp =
|
|
476
|
-
<Comp resourceName={
|
|
571
|
+
{:else if (renderedRoute === '/:resource/clone/:id' || renderedRoute === '/:parent/:parentId/:resource/clone/:id') && renderedHasRouteResource}
|
|
572
|
+
{#key `${renderedParams.resource}-clone-${renderedParams.id}`}
|
|
573
|
+
{@const Comp = renderedResourcePages?.clone ?? mergedComponents.AutoForm}
|
|
574
|
+
<Comp resourceName={renderedParams.resource} mode="clone" id={renderedParams.id} />
|
|
477
575
|
{/key}
|
|
478
576
|
{:else}
|
|
479
577
|
{@const ErrorComp = mergedComponents.ErrorPage || ErrorPage}
|
|
@@ -481,6 +579,8 @@
|
|
|
481
579
|
{/if}
|
|
482
580
|
</div>
|
|
483
581
|
{/key}
|
|
582
|
+
{/if}
|
|
583
|
+
</div>
|
|
484
584
|
</Layout>
|
|
485
585
|
{:else}
|
|
486
586
|
<div class="flex h-screen items-center justify-center">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svadmin/ui",
|
|
3
|
-
"version": "0.42.
|
|
3
|
+
"version": "0.42.2",
|
|
4
4
|
"description": "Pre-built admin UI components — AdminApp, AutoTable, AutoForm, Sidebar, Layout",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"./components/*": "./dist/components/*"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@svadmin/core": "^0.36.
|
|
42
|
+
"@svadmin/core": "^0.36.3",
|
|
43
43
|
"@tanstack/svelte-query": "^6.1.38",
|
|
44
44
|
"highlight.js": "^11.11.1",
|
|
45
45
|
"isomorphic-dompurify": "^3.22.0",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
77
|
"@floating-ui/dom": "^1.8.0",
|
|
78
|
-
"@lucide/svelte": "^1.
|
|
78
|
+
"@lucide/svelte": "^1.31.0",
|
|
79
79
|
"@tanstack/svelte-store": "^0.12.1",
|
|
80
80
|
"@tanstack/svelte-table": "9.1.2",
|
|
81
81
|
"@tanstack/table-core": "9.1.2",
|
|
@@ -89,19 +89,19 @@
|
|
|
89
89
|
"author": "zuohuadong",
|
|
90
90
|
"repository": {
|
|
91
91
|
"type": "git",
|
|
92
|
-
"url": "https://github.com/
|
|
92
|
+
"url": "https://github.com/vibeunion/svadmin.git",
|
|
93
93
|
"directory": "packages/ui"
|
|
94
94
|
},
|
|
95
|
-
"homepage": "https://github.com/
|
|
95
|
+
"homepage": "https://github.com/vibeunion/svadmin#readme",
|
|
96
96
|
"bugs": {
|
|
97
|
-
"url": "https://github.com/
|
|
97
|
+
"url": "https://github.com/vibeunion/svadmin/issues"
|
|
98
98
|
},
|
|
99
99
|
"devDependencies": {
|
|
100
100
|
"@happy-dom/global-registrator": "^20.11.2",
|
|
101
101
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
102
102
|
"@testing-library/dom": "^10.4.1",
|
|
103
103
|
"@testing-library/svelte": "^5.4.2",
|
|
104
|
-
"@testing-library/user-event": "^14.6.
|
|
104
|
+
"@testing-library/user-event": "^14.6.4",
|
|
105
105
|
"happy-dom": "^20.11.2",
|
|
106
106
|
"vitest": "^4.1.10"
|
|
107
107
|
},
|