@kyro-cms/admin 0.12.6 → 0.12.8
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/index.cjs +32 -110
- package/dist/index.css +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +32 -110
- package/package.json +11 -5
- package/src/components/AuthBridge.tsx +2 -2
- package/src/components/AutoForm.tsx +49 -57
- package/src/components/BrandingHub.tsx +1 -1
- package/src/components/DashboardMetrics.tsx +519 -290
- package/src/components/DetailView.tsx +5 -3
- package/src/components/FieldRenderer.tsx +59 -47
- package/src/components/ListView.tsx +25 -10
- package/src/components/MediaGallery.tsx +1 -1
- package/src/components/PluginsManager.tsx +39 -21
- package/src/components/Sidebar.astro +7 -14
- package/src/components/UserMenu.tsx +10 -9
- package/src/components/autoform/AutoFormApiView.tsx +1 -1
- package/src/components/autoform/AutoFormHeader.tsx +1 -1
- package/src/components/autoform/ErrorBoundary.tsx +1 -1
- package/src/components/blocks/AccordionBlock.tsx +8 -4
- package/src/components/blocks/ArrayBlock.tsx +4 -3
- package/src/components/blocks/BlockEditModal.tsx +4 -3
- package/src/components/blocks/CardBlock.tsx +10 -2
- package/src/components/blocks/ChildBlocksTree.tsx +19 -18
- package/src/components/blocks/CodeBlock.tsx +7 -2
- package/src/components/blocks/FileBlock.tsx +6 -2
- package/src/components/blocks/GenericBlock.tsx +1 -1
- package/src/components/blocks/HeadingBlock.tsx +6 -2
- package/src/components/blocks/HeadingSubheadingBlock.tsx +7 -2
- package/src/components/blocks/HeroBlock.tsx +12 -3
- package/src/components/blocks/ImageBlock.tsx +8 -2
- package/src/components/blocks/ListBlock.tsx +6 -2
- package/src/components/blocks/ParagraphBlock.tsx +2 -2
- package/src/components/blocks/RelationshipBlock.tsx +10 -2
- package/src/components/blocks/VideoBlock.tsx +7 -2
- package/src/components/fields/AccordionField.tsx +1 -1
- package/src/components/fields/ArrayLayout.tsx +55 -58
- package/src/components/fields/BlocksField.tsx +1 -1
- package/src/components/fields/ChildrenField.tsx +3 -2
- package/src/components/fields/CodeField.tsx +3 -2
- package/src/components/fields/ColumnsField.tsx +3 -2
- package/src/components/fields/DateField.tsx +2 -2
- package/src/components/fields/FieldLayout.tsx +3 -3
- package/src/components/fields/GroupLayout.tsx +2 -2
- package/src/components/fields/IconField.tsx +1 -1
- package/src/components/fields/MarkdownField.tsx +2 -2
- package/src/components/fields/NumberField.tsx +4 -4
- package/src/components/fields/RelationshipField.tsx +4 -1
- package/src/components/fields/RichTextField.tsx +200 -5
- package/src/components/fields/extensions/blockComponents.tsx +1 -1
- package/src/components/fields/extensions/blocksStore.ts +15 -12
- package/src/components/ui/Button.tsx +5 -2
- package/src/components/ui/Pagination.tsx +1 -1
- package/src/components/users/UserDetail.tsx +7 -7
- package/src/components/users/UserForm.tsx +3 -2
- package/src/components/users/UsersList.tsx +5 -4
- package/src/env.d.ts +4 -0
- package/src/fields/index.ts +1 -1
- package/src/hooks/useAutoFormState.ts +7 -6
- package/src/hooks/useQueue.ts +3 -2
- package/src/index.ts +0 -1
- package/src/integration.ts +50 -12
- package/src/kyro-cms.d.ts +5 -0
- package/src/lib/api.ts +1 -0
- package/src/lib/autoform-store.ts +4 -3
- package/src/lib/config.ts +6 -19
- package/src/lib/core-types.ts +78 -0
- package/src/lib/normalize-upload-fields.ts +17 -4
- package/src/pages/[collection]/index.astro +1 -1
- package/src/pages/users/index.astro +1 -1
- package/src/plugins/seo-admin.tsx +155 -0
- package/src/styles/main.css +2 -0
- package/src/vite-env.d.ts +10 -1
- package/src/components/fix_imports.cjs +0 -23
- package/src/components/fix_imports2.cjs +0 -19
- package/src/components/replace_svgs.cjs +0 -63
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kyro-cms/admin",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.8",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=22"
|
|
6
6
|
},
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"import": "./src/plugins/index.ts",
|
|
31
31
|
"default": "./src/plugins/index.ts"
|
|
32
32
|
},
|
|
33
|
+
"./plugins/seo-admin": {
|
|
34
|
+
"import": "./src/plugins/seo-admin.tsx",
|
|
35
|
+
"default": "./src/plugins/seo-admin.tsx"
|
|
36
|
+
},
|
|
33
37
|
"./blocks": {
|
|
34
38
|
"import": "./src/blocks/index.ts",
|
|
35
39
|
"default": "./src/blocks/index.ts"
|
|
@@ -57,8 +61,8 @@
|
|
|
57
61
|
"prepare": "npm run build"
|
|
58
62
|
},
|
|
59
63
|
"dependencies": {
|
|
60
|
-
"@astrojs/node": "^
|
|
61
|
-
"@astrojs/react": "^
|
|
64
|
+
"@astrojs/node": "^11.0.2",
|
|
65
|
+
"@astrojs/react": "^6.0.1",
|
|
62
66
|
"@codemirror/autocomplete": "^6.20.3",
|
|
63
67
|
"@codemirror/lang-cpp": "^6.0.3",
|
|
64
68
|
"@codemirror/lang-css": "^6.3.1",
|
|
@@ -77,7 +81,7 @@
|
|
|
77
81
|
"@dnd-kit/sortable": "^10.0.0",
|
|
78
82
|
"@dnd-kit/utilities": "^3.2.2",
|
|
79
83
|
"@graphiql/react": "^0.37.3",
|
|
80
|
-
"@kyro-cms/core": "
|
|
84
|
+
"@kyro-cms/core": "^0.12.7",
|
|
81
85
|
"@tailwindcss/vite": "^4.0.0",
|
|
82
86
|
"@tiptap/extension-color": "^3.23.6",
|
|
83
87
|
"@tiptap/extension-highlight": "^3.23.6",
|
|
@@ -96,7 +100,7 @@
|
|
|
96
100
|
"@uiw/codemirror-theme-dracula": "^4.25.9",
|
|
97
101
|
"@uiw/codemirror-theme-github": "^4.25.9",
|
|
98
102
|
"@uiw/react-codemirror": "^4.25.9",
|
|
99
|
-
"astro": "^
|
|
103
|
+
"astro": "^7.1.1",
|
|
100
104
|
"astro-loading-indicator": "^0.8.0",
|
|
101
105
|
"dotenv": "^17.4.2",
|
|
102
106
|
"graphiql": "^5.2.2",
|
|
@@ -104,6 +108,7 @@
|
|
|
104
108
|
"i18next-browser-languagedetector": "^8.2.1",
|
|
105
109
|
"idb-keyval": "^6.2.2",
|
|
106
110
|
"lucide-react": "^0.475.0",
|
|
111
|
+
"marked": "^18.0.6",
|
|
107
112
|
"react": "^19.0.0",
|
|
108
113
|
"react-dom": "^19.0.0",
|
|
109
114
|
"react-i18next": "^17.0.8",
|
|
@@ -123,6 +128,7 @@
|
|
|
123
128
|
"i18next-parser": "^9.4.0",
|
|
124
129
|
"tsup": "^8.5.1",
|
|
125
130
|
"typescript": "^5.7.3",
|
|
131
|
+
"vite": "^8.1.5",
|
|
126
132
|
"vitest": "^4.1.4"
|
|
127
133
|
},
|
|
128
134
|
"peerDependencies": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
|
-
import { useAuthStore } from "../lib/stores";
|
|
2
|
+
import { useAuthStore, type AuthUser, type Permissions } from "../lib/stores";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* AuthBridge
|
|
@@ -24,7 +24,7 @@ export function AuthBridge() {
|
|
|
24
24
|
|
|
25
25
|
function populate(detail: { user: unknown; permissions: unknown }) {
|
|
26
26
|
if (detail?.user) {
|
|
27
|
-
setUser(detail.user, detail.permissions ?? null);
|
|
27
|
+
setUser(detail.user as AuthUser, (detail.permissions ?? null) as Permissions | null);
|
|
28
28
|
} else {
|
|
29
29
|
// Unauthenticated – set loading to false so components don't hang
|
|
30
30
|
setLoading(false);
|
|
@@ -1,41 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Check, AlertTriangle } from "./ui/icons";
|
|
2
2
|
import { useState, useRef, useEffect } from "react";
|
|
3
3
|
import type {
|
|
4
4
|
CollectionConfig,
|
|
5
5
|
GlobalConfig,
|
|
6
6
|
Field,
|
|
7
|
-
Block,
|
|
8
7
|
} from "@kyro-cms/core/client";
|
|
8
|
+
import type { DeclarativeCondition } from "../lib/core-types";
|
|
9
9
|
|
|
10
10
|
type View = "edit" | "version" | "api";
|
|
11
|
-
import { UploadField } from "./fields/UploadField";
|
|
12
|
-
import { CodeField } from "./fields";
|
|
13
|
-
import NumberField from "./fields/NumberField";
|
|
14
|
-
import CheckboxField from "./fields/CheckboxField";
|
|
15
|
-
import SelectField from "./fields/SelectField";
|
|
16
|
-
import DateField from "./fields/DateField";
|
|
17
|
-
import { MarkdownField } from "./fields/MarkdownField";
|
|
18
|
-
import TextField from "./fields/TextField";
|
|
19
11
|
import { globals, collections } from "../lib/config";
|
|
20
|
-
import { slugifyText } from "../lib/slugify";
|
|
21
12
|
import { resolveUrl, apiGet, apiDelete, fetchWithAuth } from "../lib/api";
|
|
13
|
+
import { EmptyState } from "./ui/EmptyState";
|
|
14
|
+
import { navigate } from "astro:transitions/client";
|
|
22
15
|
import { Shimmer } from "./ui/Shimmer";
|
|
23
16
|
import { normalizeUploadFields } from "../lib/normalize-upload-fields";
|
|
24
17
|
import { useAutoFormStore } from "../lib/autoform-store";
|
|
25
18
|
import { useAutoFormState } from "../hooks/useAutoFormState";
|
|
26
19
|
import { useUIStore, toast } from "../lib/stores";
|
|
27
|
-
import { EmptyState } from "./ui/EmptyState";
|
|
28
20
|
|
|
29
|
-
import { adminPath as ADMIN_BASE
|
|
21
|
+
import { adminPath as ADMIN_BASE } from "../lib/paths";
|
|
30
22
|
|
|
31
|
-
import { BlocksField } from "./fields/BlocksField";
|
|
32
|
-
import { ConfirmModal, Modal as UIModal } from "./ui/Modal";
|
|
33
|
-
import { ListField } from "./fields/ListField";
|
|
34
23
|
import { RelationshipBlockField } from "./fields/RelationshipBlockField";
|
|
35
24
|
import { FieldRenderer } from "./FieldRenderer";
|
|
36
|
-
import { Dropdown, DropdownItem, DropdownSeparator } from "./ui/Dropdown";
|
|
37
|
-
import { SplitButton } from "./ui/SplitButton";
|
|
38
|
-
import type { SplitButtonStatus } from "./ui/SplitButton";
|
|
39
25
|
import { TabsLayout } from "./fields/TabsLayout";
|
|
40
26
|
import { GroupLayout } from "./fields/GroupLayout";
|
|
41
27
|
import { ArrayLayout } from "./fields/ArrayLayout";
|
|
@@ -62,28 +48,33 @@ interface AutoFormProps {
|
|
|
62
48
|
documentStatus?: string;
|
|
63
49
|
}
|
|
64
50
|
|
|
65
|
-
function getNestedValue(obj:
|
|
51
|
+
function getNestedValue(obj: unknown, path: string): unknown {
|
|
66
52
|
if (!obj || typeof path !== "string") return undefined;
|
|
67
|
-
return path.split('.').reduce((acc, part) =>
|
|
53
|
+
return path.split('.').reduce<unknown>((acc, part) => {
|
|
54
|
+
if (acc && typeof acc === "object" && part in acc) {
|
|
55
|
+
return (acc as Record<string, unknown>)[part];
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}, obj);
|
|
68
59
|
}
|
|
69
60
|
|
|
70
|
-
function evaluateDeclarativeCondition(cond:
|
|
61
|
+
function evaluateDeclarativeCondition(cond: DeclarativeCondition | undefined, currentData: Record<string, unknown>, formData: Record<string, unknown>): boolean {
|
|
71
62
|
if (!cond) return true;
|
|
72
|
-
|
|
73
|
-
if (Array.isArray(cond.and)) {
|
|
74
|
-
return cond.and.every((c:
|
|
63
|
+
|
|
64
|
+
if ("and" in cond && Array.isArray(cond.and)) {
|
|
65
|
+
return cond.and.every((c: DeclarativeCondition) => evaluateDeclarativeCondition(c, currentData, formData));
|
|
75
66
|
}
|
|
76
|
-
if (Array.isArray(cond.or)) {
|
|
77
|
-
return cond.or.some((c:
|
|
67
|
+
if ("or" in cond && Array.isArray(cond.or)) {
|
|
68
|
+
return cond.or.some((c: DeclarativeCondition) => evaluateDeclarativeCondition(c, currentData, formData));
|
|
78
69
|
}
|
|
79
|
-
|
|
80
|
-
if (cond.field) {
|
|
70
|
+
|
|
71
|
+
if ("field" in cond && cond.field) {
|
|
81
72
|
const targetField = cond.field;
|
|
82
73
|
let val = getNestedValue(currentData, targetField);
|
|
83
74
|
if (val === undefined) {
|
|
84
75
|
val = getNestedValue(formData, targetField);
|
|
85
76
|
}
|
|
86
|
-
|
|
77
|
+
|
|
87
78
|
if ("equals" in cond) {
|
|
88
79
|
return val === cond.equals;
|
|
89
80
|
}
|
|
@@ -91,16 +82,16 @@ function evaluateDeclarativeCondition(cond: any, currentData: any, formData: any
|
|
|
91
82
|
return val !== cond.notEquals;
|
|
92
83
|
}
|
|
93
84
|
if ("in" in cond && Array.isArray(cond.in)) {
|
|
94
|
-
return cond.in.includes(val);
|
|
85
|
+
return cond.in.includes(val as string | number | boolean);
|
|
95
86
|
}
|
|
96
|
-
if ("greaterThan" in cond) {
|
|
87
|
+
if ("greaterThan" in cond && cond.greaterThan !== undefined) {
|
|
97
88
|
return typeof val === "number" && val > cond.greaterThan;
|
|
98
89
|
}
|
|
99
|
-
|
|
90
|
+
|
|
100
91
|
// If field exists but no operator is provided, just check truthiness
|
|
101
92
|
return Boolean(val);
|
|
102
93
|
}
|
|
103
|
-
|
|
94
|
+
|
|
104
95
|
return true;
|
|
105
96
|
}
|
|
106
97
|
|
|
@@ -127,14 +118,15 @@ export function AutoForm({
|
|
|
127
118
|
: collectionSlug
|
|
128
119
|
? collections[collectionSlug]
|
|
129
120
|
: null);
|
|
130
|
-
|
|
131
|
-
const [liveConfig, setLiveConfig] = useState<
|
|
132
|
-
|
|
121
|
+
|
|
122
|
+
const [liveConfig, setLiveConfig] = useState<CollectionConfig | GlobalConfig | null>(activeConfig);
|
|
123
|
+
|
|
133
124
|
useEffect(() => {
|
|
134
|
-
if (globalSlug
|
|
135
|
-
apiGet("/api/kyro/schema").then((schema:
|
|
136
|
-
|
|
137
|
-
|
|
125
|
+
if (globalSlug && !activeConfig) {
|
|
126
|
+
apiGet("/api/kyro/schema").then((schema: unknown) => {
|
|
127
|
+
const schemaTyped = schema as { globals?: Record<string, GlobalConfig>; collections?: Record<string, CollectionConfig> };
|
|
128
|
+
if (schemaTyped?.globals?.[globalSlug]) {
|
|
129
|
+
setLiveConfig(schemaTyped.globals[globalSlug]);
|
|
138
130
|
}
|
|
139
131
|
}).catch(err => console.error("[AutoForm] Failed to fetch dynamic schema", err));
|
|
140
132
|
} else {
|
|
@@ -436,11 +428,11 @@ export function AutoForm({
|
|
|
436
428
|
onConfirm: async () => {
|
|
437
429
|
await handleSaveDraft();
|
|
438
430
|
await new Promise((r) => setTimeout(r, 1000));
|
|
439
|
-
|
|
431
|
+
navigate(`${ADMIN_BASE}/${collectionSlug}/new`);
|
|
440
432
|
},
|
|
441
433
|
});
|
|
442
434
|
} else {
|
|
443
|
-
|
|
435
|
+
navigate(`${ADMIN_BASE}/${collectionSlug}/new`);
|
|
444
436
|
}
|
|
445
437
|
};
|
|
446
438
|
|
|
@@ -461,9 +453,9 @@ export function AutoForm({
|
|
|
461
453
|
const result = await response.json();
|
|
462
454
|
onActionSuccess?.("Document duplicated successfully");
|
|
463
455
|
if (result.data?.id) {
|
|
464
|
-
|
|
456
|
+
navigate(`${ADMIN_BASE}/${collectionSlug}/${result.data.id}`);
|
|
465
457
|
} else {
|
|
466
|
-
|
|
458
|
+
navigate(`${ADMIN_BASE}/${collectionSlug}`);
|
|
467
459
|
}
|
|
468
460
|
} else {
|
|
469
461
|
const err = await response.json();
|
|
@@ -506,7 +498,7 @@ export function AutoForm({
|
|
|
506
498
|
autoSaveSkipRef.current = true;
|
|
507
499
|
try {
|
|
508
500
|
await apiDelete(`/api/${collectionSlug}/${formData.id}`);
|
|
509
|
-
|
|
501
|
+
navigate(`${ADMIN_BASE}/${collectionSlug}`);
|
|
510
502
|
} catch (err) {
|
|
511
503
|
toast.error((err as Error).message || "Failed to delete document");
|
|
512
504
|
} finally {
|
|
@@ -553,7 +545,7 @@ export function AutoForm({
|
|
|
553
545
|
setLocalSaveStatus("saving");
|
|
554
546
|
|
|
555
547
|
try {
|
|
556
|
-
const data = normalizeUploadFields({ ...formData }) as Record<string, unknown>;
|
|
548
|
+
const data = normalizeUploadFields({ ...formData }, true) as Record<string, unknown>;
|
|
557
549
|
const isPost = isNewDoc && !globalSlug;
|
|
558
550
|
|
|
559
551
|
const response = isPost
|
|
@@ -584,7 +576,7 @@ export function AutoForm({
|
|
|
584
576
|
}
|
|
585
577
|
if (isPost) {
|
|
586
578
|
setTimeout(() => {
|
|
587
|
-
|
|
579
|
+
navigate(`${ADMIN_BASE}/${collectionSlug}/${result.data.id}`);
|
|
588
580
|
}, 800);
|
|
589
581
|
}
|
|
590
582
|
} else {
|
|
@@ -616,7 +608,7 @@ export function AutoForm({
|
|
|
616
608
|
|
|
617
609
|
if (isNewDoc && !globalSlug) {
|
|
618
610
|
// Create then immediately publish
|
|
619
|
-
const data = normalizeUploadFields({ ...formData }) as Record<string, unknown>;
|
|
611
|
+
const data = normalizeUploadFields({ ...formData }, true) as Record<string, unknown>;
|
|
620
612
|
const response = await fetchWithAuth(`/api/${collectionSlug}`, {
|
|
621
613
|
method: "POST",
|
|
622
614
|
headers: { "Content-Type": "application/json" },
|
|
@@ -637,7 +629,7 @@ export function AutoForm({
|
|
|
637
629
|
}
|
|
638
630
|
|
|
639
631
|
// Save and publish (X-Draft: false writes to main doc + versions table)
|
|
640
|
-
const data = normalizeUploadFields(dataToPublish) as Record<string, unknown>;
|
|
632
|
+
const data = normalizeUploadFields(dataToPublish, true) as Record<string, unknown>;
|
|
641
633
|
const response = await saveDocument(data, false);
|
|
642
634
|
|
|
643
635
|
if (response?.ok) {
|
|
@@ -646,14 +638,14 @@ export function AutoForm({
|
|
|
646
638
|
useAutoFormStore.getState().loadDocument(savedData, savedData);
|
|
647
639
|
setLocalSaveStatus("saved");
|
|
648
640
|
onActionSuccess?.("Published successfully");
|
|
649
|
-
|
|
641
|
+
|
|
650
642
|
setTimeout(() => {
|
|
651
643
|
setLocalSaveStatus("idle");
|
|
652
644
|
}, 2000);
|
|
653
645
|
|
|
654
646
|
if (isNewDoc && !globalSlug && dataToPublish.id) {
|
|
655
647
|
setTimeout(() => {
|
|
656
|
-
|
|
648
|
+
navigate(`${ADMIN_BASE}/${collectionSlug}/${dataToPublish.id}`);
|
|
657
649
|
}, 800);
|
|
658
650
|
}
|
|
659
651
|
} else {
|
|
@@ -663,7 +655,7 @@ export function AutoForm({
|
|
|
663
655
|
const error = await response?.json().catch(() => ({}));
|
|
664
656
|
toast.warning("Document saved as draft. Publishing failed: " + (error?.error || "Unknown error"));
|
|
665
657
|
setTimeout(() => {
|
|
666
|
-
|
|
658
|
+
navigate(`${ADMIN_BASE}/${collectionSlug}/${dataToPublish.id}`);
|
|
667
659
|
}, 1200);
|
|
668
660
|
return;
|
|
669
661
|
}
|
|
@@ -689,7 +681,7 @@ export function AutoForm({
|
|
|
689
681
|
|
|
690
682
|
try {
|
|
691
683
|
const data = {
|
|
692
|
-
...normalizeUploadFields({ ...formData }) as Record<string, unknown>,
|
|
684
|
+
...normalizeUploadFields({ ...formData }, true) as Record<string, unknown>,
|
|
693
685
|
_schedulePublishAt: scheduledFor,
|
|
694
686
|
};
|
|
695
687
|
|
|
@@ -729,7 +721,7 @@ export function AutoForm({
|
|
|
729
721
|
const renderField = (
|
|
730
722
|
field: Field,
|
|
731
723
|
parentData?: Record<string, unknown>,
|
|
732
|
-
onParentChange?: (val: unknown) => void,
|
|
724
|
+
onParentChange?: (val: Record<string, unknown>) => void,
|
|
733
725
|
): React.ReactNode => {
|
|
734
726
|
const currentData = parentData !== undefined ? parentData : formData;
|
|
735
727
|
const isHidden = resolveAdminFlag((field.hidden !== undefined ? field.hidden : field.admin?.hidden) as any, currentData);
|
|
@@ -756,7 +748,7 @@ export function AutoForm({
|
|
|
756
748
|
}
|
|
757
749
|
} else if (typeof field.admin.condition === "object") {
|
|
758
750
|
try {
|
|
759
|
-
const shouldShow = evaluateDeclarativeCondition(field.admin.condition, currentData, formData);
|
|
751
|
+
const shouldShow = evaluateDeclarativeCondition(field.admin.condition as DeclarativeCondition, currentData, formData);
|
|
760
752
|
if (!shouldShow) {
|
|
761
753
|
return null;
|
|
762
754
|
}
|
|
@@ -1018,7 +1010,7 @@ export function AutoForm({
|
|
|
1018
1010
|
}
|
|
1019
1011
|
};
|
|
1020
1012
|
|
|
1021
|
-
if (clientLoading) {
|
|
1013
|
+
if (clientLoading || !config) {
|
|
1022
1014
|
return (
|
|
1023
1015
|
<div className="space-y-6 p-4">
|
|
1024
1016
|
<div className="space-y-2">
|
|
@@ -59,7 +59,7 @@ export function BrandingHub() {
|
|
|
59
59
|
"--kyro-primary",
|
|
60
60
|
primaryColor,
|
|
61
61
|
);
|
|
62
|
-
setTimeout(() => window.
|
|
62
|
+
setTimeout(() => window.dispatchEvent(new Event('kyro:soft-reload')), 800);
|
|
63
63
|
} catch (e) {
|
|
64
64
|
toast.error("Failed to save branding");
|
|
65
65
|
console.error(e);
|