@svadmin/ui 0.34.10 → 0.35.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.
Files changed (70) hide show
  1. package/dist/app.css +372 -0
  2. package/dist/component-registry.svelte.d.ts +1 -0
  3. package/dist/components/AdminApp.svelte +94 -15
  4. package/dist/components/AdminApp.svelte.d.ts +32 -2
  5. package/dist/components/ApiSettings.svelte +191 -0
  6. package/dist/components/ApiSettings.svelte.d.ts +3 -0
  7. package/dist/components/AutoForm.svelte +48 -43
  8. package/dist/components/AutoTable.svelte +27 -22
  9. package/dist/components/ErrorPage.svelte +58 -0
  10. package/dist/components/ErrorPage.svelte.d.ts +8 -0
  11. package/dist/components/FieldRenderer.svelte +2 -2
  12. package/dist/components/ForgotPasswordPage.svelte +111 -68
  13. package/dist/components/Header.svelte +6 -7
  14. package/dist/components/IntegrationsSettings.svelte +70 -0
  15. package/dist/components/IntegrationsSettings.svelte.d.ts +4 -0
  16. package/dist/components/Layout.svelte +8 -8
  17. package/dist/components/LoginPage.svelte +207 -105
  18. package/dist/components/LoginPage.svelte.d.ts +3 -15
  19. package/dist/components/NotificationsSettings.svelte +115 -0
  20. package/dist/components/NotificationsSettings.svelte.d.ts +3 -0
  21. package/dist/components/PageHeader.svelte +9 -7
  22. package/dist/components/RegisterPage.svelte +103 -65
  23. package/dist/components/ResourceOperationsPage.svelte +153 -0
  24. package/dist/components/ResourceOperationsPage.svelte.d.ts +37 -0
  25. package/dist/components/SecuritySettings.svelte +179 -0
  26. package/dist/components/SecuritySettings.svelte.d.ts +4 -0
  27. package/dist/components/SettingsPage.svelte +121 -54
  28. package/dist/components/SettingsPage.svelte.d.ts +25 -1
  29. package/dist/components/ShowPage.svelte +9 -9
  30. package/dist/components/Sidebar.svelte +96 -48
  31. package/dist/components/SidebarItem.svelte +37 -12
  32. package/dist/components/TwoFactorAuthPage.svelte +247 -0
  33. package/dist/components/TwoFactorAuthPage.svelte.d.ts +3 -0
  34. package/dist/components/UpdatePasswordPage.svelte +86 -48
  35. package/dist/components/account/CompanyProfilePage.svelte +99 -0
  36. package/dist/components/account/CompanyProfilePage.svelte.d.ts +3 -0
  37. package/dist/components/account/GetStartedPage.svelte +123 -0
  38. package/dist/components/account/GetStartedPage.svelte.d.ts +4 -0
  39. package/dist/components/account/ImportMembersPage.svelte +143 -0
  40. package/dist/components/account/ImportMembersPage.svelte.d.ts +3 -0
  41. package/dist/components/account/MembersStarterPage.svelte +104 -0
  42. package/dist/components/account/MembersStarterPage.svelte.d.ts +3 -0
  43. package/dist/components/account/SecurityLogPage.svelte +108 -0
  44. package/dist/components/account/SecurityLogPage.svelte.d.ts +3 -0
  45. package/dist/components/account/SettingsEnterprisePage.svelte +131 -0
  46. package/dist/components/account/SettingsEnterprisePage.svelte.d.ts +3 -0
  47. package/dist/components/account/SettingsPlainPage.svelte +81 -0
  48. package/dist/components/account/SettingsPlainPage.svelte.d.ts +3 -0
  49. package/dist/components/account/SettingsSidebarPage.svelte +98 -0
  50. package/dist/components/account/SettingsSidebarPage.svelte.d.ts +3 -0
  51. package/dist/components/account/TeamMembersPage.svelte +98 -0
  52. package/dist/components/account/TeamMembersPage.svelte.d.ts +3 -0
  53. package/dist/components/network/TeamCrewTablePage.svelte +122 -0
  54. package/dist/components/network/TeamCrewTablePage.svelte.d.ts +3 -0
  55. package/dist/components/network/UserCardsNFTPage.svelte +134 -0
  56. package/dist/components/network/UserCardsNFTPage.svelte.d.ts +3 -0
  57. package/dist/components/profile/ActivityTimeline.svelte +77 -0
  58. package/dist/components/profile/ActivityTimeline.svelte.d.ts +16 -0
  59. package/dist/components/profile/ProfileCard.svelte +216 -0
  60. package/dist/components/profile/ProfileCard.svelte.d.ts +27 -0
  61. package/dist/components/profile/ProjectsGrid.svelte +91 -0
  62. package/dist/components/profile/ProjectsGrid.svelte.d.ts +17 -0
  63. package/dist/components/profile/PublicProfilePage.svelte +179 -0
  64. package/dist/components/profile/PublicProfilePage.svelte.d.ts +10 -0
  65. package/dist/components/profile/TeamsShowcase.svelte +98 -0
  66. package/dist/components/profile/TeamsShowcase.svelte.d.ts +19 -0
  67. package/dist/index.d.ts +23 -0
  68. package/dist/index.js +27 -0
  69. package/dist/router-state.svelte.js +17 -0
  70. package/package.json +2 -2
package/dist/app.css CHANGED
@@ -672,3 +672,375 @@ main {
672
672
  .chat-markdown strong {
673
673
  font-weight: 600;
674
674
  }
675
+
676
+ /* ─── High-Contrast Flat Layout Preset (clean-flat) ─── */
677
+ .layout-clean-flat {
678
+ --radius: 0.75rem;
679
+ }
680
+
681
+ /* Base card flat background overrides */
682
+ .layout-clean-flat .bg-card,
683
+ .layout-clean-flat [data-slot="card"] {
684
+ background: #ffffff !important;
685
+ border: 1px solid #f1f3f7 !important;
686
+ box-shadow: 0px 0px 20px 0px rgba(76, 87, 125, 0.02) !important;
687
+ border-radius: 12px !important;
688
+ }
689
+ .dark.layout-clean-flat .bg-card,
690
+ .dark .layout-clean-flat .bg-card,
691
+ .dark.layout-clean-flat [data-slot="card"],
692
+ .dark .layout-clean-flat [data-slot="card"] {
693
+ background: #1e1e2d !important;
694
+ border: 1px solid #2b2b40 !important;
695
+ box-shadow: none !important;
696
+ }
697
+
698
+ /* Crisp table header styles */
699
+ .layout-clean-flat th {
700
+ background-color: #f9f9f9 !important;
701
+ border-bottom: 1px solid #f1f3f7 !important;
702
+ color: #181c32 !important;
703
+ font-weight: 600 !important;
704
+ font-size: 0.75rem !important;
705
+ text-transform: uppercase !important;
706
+ letter-spacing: 0.05em !important;
707
+ }
708
+ .dark.layout-clean-flat th,
709
+ .dark .layout-clean-flat th {
710
+ background-color: #1a1a27 !important;
711
+ border-bottom: 1px solid #2b2b40 !important;
712
+ color: #a1a5b7 !important;
713
+ }
714
+
715
+ /* Crisp table cell styles */
716
+ .layout-clean-flat td {
717
+ border-bottom: 1px solid #f1f3f7 !important;
718
+ color: #5e6278 !important;
719
+ font-size: 0.8125rem !important;
720
+ }
721
+ .dark.layout-clean-flat td,
722
+ .dark .layout-clean-flat td {
723
+ border-bottom: 1px solid rgba(43, 43, 64, 0.6) !important;
724
+ color: #a1a5b7 !important;
725
+ }
726
+
727
+ /* Table row hover state */
728
+ .layout-clean-flat tr:hover td {
729
+ background-color: #f9f9f9 !important;
730
+ }
731
+ .dark.layout-clean-flat tr:hover td,
732
+ .dark .layout-clean-flat tr:hover td {
733
+ background-color: rgba(43, 43, 64, 0.3) !important;
734
+ }
735
+
736
+ /* Sidebar layout adjustments */
737
+ .layout-clean-flat [data-sidebar="sidebar"] {
738
+ background: #ffffff !important;
739
+ color: #181c32 !important;
740
+ border-right: 1px solid #f1f3f7 !important;
741
+ box-shadow: 0 0 40px rgba(76, 87, 125, 0.04) !important;
742
+ }
743
+ .dark.layout-clean-flat [data-sidebar="sidebar"],
744
+ .dark .layout-clean-flat [data-sidebar="sidebar"] {
745
+ background: #1e1e2d !important;
746
+ color: #a1a5b7 !important;
747
+ border-right: 1px solid #2b2b40 !important;
748
+ }
749
+
750
+ .layout-clean-flat [data-sidebar="group-label"] {
751
+ font-size: 0.6875rem !important;
752
+ font-weight: 600 !important;
753
+ text-transform: uppercase;
754
+ letter-spacing: 0.06em;
755
+ color: #a1a5b7 !important;
756
+ padding: 0.5rem 0.5rem 0.375rem !important;
757
+ }
758
+
759
+ .layout-clean-flat [data-sidebar="menu-button"] {
760
+ color: #5e6278 !important;
761
+ border-radius: 0.675rem !important;
762
+ transition: all 0.15s ease !important;
763
+ font-weight: 500 !important;
764
+ }
765
+
766
+ .layout-clean-flat [data-sidebar="menu-button"]:hover {
767
+ background: #f5f8fa !important;
768
+ color: #181c32 !important;
769
+ }
770
+ .dark.layout-clean-flat [data-sidebar="menu-button"]:hover,
771
+ .dark .layout-clean-flat [data-sidebar="menu-button"]:hover {
772
+ background: #2b2b40 !important;
773
+ color: #f1f6ff !important;
774
+ }
775
+
776
+ .layout-clean-flat [data-sidebar="menu-button"]:hover > svg {
777
+ opacity: 0.85 !important;
778
+ }
779
+
780
+ .layout-clean-flat [data-sidebar="menu-button"][data-active="true"] {
781
+ background: #eef6ff !important;
782
+ color: #3e97ff !important;
783
+ font-weight: 600 !important;
784
+ box-shadow: none !important;
785
+ }
786
+ .dark.layout-clean-flat [data-sidebar="menu-button"][data-active="true"],
787
+ .dark .layout-clean-flat [data-sidebar="menu-button"][data-active="true"] {
788
+ background: rgba(62, 151, 255, 0.1) !important;
789
+ color: #3e97ff !important;
790
+ }
791
+
792
+ .layout-clean-flat [data-sidebar="menu-button"][data-active="true"] svg {
793
+ color: #3e97ff !important;
794
+ opacity: 1 !important;
795
+ }
796
+
797
+ .layout-clean-flat [data-sidebar="menu-button"][data-active="true"] [data-sidebar="menu-icon"],
798
+ .layout-clean-flat [data-sidebar="menu-sub-button"][data-active="true"] [data-sidebar="menu-icon"] {
799
+ background: rgba(62, 151, 255, 0.12) !important;
800
+ color: #3e97ff !important;
801
+ }
802
+
803
+ .layout-clean-flat [data-sidebar="menu-sub"] {
804
+ margin-left: 1.125rem !important;
805
+ padding-left: 0.75rem !important;
806
+ border-left: 1px solid #f1f3f7 !important;
807
+ }
808
+ .dark.layout-clean-flat [data-sidebar="menu-sub"],
809
+ .dark .layout-clean-flat [data-sidebar="menu-sub"] {
810
+ border-left-color: #2b2b40 !important;
811
+ }
812
+
813
+ .layout-clean-flat [data-sidebar="menu-sub-button"] {
814
+ color: #5e6278 !important;
815
+ border-radius: 0.675rem !important;
816
+ font-weight: 400 !important;
817
+ transition: all 0.15s !important;
818
+ }
819
+
820
+ .layout-clean-flat [data-sidebar="menu-sub-button"]:hover {
821
+ background: #f5f8fa !important;
822
+ color: #181c32 !important;
823
+ }
824
+ .dark.layout-clean-flat [data-sidebar="menu-sub-button"]:hover,
825
+ .dark .layout-clean-flat [data-sidebar="menu-sub-button"]:hover {
826
+ background: #2b2b40 !important;
827
+ color: #f1f6ff !important;
828
+ }
829
+
830
+ .layout-clean-flat [data-sidebar="menu-sub-button"][data-active="true"] {
831
+ background: #eef6ff !important;
832
+ color: #3e97ff !important;
833
+ font-weight: 500 !important;
834
+ box-shadow: none !important;
835
+ }
836
+ .dark.layout-clean-flat [data-sidebar="menu-sub-button"][data-active="true"],
837
+ .dark .layout-clean-flat [data-sidebar="menu-sub-button"][data-active="true"] {
838
+ background: rgba(62, 151, 255, 0.1) !important;
839
+ color: #3e97ff !important;
840
+ }
841
+
842
+ /* Button & input styles override under clean-flat layout */
843
+ .layout-clean-flat [data-slot="button"][data-variant="default"] {
844
+ background: #3e97ff !important;
845
+ color: #ffffff !important;
846
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05) !important;
847
+ border-radius: 8px !important;
848
+ font-weight: 600 !important;
849
+ transition: all 0.15s ease !important;
850
+ }
851
+ .layout-clean-flat [data-slot="button"][data-variant="default"]:hover {
852
+ background: #1880ff !important;
853
+ }
854
+
855
+ .layout-clean-flat [data-slot="button"][data-variant="outline"] {
856
+ background: #ffffff !important;
857
+ color: #5e6278 !important;
858
+ border: 1px solid #e1e3ea !important;
859
+ box-shadow: 0 1px 2px 0 rgba(0,0,0,0.05) !important;
860
+ border-radius: 8px !important;
861
+ font-weight: 600 !important;
862
+ transition: all 0.15s ease !important;
863
+ }
864
+ .layout-clean-flat [data-slot="button"][data-variant="outline"]:hover {
865
+ background: #f5f8fa !important;
866
+ border-color: #cbd5e1 !important;
867
+ color: #181c32 !important;
868
+ }
869
+ .dark.layout-clean-flat [data-slot="button"][data-variant="outline"],
870
+ .dark .layout-clean-flat [data-slot="button"][data-variant="outline"] {
871
+ background: #1e1e2d !important;
872
+ color: #a1a5b7 !important;
873
+ border-color: #2b2b40 !important;
874
+ }
875
+ .dark.layout-clean-flat [data-slot="button"][data-variant="outline"]:hover,
876
+ .dark .layout-clean-flat [data-slot="button"][data-variant="outline"]:hover {
877
+ background: #2b2b40 !important;
878
+ color: #ffffff !important;
879
+ }
880
+
881
+ .layout-clean-flat [data-slot="input"] {
882
+ border: 1px solid #e1e3ea !important;
883
+ border-radius: 8px !important;
884
+ box-shadow: inset 0 1px 2px rgba(0,0,0,0.01) !important;
885
+ outline: none !important;
886
+ transition: all 0.15s ease !important;
887
+ }
888
+ .layout-clean-flat [data-slot="input"]:focus {
889
+ border-color: #3e97ff !important;
890
+ box-shadow: 0 0 0 4px rgba(62,151,255,0.1) !important;
891
+ }
892
+ .dark.layout-clean-flat [data-slot="input"],
893
+ .dark .layout-clean-flat [data-slot="input"] {
894
+ border-color: #2b2b40 !important;
895
+ background: #151521 !important;
896
+ color: #f1f6ff !important;
897
+ }
898
+ .dark.layout-clean-flat [data-slot="input"]:focus,
899
+ .dark .layout-clean-flat [data-slot="input"]:focus {
900
+ border-color: #3e97ff !important;
901
+ }
902
+
903
+ /* ─── svadmin Metronic-inspired list and edit surfaces ─── */
904
+ [data-svadmin-list-header],
905
+ [data-svadmin-table-toolbar],
906
+ [data-svadmin-table-card],
907
+ [data-svadmin-form-header],
908
+ [data-svadmin-form-card],
909
+ [data-svadmin-form-actions] {
910
+ box-shadow: 0 0 24px rgba(76, 87, 125, 0.045) !important;
911
+ }
912
+
913
+ .layout-clean-flat [data-svadmin-list-header],
914
+ .layout-clean-flat [data-svadmin-table-toolbar],
915
+ .layout-clean-flat [data-svadmin-table-card],
916
+ .layout-clean-flat [data-svadmin-form-header],
917
+ .layout-clean-flat [data-svadmin-form-card],
918
+ .layout-clean-flat [data-svadmin-form-actions] {
919
+ border-color: #f1f3f7 !important;
920
+ background: #ffffff !important;
921
+ border-radius: 1.15rem !important;
922
+ }
923
+
924
+ .layout-clean-flat [data-svadmin-list-actions] [data-slot="button"],
925
+ .layout-clean-flat [data-svadmin-table-toolbar] [data-slot="button"],
926
+ .layout-clean-flat [data-svadmin-form-actions] [data-slot="button"] {
927
+ height: 2.625rem !important;
928
+ border-radius: 0.75rem !important;
929
+ }
930
+
931
+ .layout-clean-flat [data-svadmin-table-toolbar] {
932
+ padding: 0.875rem !important;
933
+ }
934
+
935
+ .layout-clean-flat [data-svadmin-table-toolbar] [data-slot="input"] {
936
+ height: 2.75rem !important;
937
+ border-radius: 0.85rem !important;
938
+ background: #f9f9f9 !important;
939
+ }
940
+
941
+ [data-svadmin-datatable] {
942
+ border-collapse: separate !important;
943
+ border-spacing: 0 !important;
944
+ }
945
+
946
+ [data-svadmin-table-head] [data-slot="table-head"] {
947
+ height: 3.25rem !important;
948
+ border-bottom: 1px solid rgba(225, 227, 234, 0.78) !important;
949
+ background: #f9f9f9 !important;
950
+ color: #a1a5b7 !important;
951
+ font-size: 0.6875rem !important;
952
+ font-weight: 700 !important;
953
+ letter-spacing: 0.08em !important;
954
+ text-transform: uppercase !important;
955
+ }
956
+
957
+ [data-svadmin-table-row] {
958
+ border-bottom-color: rgba(241, 243, 247, 0.95) !important;
959
+ }
960
+
961
+ [data-svadmin-table-row] [data-slot="table-cell"] {
962
+ height: 3.625rem !important;
963
+ color: #5e6278 !important;
964
+ font-size: 0.8125rem !important;
965
+ }
966
+
967
+ [data-svadmin-table-row]:hover [data-slot="table-cell"] {
968
+ background: #fbfcfd !important;
969
+ }
970
+
971
+ [data-svadmin-pagination] {
972
+ border-radius: 1rem;
973
+ padding: 0.35rem 0.15rem;
974
+ }
975
+
976
+ [data-svadmin-form-content] {
977
+ padding-top: 0 !important;
978
+ padding-bottom: 0 !important;
979
+ }
980
+
981
+ [data-svadmin-form-row] {
982
+ border-bottom: 1px solid rgba(241, 243, 247, 0.95);
983
+ padding: 1.35rem 0;
984
+ }
985
+
986
+ [data-svadmin-form-row]:last-child {
987
+ border-bottom: 0;
988
+ }
989
+
990
+ [data-svadmin-field-label] {
991
+ color: #3f4254 !important;
992
+ font-weight: 600 !important;
993
+ }
994
+
995
+ [data-svadmin-form-settings] [data-slot="input"],
996
+ [data-svadmin-form-settings] textarea,
997
+ [data-svadmin-form-settings] [data-slot="select-trigger"] {
998
+ min-height: 2.75rem !important;
999
+ border-radius: 0.85rem !important;
1000
+ background: #f9f9f9 !important;
1001
+ }
1002
+
1003
+ [data-svadmin-form-actions] {
1004
+ width: fit-content;
1005
+ max-width: 100%;
1006
+ }
1007
+
1008
+ @media (min-width: 768px) {
1009
+ [data-svadmin-form-settings] [data-svadmin-field] {
1010
+ display: grid !important;
1011
+ grid-template-columns: minmax(10rem, 14rem) minmax(0, 1fr) !important;
1012
+ align-items: start !important;
1013
+ column-gap: 2rem !important;
1014
+ row-gap: 0.35rem !important;
1015
+ }
1016
+
1017
+ [data-svadmin-form-settings] [data-svadmin-field] > [data-svadmin-field-label] {
1018
+ padding-top: 0.8rem !important;
1019
+ }
1020
+
1021
+ [data-svadmin-form-settings] [data-svadmin-field] > :not([data-svadmin-field-label]) {
1022
+ grid-column: 2 !important;
1023
+ }
1024
+ }
1025
+
1026
+ .dark [data-svadmin-table-head] [data-slot="table-head"] {
1027
+ background: #1a1a27 !important;
1028
+ border-bottom-color: #2b2b40 !important;
1029
+ color: #a1a5b7 !important;
1030
+ }
1031
+
1032
+ .dark [data-svadmin-table-row] {
1033
+ border-bottom-color: rgba(43, 43, 64, 0.82) !important;
1034
+ }
1035
+
1036
+ .dark [data-svadmin-table-row] [data-slot="table-cell"] {
1037
+ color: #a1a5b7 !important;
1038
+ }
1039
+
1040
+ .dark [data-svadmin-table-row]:hover [data-slot="table-cell"] {
1041
+ background: rgba(43, 43, 64, 0.32) !important;
1042
+ }
1043
+
1044
+ .dark [data-svadmin-form-row] {
1045
+ border-bottom-color: rgba(43, 43, 64, 0.82);
1046
+ }
@@ -13,6 +13,7 @@ export interface ComponentRegistry {
13
13
  AutoTable: Component<any>;
14
14
  AutoForm: Component<any>;
15
15
  ShowPage: Component<any>;
16
+ ErrorPage?: Component<any>;
16
17
  Button: Component<any>;
17
18
  Input: Component<any>;
18
19
  Badge: Component<any>;
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  // Auto-inject design tokens so consumers don't need to manually import
3
3
  import '../app.css';
4
- import type { Snippet } from 'svelte';
4
+ import type { Component, Snippet } from 'svelte';
5
5
  import type { DataProvider, AuthProvider, ResourceDefinition, ThemeMode, RouterProvider, ThemeConfig, MenuItem, TaskProvider } from '@svadmin/core';
6
6
  import { setDataProvider, setAuthProvider, setResources, setLocale, setTheme, setRouterProvider, setTaskProvider, createHashRouterProvider, configureTheme } from '@svadmin/core';
7
7
  import { t } from '@svadmin/core/i18n';
@@ -23,6 +23,20 @@
23
23
  import Header from './Header.svelte';
24
24
  import TaskQueueDrawer from './TaskQueueDrawer.svelte';
25
25
  import SettingsPage from './SettingsPage.svelte';
26
+ import ErrorPage from './ErrorPage.svelte';
27
+ import PublicProfilePage from './profile/PublicProfilePage.svelte';
28
+ import GetStartedPage from './account/GetStartedPage.svelte';
29
+ import CompanyProfilePage from './account/CompanyProfilePage.svelte';
30
+ import SettingsPlainPage from './account/SettingsPlainPage.svelte';
31
+ import SettingsSidebarPage from './account/SettingsSidebarPage.svelte';
32
+ import SettingsEnterprisePage from './account/SettingsEnterprisePage.svelte';
33
+ import ImportMembersPage from './account/ImportMembersPage.svelte';
34
+ import MembersStarterPage from './account/MembersStarterPage.svelte';
35
+ import TeamMembersPage from './account/TeamMembersPage.svelte';
36
+ import SecurityLogPage from './account/SecurityLogPage.svelte';
37
+ import UserCardsNFTPage from './network/UserCardsNFTPage.svelte';
38
+ import TeamCrewTablePage from './network/TeamCrewTablePage.svelte';
39
+ import TwoFactorAuthPage from './TwoFactorAuthPage.svelte';
26
40
  import { Button } from './ui/button/index.js';
27
41
  import { Input } from './ui/input/index.js';
28
42
  import { Badge } from './ui/badge/index.js';
@@ -42,8 +56,22 @@
42
56
  themeConfig?: ThemeConfig;
43
57
  dashboard?: Snippet;
44
58
  loginPage?: Snippet;
59
+ /** Prefill and display credentials on the default login page, useful for demos/examples. */
60
+ loginDefaults?: {
61
+ identifier?: string;
62
+ password?: string;
63
+ hint?: string;
64
+ };
45
65
  /** Override default components via DI */
46
66
  components?: Partial<ComponentRegistry>;
67
+ /** Resource-specific page overrides while keeping svadmin defaults as fallback. */
68
+ resourcePages?: Record<string, {
69
+ list?: Component<{ resourceName: string }>;
70
+ create?: Component<{ resourceName: string; mode?: 'create' }>;
71
+ edit?: Component<{ resourceName: string; mode?: 'edit'; id?: string | number }>;
72
+ show?: Component<{ resourceName: string; id?: string | number }>;
73
+ clone?: Component<{ resourceName: string; mode?: 'clone'; id?: string | number }>;
74
+ }>;
47
75
  /** Custom multi-level menu configuration */
48
76
  menu?: MenuItem[];
49
77
  /** External URL to the main application or workspace (renders a shortcut in the header) */
@@ -68,7 +96,9 @@
68
96
  themeConfig: userThemeConfig,
69
97
  dashboard,
70
98
  loginPage,
99
+ loginDefaults,
71
100
  components: userComponents,
101
+ resourcePages,
72
102
  menu,
73
103
  siteUrl,
74
104
  routeMode,
@@ -80,7 +110,7 @@
80
110
  const defaultComponents: ComponentRegistry = {
81
111
  Layout, Sidebar, Header,
82
112
  LoginPage, AutoTable, AutoForm, ShowPage,
83
- TaskQueueDrawer,
113
+ TaskQueueDrawer, ErrorPage,
84
114
  Button: Button as unknown as ComponentRegistry['Button'],
85
115
  Input: Input as unknown as ComponentRegistry['Input'],
86
116
  Badge: Badge as unknown as ComponentRegistry['Badge'],
@@ -134,6 +164,9 @@
134
164
  // Reactive getters for route state
135
165
  const route = $derived(getRoute());
136
166
  const params = $derived(getParams());
167
+ const resourceNames = $derived(new Set(resources.map((resource) => resource.name)));
168
+ const hasRouteResource = $derived(!params.resource || resourceNames.has(params.resource));
169
+ const currentResourcePages = $derived(params.resource ? resourcePages?.[params.resource] : undefined);
137
170
 
138
171
  // Auth check
139
172
  let isAuthenticated = $state(false);
@@ -183,7 +216,13 @@
183
216
  {:else if route === '/login' && loginPage}
184
217
  {@render loginPage()}
185
218
  {:else if route === '/login' && authProvider}
186
- <LoginPage {title} onSuccess={() => { isAuthenticated = true; navigate('/'); }} />
219
+ <LoginPage
220
+ {title}
221
+ defaultIdentifier={loginDefaults?.identifier}
222
+ defaultPassword={loginDefaults?.password}
223
+ loginHint={loginDefaults?.hint}
224
+ onSuccess={() => { isAuthenticated = true; navigate('/'); }}
225
+ />
187
226
  {:else if route === '/register' && authProvider?.register}
188
227
  <RegisterPage {title} />
189
228
  {:else if route === '/forgot-password' && authProvider?.forgotPassword}
@@ -194,10 +233,47 @@
194
233
  <ConfigErrorScreen title="{title} — {t('common.configRequired')}" />
195
234
  {:else if isAuthenticated || !authProvider}
196
235
  <Layout {title} {menu} {siteUrl} routeMode={resolvedRouteMode}>
197
- {#key route + (params.resource ?? '') + (params.id ?? '')}
236
+ {#key route + (params.resource ?? '') + (params.id ?? '') + (params.variant ?? '') + (params.columns ?? '')}
198
237
  <div class="svadmin-page-enter">
199
- {#if route.startsWith('/settings')}
238
+ {#if route === '/2fa'}
239
+ <TwoFactorAuthPage />
240
+ {:else if route === '/public-profile'}
241
+ <PublicProfilePage variant="default" initialTab="projects" />
242
+ {:else if route === '/public-profile/projects/:columns'}
243
+ <PublicProfilePage variant="default" initialTab="projects" columns={params.columns === '3' ? 3 : 2} />
244
+ {:else if route === '/public-profile/activity'}
245
+ <PublicProfilePage variant="default" initialTab="activity" />
246
+ {:else if route === '/public-profile/teams'}
247
+ <PublicProfilePage variant="default" initialTab="teams" />
248
+ {:else if route === '/public-profile/profiles/:variant'}
249
+ <PublicProfilePage variant={(params.variant === 'company' || params.variant === 'gamer' || params.variant === 'default' ? params.variant : 'default') as 'company' | 'gamer' | 'default'} />
250
+ {:else if route === '/account/get-started'}
251
+ <GetStartedPage />
252
+ {:else if route === '/account/company-profile'}
253
+ <CompanyProfilePage />
254
+ {:else if route === '/account/settings-plain'}
255
+ <SettingsPlainPage />
256
+ {:else if route === '/account/settings-sidebar'}
257
+ <SettingsSidebarPage />
258
+ {:else if route === '/account/settings-enterprise'}
259
+ <SettingsEnterprisePage />
260
+ {:else if route === '/account/import-members'}
261
+ <ImportMembersPage />
262
+ {:else if route === '/account/members-starter'}
263
+ <MembersStarterPage />
264
+ {:else if route === '/account/team-members'}
265
+ <TeamMembersPage />
266
+ {:else if route === '/account/security-log'}
267
+ <SecurityLogPage />
268
+ {:else if route === '/network/user-cards'}
269
+ <UserCardsNFTPage />
270
+ {:else if route === '/network/team-crew'}
271
+ <TeamCrewTablePage />
272
+ {:else if route.startsWith('/settings')}
200
273
  <SettingsPage />
274
+ {:else if route === '/500' || (route === '/:resource' && params.resource === '500')}
275
+ {@const ErrorComp = mergedComponents.ErrorPage || ErrorPage}
276
+ <ErrorComp status="500" />
201
277
  {:else if route === '/' || route === ''}
202
278
  {#if dashboard}
203
279
  {@render dashboard()}
@@ -207,31 +283,34 @@
207
283
  <p class="text-muted-foreground">{t('common.dashboardHint')}</p>
208
284
  </div>
209
285
  {/if}
210
- {:else if route === '/:resource' || route === '/:parent/:parentId/:resource'}
286
+ {:else if (route === '/:resource' || route === '/:parent/:parentId/:resource') && hasRouteResource}
211
287
  {#key params.resource}
212
- {@const Comp = mergedComponents.AutoTable}
288
+ {@const Comp = currentResourcePages?.list ?? mergedComponents.AutoTable}
213
289
  <Comp resourceName={params.resource} />
214
290
  {/key}
215
- {:else if route === '/:resource/create' || route === '/:parent/:parentId/:resource/create'}
291
+ {:else if (route === '/:resource/create' || route === '/:parent/:parentId/:resource/create') && hasRouteResource}
216
292
  {#key params.resource}
217
- {@const Comp = mergedComponents.AutoForm}
293
+ {@const Comp = currentResourcePages?.create ?? mergedComponents.AutoForm}
218
294
  <Comp resourceName={params.resource} mode="create" />
219
295
  {/key}
220
- {:else if route === '/:resource/edit/:id' || route === '/:parent/:parentId/:resource/edit/:id'}
296
+ {:else if (route === '/:resource/edit/:id' || route === '/:parent/:parentId/:resource/edit/:id') && hasRouteResource}
221
297
  {#key `${params.resource}-${params.id}`}
222
- {@const Comp = mergedComponents.AutoForm}
298
+ {@const Comp = currentResourcePages?.edit ?? mergedComponents.AutoForm}
223
299
  <Comp resourceName={params.resource} mode="edit" id={params.id} />
224
300
  {/key}
225
- {:else if route === '/:resource/show/:id' || route === '/:parent/:parentId/:resource/show/:id'}
301
+ {:else if (route === '/:resource/show/:id' || route === '/:parent/:parentId/:resource/show/:id') && hasRouteResource}
226
302
  {#key `${params.resource}-${params.id}`}
227
- {@const Comp = mergedComponents.ShowPage}
303
+ {@const Comp = currentResourcePages?.show ?? mergedComponents.ShowPage}
228
304
  <Comp resourceName={params.resource} id={params.id} />
229
305
  {/key}
230
- {:else if route === '/:resource/clone/:id' || route === '/:parent/:parentId/:resource/clone/:id'}
306
+ {:else if (route === '/:resource/clone/:id' || route === '/:parent/:parentId/:resource/clone/:id') && hasRouteResource}
231
307
  {#key `${params.resource}-clone-${params.id}`}
232
- {@const Comp = mergedComponents.AutoForm}
308
+ {@const Comp = currentResourcePages?.clone ?? mergedComponents.AutoForm}
233
309
  <Comp resourceName={params.resource} mode="clone" id={params.id} />
234
310
  {/key}
311
+ {:else}
312
+ {@const ErrorComp = mergedComponents.ErrorPage || ErrorPage}
313
+ <ErrorComp status="404" />
235
314
  {/if}
236
315
  </div>
237
316
  {/key}
@@ -1,5 +1,5 @@
1
1
  import '../app.css';
2
- import type { Snippet } from 'svelte';
2
+ import type { Component, Snippet } from 'svelte';
3
3
  import type { DataProvider, AuthProvider, ResourceDefinition, ThemeMode, RouterProvider, ThemeConfig, MenuItem, TaskProvider } from '@svadmin/core';
4
4
  import { QueryClient, type DefaultOptions } from '@tanstack/svelte-query';
5
5
  import { type ComponentRegistry } from '../component-registry.svelte.js';
@@ -16,8 +16,38 @@ interface Props {
16
16
  themeConfig?: ThemeConfig;
17
17
  dashboard?: Snippet;
18
18
  loginPage?: Snippet;
19
+ /** Prefill and display credentials on the default login page, useful for demos/examples. */
20
+ loginDefaults?: {
21
+ identifier?: string;
22
+ password?: string;
23
+ hint?: string;
24
+ };
19
25
  /** Override default components via DI */
20
26
  components?: Partial<ComponentRegistry>;
27
+ /** Resource-specific page overrides while keeping svadmin defaults as fallback. */
28
+ resourcePages?: Record<string, {
29
+ list?: Component<{
30
+ resourceName: string;
31
+ }>;
32
+ create?: Component<{
33
+ resourceName: string;
34
+ mode?: 'create';
35
+ }>;
36
+ edit?: Component<{
37
+ resourceName: string;
38
+ mode?: 'edit';
39
+ id?: string | number;
40
+ }>;
41
+ show?: Component<{
42
+ resourceName: string;
43
+ id?: string | number;
44
+ }>;
45
+ clone?: Component<{
46
+ resourceName: string;
47
+ mode?: 'clone';
48
+ id?: string | number;
49
+ }>;
50
+ }>;
21
51
  /** Custom multi-level menu configuration */
22
52
  menu?: MenuItem[];
23
53
  /** External URL to the main application or workspace (renders a shortcut in the header) */
@@ -29,6 +59,6 @@ interface Props {
29
59
  /** Optional TanStack Query default options override when QueryClient is not supplied. */
30
60
  queryClientDefaultOptions?: DefaultOptions;
31
61
  }
32
- declare const AdminApp: import("svelte").Component<Props, {}, "">;
62
+ declare const AdminApp: Component<Props, {}, "">;
33
63
  type AdminApp = ReturnType<typeof AdminApp>;
34
64
  export default AdminApp;