@kyro-cms/admin 0.12.3 → 0.12.5

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 (110) hide show
  1. package/dist/index.cjs +64 -26
  2. package/dist/index.d.cts +2 -1
  3. package/dist/index.d.ts +2 -1
  4. package/dist/index.js +64 -26
  5. package/package.json +8 -2
  6. package/src/components/ActionBar.tsx +16 -16
  7. package/src/components/ApiHealth.tsx +522 -0
  8. package/src/components/ApiKeysManager.tsx +6 -4
  9. package/src/components/AuditLogsPage.tsx +4 -2
  10. package/src/components/AutoForm.tsx +10 -7
  11. package/src/components/BrandingHub.tsx +3 -1
  12. package/src/components/Dashboard.tsx +44 -37
  13. package/src/components/DashboardMetrics.tsx +168 -58
  14. package/src/components/DetailView.tsx +125 -50
  15. package/src/components/DeveloperCenter.tsx +5 -3
  16. package/src/components/GraphQLPlayground.tsx +10 -8
  17. package/src/components/ListView.tsx +66 -45
  18. package/src/components/LoginPage.tsx +21 -19
  19. package/src/components/MarketplaceManager.tsx +3 -1
  20. package/src/components/MediaGallery.tsx +12 -9
  21. package/src/components/PluginsManager.tsx +6 -4
  22. package/src/components/RestPlayground.tsx +8 -6
  23. package/src/components/SessionsManager.tsx +3 -1
  24. package/src/components/Sidebar.astro +180 -92
  25. package/src/components/UserManagement.tsx +8 -6
  26. package/src/components/UserMenu.tsx +19 -17
  27. package/src/components/VersionHistoryPanel.tsx +6 -4
  28. package/src/components/WebhookManager.tsx +143 -15
  29. package/src/components/autoform/AutoFormApiView.tsx +3 -1
  30. package/src/components/autoform/AutoFormEditView.tsx +5 -3
  31. package/src/components/autoform/AutoFormHeader.tsx +6 -6
  32. package/src/components/blocks/AccordionBlock.tsx +5 -3
  33. package/src/components/blocks/ArrayBlock.tsx +5 -3
  34. package/src/components/blocks/BlockWrapper.tsx +5 -3
  35. package/src/components/blocks/CodeBlock.tsx +4 -2
  36. package/src/components/blocks/FileBlock.tsx +5 -3
  37. package/src/components/blocks/HeroBlock.tsx +5 -3
  38. package/src/components/blocks/ImageBlock.tsx +6 -4
  39. package/src/components/blocks/ListBlock.tsx +5 -3
  40. package/src/components/blocks/ParagraphBlock.tsx +3 -1
  41. package/src/components/blocks/RelationshipBlock.tsx +5 -3
  42. package/src/components/blocks/RichTextBlock.tsx +5 -3
  43. package/src/components/blocks/VideoBlock.tsx +5 -3
  44. package/src/components/fields/AccordionField.tsx +10 -51
  45. package/src/components/fields/ArrayField.tsx +9 -7
  46. package/src/components/fields/ArrayLayout.tsx +3 -1
  47. package/src/components/fields/BlocksField.tsx +24 -75
  48. package/src/components/fields/ButtonField.tsx +3 -1
  49. package/src/components/fields/CardField.tsx +6 -4
  50. package/src/components/fields/GroupLayout.tsx +2 -49
  51. package/src/components/fields/HeadingField.tsx +3 -1
  52. package/src/components/fields/HeadingSubheadingField.tsx +4 -2
  53. package/src/components/fields/HeroField.tsx +8 -6
  54. package/src/components/fields/JSONField.tsx +9 -10
  55. package/src/components/fields/LinkField.tsx +4 -2
  56. package/src/components/fields/ListField.tsx +3 -1
  57. package/src/components/fields/MarkdownField.tsx +3 -17
  58. package/src/components/fields/RelationshipBlockField.tsx +7 -5
  59. package/src/components/fields/RelationshipField.tsx +3 -1
  60. package/src/components/fields/RichTextField.tsx +19 -16
  61. package/src/components/fields/SecretField.tsx +9 -69
  62. package/src/components/fields/SelectField.tsx +11 -6
  63. package/src/components/fields/TabsLayout.tsx +2 -48
  64. package/src/components/fields/TextField.tsx +9 -7
  65. package/src/components/fields/UploadField.tsx +3 -1
  66. package/src/components/fields/VideoField.tsx +6 -4
  67. package/src/components/ui/BlockDrawer.tsx +3 -1
  68. package/src/components/ui/CommandPalette.tsx +9 -7
  69. package/src/components/ui/CommandPaletteWrapper.tsx +3 -1
  70. package/src/components/ui/IconPickerModal.tsx +4 -2
  71. package/src/components/ui/PageHeader.tsx +48 -20
  72. package/src/components/users/UserDetail.tsx +3 -1
  73. package/src/components/users/UserForm.tsx +5 -3
  74. package/src/fields/examples/sample-field-2.tsx +3 -1
  75. package/src/fields/examples/sample-field.tsx +3 -1
  76. package/src/hooks/useAutoFormState.ts +19 -11
  77. package/src/integration.ts +21 -0
  78. package/src/layouts/AdminLayout.astro +252 -189
  79. package/src/layouts/AuthLayout.astro +52 -31
  80. package/src/lib/api.ts +7 -7
  81. package/src/lib/autoform-store.ts +4 -0
  82. package/src/lib/deep-equal.ts +3 -3
  83. package/src/lib/i18n.ts +44 -0
  84. package/src/lib/vite-shim-plugin.ts +3 -0
  85. package/src/locales/de.json +163 -0
  86. package/src/locales/en.json +163 -0
  87. package/src/locales/es.json +163 -0
  88. package/src/locales/fr.json +163 -0
  89. package/src/locales/pt.json +163 -0
  90. package/src/pages/403.astro +2 -1
  91. package/src/pages/[collection]/[id].astro +23 -10
  92. package/src/pages/[collection]/index.astro +48 -34
  93. package/src/pages/audit/index.astro +2 -1
  94. package/src/pages/graphql-explorer.astro +2 -1
  95. package/src/pages/graphql.astro +2 -1
  96. package/src/pages/health.astro +9 -0
  97. package/src/pages/index.astro +6 -5
  98. package/src/pages/keys.astro +2 -1
  99. package/src/pages/marketplace.astro +2 -1
  100. package/src/pages/media.astro +2 -1
  101. package/src/pages/plugins.astro +2 -1
  102. package/src/pages/rest-playground.astro +2 -1
  103. package/src/pages/roles/index.astro +16 -52
  104. package/src/pages/sessions.astro +2 -1
  105. package/src/pages/settings/[slug].astro +45 -30
  106. package/src/pages/users/[id].astro +1 -0
  107. package/src/pages/users/index.astro +2 -1
  108. package/src/pages/users/new.astro +2 -1
  109. package/src/pages/webhooks.astro +2 -1
  110. package/src/pages/index_ALT.astro +0 -3
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  import AdminLayout from '../../layouts/AdminLayout.astro';
3
+ import i18next from '../../lib/i18n';
3
4
  import { AuditLogsPage } from '../../components/AuditLogsPage';
4
5
 
5
6
  import { adminPath, apiPath } from '../../lib/paths';
6
7
  ---
7
8
 
8
- <AdminLayout title="Audit Logs">
9
+ <AdminLayout title={i18next.t("pages." + "Audit Logs".replace(/ /g, ""), { defaultValue: "Audit Logs" })}>
9
10
  <AuditLogsPage client:only="react" />
10
11
  </AdminLayout>
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import AdminLayout from "../layouts/AdminLayout.astro";
3
+ import i18next from '../lib/i18n';
3
4
  import { GraphQLPlayground } from "../components/GraphQLPlayground";
4
5
  import { apiPath } from "../lib/paths";
5
6
  import { getGlobal } from "../lib/globals";
@@ -9,7 +10,7 @@ const apiAccess = accessSettings?.apiAccess || {};
9
10
  const graphqlEnabled = apiAccess.graphqlEnabled !== false;
10
11
  ---
11
12
 
12
- <AdminLayout title="GraphQL Explorer">
13
+ <AdminLayout title={i18next.t("pages." + "GraphQL Explorer".replace(/ /g, ""), { defaultValue: "GraphQL Explorer" })}>
13
14
  <div class="flex-1 overflow-hidden h-[calc(100vh-64px)] md:h-[calc(100vh-80px)]">
14
15
  {graphqlEnabled ? (
15
16
  <GraphQLPlayground
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import AdminLayout from "../layouts/AdminLayout.astro";
3
+ import i18next from '../lib/i18n';
3
4
  import { GraphQLPlayground } from "../components/GraphQLPlayground";
4
5
  import { apiPath } from "../lib/paths";
5
6
  import { getGlobal } from "../lib/globals";
@@ -13,7 +14,7 @@ const params = new URL(url).searchParams;
13
14
  const initialQuery = params.get("query") || "";
14
15
  ---
15
16
 
16
- <AdminLayout title="GraphQL Playground">
17
+ <AdminLayout title={i18next.t("pages." + "GraphQL Playground".replace(/ /g, ""), { defaultValue: "GraphQL Playground" })}>
17
18
  <div class="flex-1 overflow-hidden h-[calc(100vh-64px)] md:h-[calc(100vh-80px)]">
18
19
  {graphqlEnabled ? (
19
20
  <GraphQLPlayground
@@ -0,0 +1,9 @@
1
+ ---
2
+ import AdminLayout from "../layouts/AdminLayout.astro";
3
+ import i18next from '../lib/i18n';
4
+ import { ApiHealth } from "../components/ApiHealth";
5
+ ---
6
+
7
+ <AdminLayout title={i18next.t("pages." + "API Health".replace(/ /g, ""), { defaultValue: "API Health" })}>
8
+ <ApiHealth client:only="react" />
9
+ </AdminLayout>
@@ -1,9 +1,10 @@
1
1
  ---
2
2
  import AdminLayout from "../layouts/AdminLayout.astro";
3
+ import i18next from '../lib/i18n';
3
4
  import { collections } from "../lib/config";
4
5
  const authCollections = ["users", "audit_logs"];
5
6
  import { adminPath, apiPath } from "../lib/paths";
6
- import { DashboardMetrics } from "../components/DashboardMetrics";
7
+ import { DashboardMetrics, RevenueChart } from "../components/DashboardMetrics";
7
8
 
8
9
  const authItems = authCollections.map((slug) => ({
9
10
  slug,
@@ -12,7 +13,7 @@ const authItems = authCollections.map((slug) => ({
12
13
  }));
13
14
  ---
14
15
 
15
- <AdminLayout title="Dashboard">
16
+ <AdminLayout title={i18next.t("pages." + "Dashboard".replace(/ /g, ""), { defaultValue: "Dashboard" })}>
16
17
  <div class="flex-1 overflow-y-auto space-y-8">
17
18
  <!-- Header -->
18
19
  <div class="surface-tile p-6 flex items-center justify-between gap-8">
@@ -50,7 +51,8 @@ const authItems = authCollections.map((slug) => ({
50
51
  </div>
51
52
 
52
53
  <!-- CMS Metrics -->
53
- <DashboardMetrics client:load />
54
+ <DashboardMetrics client:load isEcommerce={!!collections['orders'] || !!collections['products']} />
55
+ {(!!collections['orders'] || !!collections['products']) && <RevenueChart client:load />}
54
56
 
55
57
  <!-- Quick Links Section -->
56
58
  <div class="surface-tile overflow-hidden mt-8">
@@ -151,8 +153,7 @@ const authItems = authCollections.map((slug) => ({
151
153
  </a>
152
154
 
153
155
  <a
154
- href={`${apiPath}/health`}
155
- target="_blank"
156
+ href={`${adminPath}/health`}
156
157
  class="p-5 bg-[var(--kyro-surface-accent)] rounded-lg hover:bg-[var(--kyro-surface-accent)] transition-all border border-transparent hover:border-[var(--kyro-border)] group"
157
158
  >
158
159
  <div class="w-10 h-10 rounded-lg bg-green-500/10 text-green-500 flex items-center justify-center mb-3">
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  import AdminLayout from "../layouts/AdminLayout.astro";
3
+ import i18next from '../lib/i18n';
3
4
  import { ApiKeysManager } from "../components/ApiKeysManager";
4
5
 
5
6
  import { adminPath, apiPath } from '../lib/paths';
6
7
 
7
8
  ---
8
9
 
9
- <AdminLayout title="API Keys">
10
+ <AdminLayout title={i18next.t("pages." + "API Keys".replace(/ /g, ""), { defaultValue: "API Keys" })}>
10
11
  <ApiKeysManager client:only="react" />
11
12
  </AdminLayout>
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  import AdminLayout from "../layouts/AdminLayout.astro";
3
+ import i18next from '../lib/i18n';
3
4
  import { MarketplaceManager } from "../components/MarketplaceManager";
4
5
 
5
6
  import { adminPath, apiPath } from '../lib/paths';
6
7
 
7
8
  ---
8
9
 
9
- <AdminLayout title="Marketplace">
10
+ <AdminLayout title={i18next.t("pages." + "Marketplace".replace(/ /g, ""), { defaultValue: "Marketplace" })}>
10
11
  <MarketplaceManager client:only="react" />
11
12
  </AdminLayout>
@@ -1,12 +1,13 @@
1
1
  ---
2
2
  import AdminLayout from '../layouts/AdminLayout.astro';
3
+ import i18next from '../lib/i18n';
3
4
  import { MediaGallery } from '../components/MediaGallery';
4
5
 
5
6
  import { adminPath, apiPath } from '../lib/paths';
6
7
 
7
8
  ---
8
9
 
9
- <AdminLayout title="Media Library">
10
+ <AdminLayout title={i18next.t("pages." + "Media Library".replace(/ /g, ""), { defaultValue: "Media Library" })}>
10
11
  <div class="flex-1 flex flex-col h-full min-h-0 overflow-hidden -m-4 md:m-0">
11
12
  <MediaGallery client:load />
12
13
  </div>
@@ -1,8 +1,9 @@
1
1
  ---
2
2
  import AdminLayout from "../layouts/AdminLayout.astro";
3
+ import i18next from '../lib/i18n';
3
4
  import { PluginsManager } from "../components/PluginsManager";
4
5
  ---
5
6
 
6
- <AdminLayout title="Plugins">
7
+ <AdminLayout title={i18next.t("pages." + "Plugins".replace(/ /g, ""), { defaultValue: "Plugins" })}>
7
8
  <PluginsManager client:only="react" />
8
9
  </AdminLayout>
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import AdminLayout from "../layouts/AdminLayout.astro";
3
+ import i18next from '../lib/i18n';
3
4
  import { RestPlayground } from "../components/RestPlayground";
4
5
  import { apiPath } from "../lib/paths";
5
6
 
@@ -10,7 +11,7 @@ const collectionsData = await collectionsResponse.json();
10
11
  const collections = collectionsData.collections || [];
11
12
  ---
12
13
 
13
- <AdminLayout title="REST Playground">
14
+ <AdminLayout title={i18next.t("pages." + "REST Playground".replace(/ /g, ""), { defaultValue: "REST Playground" })}>
14
15
  <div class="flex-1 overflow-hidden h-[calc(100vh-64px)] md:h-[calc(100vh-80px)]">
15
16
  <RestPlayground client:load collections={collections} />
16
17
  </div>
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  import AdminLayout from "../../layouts/AdminLayout.astro";
3
-
4
- import { adminPath, apiPath } from "../../lib/paths";
3
+ import i18next from "../../lib/i18n";
5
4
 
6
5
  const defaultRoles = [
7
6
  {
@@ -37,8 +36,7 @@ const defaultRoles = [
37
36
  level: 30,
38
37
  inherits: [],
39
38
  description: "Access own data and make purchases",
40
- color:
41
- "bg-[var(--kyro-surface-accent)] text-[var(--kyro-text-secondary)] border-[var(--kyro-border)]",
39
+ color: "bg-[var(--kyro-surface-accent)] text-[var(--kyro-text-secondary)] border-[var(--kyro-border)]",
42
40
  },
43
41
  {
44
42
  name: "guest",
@@ -59,15 +57,11 @@ const permissions: Record<string, string[]> = {
59
57
  };
60
58
  ---
61
59
 
62
- <AdminLayout title="Roles">
63
- <div class="flex-1 overflow-y-auto space-y-8">
60
+ <AdminLayout title={i18next.t("pages." + "Roles".replace(/ /g, ""), { defaultValue: "Roles" })}>
61
+ <div class="flex-1 overflow-y-auto space-y-8">
64
62
  <!-- Header -->
65
63
  <div class="surface-tile p-6">
66
- <h1
67
- class="text-xl font-bold tracking-tighter text-[var(--kyro-text-primary)]"
68
- >
69
- Roles & Permissions
70
- </h1>
64
+ <h1 class="text-xl font-bold tracking-tighter text-[var(--kyro-text-primary)]">Roles & Permissions</h1>
71
65
  <p class="text-sm text-[var(--kyro-text-secondary)] mt-1 font-regular">
72
66
  Role-based access control with hierarchical permissions
73
67
  </p>
@@ -77,22 +71,14 @@ const permissions: Record<string, string[]> = {
77
71
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
78
72
  {
79
73
  defaultRoles.map((role) => (
80
- <div
81
- class={`surface-tile p-6 border-2 ${role.color.split(" ").slice(2).join(" ")} rounded-2xl`}
82
- >
74
+ <div class={`surface-tile p-6 border-2 ${role.color.split(" ").slice(2).join(" ")} rounded-2xl`}>
83
75
  <div class="flex items-center justify-between mb-4">
84
- <h3 class="text-xl font-bold text-[var(--kyro-text-primary)] tracking-tighter">
85
- {role.name}
86
- </h3>
87
- <span
88
- class={`px-3 py-1 rounded-lg text-xs font-bold ${role.color.split(" ").slice(0, 2).join(" ")}`}
89
- >
76
+ <h3 class="text-xl font-bold text-[var(--kyro-text-primary)] tracking-tighter">{role.name}</h3>
77
+ <span class={`px-3 py-1 rounded-lg text-xs font-bold ${role.color.split(" ").slice(0, 2).join(" ")}`}>
90
78
  Level {role.level}
91
79
  </span>
92
80
  </div>
93
- <p class="text-sm text-[var(--kyro-text-secondary)] mb-4">
94
- {role.description}
95
- </p>
81
+ <p class="text-sm text-[var(--kyro-text-secondary)] mb-4">{role.description}</p>
96
82
  {role.inherits.length > 0 && (
97
83
  <div class="mb-4">
98
84
  <span class="text-xs font-bold text-[var(--kyro-text-secondary)] tracking-[0.1em] opacity-50">
@@ -126,14 +112,8 @@ const permissions: Record<string, string[]> = {
126
112
 
127
113
  <!-- Permission Matrix -->
128
114
  <div class="surface-tile overflow-hidden">
129
- <div
130
- class="p-6 border-b border-[var(--kyro-border)] bg-[var(--kyro-surface-accent)]"
131
- >
132
- <h2
133
- class="text-xl font-bold text-[var(--kyro-text-primary)] tracking-tighter"
134
- >
135
- Permission Matrix
136
- </h2>
115
+ <div class="p-6 border-b border-[var(--kyro-border)] bg-[var(--kyro-surface-accent)]">
116
+ <h2 class="text-xl font-bold text-[var(--kyro-text-primary)] tracking-tighter">Permission Matrix</h2>
137
117
  </div>
138
118
  <table class="w-full text-left">
139
119
  <thead>
@@ -141,11 +121,7 @@ const permissions: Record<string, string[]> = {
141
121
  class="text-[var(--kyro-text-secondary)] font-bold text-[10px] tracking-[0.3em] border-b border-[var(--kyro-border)]"
142
122
  >
143
123
  <th class="px-6 py-4">Resource</th>
144
- {
145
- defaultRoles.map((role) => (
146
- <th class="px-4 py-4 text-center">{role.name}</th>
147
- ))
148
- }
124
+ {defaultRoles.map((role) => <th class="px-4 py-4 text-center">{role.name}</th>)}
149
125
  </tr>
150
126
  </thead>
151
127
  <tbody class="divide-y divide-[var(--kyro-border)]">
@@ -171,24 +147,16 @@ const permissions: Record<string, string[]> = {
171
147
  { resource: "Audit Logs", actions: ["read"] },
172
148
  ].map((row) => (
173
149
  <tr class="hover:bg-[var(--kyro-surface-accent)] transition-colors">
174
- <td class="px-6 py-4 font-medium text-[var(--kyro-text-primary)]">
175
- {row.resource}
176
- </td>
150
+ <td class="px-6 py-4 font-medium text-[var(--kyro-text-primary)]">{row.resource}</td>
177
151
  {defaultRoles.map((role) => {
178
152
  const hasAll = permissions[role.name]?.includes("*");
179
153
  const hasResource = permissions[role.name]?.some(
180
- (p) =>
181
- p.startsWith(row.resource.toLowerCase() + ":") ||
182
- p === "*",
154
+ (p) => p.startsWith(row.resource.toLowerCase() + ":") || p === "*",
183
155
  );
184
156
  return (
185
157
  <td class="px-4 py-4 text-center">
186
158
  {hasAll || hasResource ? (
187
- <svg
188
- class="w-5 h-5 text-green-500 mx-auto"
189
- fill="currentColor"
190
- viewBox="0 0 20 20"
191
- >
159
+ <svg class="w-5 h-5 text-green-500 mx-auto" fill="currentColor" viewBox="0 0 20 20">
192
160
  <path
193
161
  fill-rule="evenodd"
194
162
  d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
@@ -196,11 +164,7 @@ const permissions: Record<string, string[]> = {
196
164
  />
197
165
  </svg>
198
166
  ) : (
199
- <svg
200
- class="w-5 h-5 text-[var(--kyro-border)] mx-auto"
201
- fill="currentColor"
202
- viewBox="0 0 20 20"
203
- >
167
+ <svg class="w-5 h-5 text-[var(--kyro-border)] mx-auto" fill="currentColor" viewBox="0 0 20 20">
204
168
  <path
205
169
  fill-rule="evenodd"
206
170
  d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
@@ -1,8 +1,9 @@
1
1
  ---
2
2
  import AdminLayout from "../layouts/AdminLayout.astro";
3
+ import i18next from '../lib/i18n';
3
4
  import { SessionsManager } from "../components/SessionsManager";
4
5
  ---
5
6
 
6
- <AdminLayout title="Sessions">
7
+ <AdminLayout title={i18next.t("pages." + "Sessions".replace(/ /g, ""), { defaultValue: "Sessions" })}>
7
8
  <SessionsManager client:only="react" />
8
9
  </AdminLayout>
@@ -1,11 +1,13 @@
1
1
  ---
2
2
  import AdminLayout from "../../layouts/AdminLayout.astro";
3
+ import i18next from '../../lib/i18n';
3
4
  import { globals } from "../../lib/config";
4
5
  import { SettingsPage } from "../../components/SettingsPage";
6
+ import { getGlobal } from "../../lib/globals";
5
7
 
6
8
  import { adminPath } from "../../lib/paths";
7
9
 
8
- const { slug } = Astro.params;
10
+ const slug = Astro.props.slug || Astro.params.slug || Astro.params.id;
9
11
 
10
12
  // Validate global exists
11
13
  if (!slug || !globals[slug]) {
@@ -13,6 +15,7 @@ if (!slug || !globals[slug]) {
13
15
  }
14
16
 
15
17
  const config = globals[slug];
18
+ const data = (await getGlobal(slug, { request: Astro.request })) || {};
16
19
  const hasVersioning = !!config.versions;
17
20
 
18
21
  const activeSlug = slug;
@@ -20,14 +23,13 @@ const allGlobals = Object.values(globals);
20
23
 
21
24
  const title = config.label || slug;
22
25
  const description =
23
- config.admin?.description ||
26
+ (config as any).admin?.description ||
24
27
  `Manage your ${title.toLowerCase()} configurations.`;
25
28
  ---
26
29
 
27
30
  <AdminLayout title={`Settings - ${title}`}>
28
31
  <div class="flex-1 overflow-y-auto">
29
32
  { !hasVersioning && (
30
- <!-- Standard Header (Only if no versioning/AutoForm header) -->
31
33
  <div class="surface-tile p-8 pb-0 mb-6">
32
34
  <div class="flex items-center justify-between mb-8">
33
35
  <div>
@@ -94,6 +96,7 @@ const description =
94
96
  config={config}
95
97
  globalSlug={slug}
96
98
  layout={hasVersioning ? "split" : "single"}
99
+ data={data}
97
100
  />
98
101
  <input type="hidden" id="form-data" name="form-data" value="{}" />
99
102
  </form>
@@ -101,34 +104,46 @@ const description =
101
104
  </div>
102
105
 
103
106
  <script>
104
- console.log("[Astro] Settings page script running");
105
- const saveBtn = document.getElementById("settings-save-btn");
106
-
107
- saveBtn?.addEventListener("click", () => {
108
- console.log("[Astro] Header save clicked");
109
- const btn = document.getElementById("btn-save");
110
- if (btn) {
111
- console.log("[Astro] Triggering hidden button click");
112
- btn.click();
113
- } else {
114
- console.error("[Astro] Hidden save button not found!");
115
- }
116
- });
107
+ const initSettingsPage = () => {
108
+ console.log("[Astro] Settings page init");
109
+ const saveBtn = document.getElementById("settings-save-btn");
110
+
111
+ saveBtn?.addEventListener("click", () => {
112
+ console.log("[Astro] Header save clicked");
113
+ const btn = document.getElementById("btn-save");
114
+ if (btn) {
115
+ console.log("[Astro] Triggering hidden button click");
116
+ btn.click();
117
+ } else {
118
+ console.error("[Astro] Hidden save button not found!");
119
+ }
120
+ });
121
+ };
117
122
 
118
- window.addEventListener("kyro:global-save-start", () => {
119
- if (saveBtn) {
120
- saveBtn.disabled = true;
121
- saveBtn.innerHTML = `<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path></svg>Saving...`;
122
- saveBtn.classList.add("opacity-50", "cursor-wait");
123
- }
124
- });
123
+ document.addEventListener("astro:page-load", initSettingsPage);
125
124
 
126
- window.addEventListener("kyro:global-save-end", () => {
127
- if (saveBtn) {
128
- saveBtn.disabled = false;
129
- saveBtn.innerHTML = "Save Changes";
130
- saveBtn.classList.remove("opacity-50", "cursor-wait");
131
- }
132
- });
125
+ // Global listeners should only be attached once
126
+ // @ts-ignore
127
+ if (!window.__kyroSettingsGlobalListenersAttached) {
128
+ window.addEventListener("kyro:global-save-start", () => {
129
+ const saveBtn = document.getElementById("settings-save-btn");
130
+ if (saveBtn) {
131
+ saveBtn.setAttribute("disabled", "true");
132
+ saveBtn.innerHTML = `<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path></svg>Saving...`;
133
+ saveBtn.classList.add("opacity-50", "cursor-wait");
134
+ }
135
+ });
136
+
137
+ window.addEventListener("kyro:global-save-end", () => {
138
+ const saveBtn = document.getElementById("settings-save-btn");
139
+ if (saveBtn) {
140
+ saveBtn.removeAttribute("disabled");
141
+ saveBtn.innerHTML = "Save Changes";
142
+ saveBtn.classList.remove("opacity-50", "cursor-wait");
143
+ }
144
+ });
145
+ // @ts-ignore
146
+ window.__kyroSettingsGlobalListenersAttached = true;
147
+ }
133
148
  </script>
134
149
  </AdminLayout>
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import AdminLayout from "../../layouts/AdminLayout.astro";
3
+ import i18next from '../../lib/i18n';
3
4
  import { UserDetail } from "../../components/users/UserDetail";
4
5
 
5
6
  import { adminPath, apiPath } from "../../lib/paths";
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import AdminLayout from "../../layouts/AdminLayout.astro";
3
+ import i18next from '../../lib/i18n';
3
4
  import { UsersList } from "../../components/users/UsersList";
4
5
 
5
6
  import { adminPath, apiPath } from "../../lib/paths";
@@ -25,7 +26,7 @@ try {
25
26
  }
26
27
  ---
27
28
 
28
- <AdminLayout title="Users">
29
+ <AdminLayout title={i18next.t("pages." + "Users".replace(/ /g, ""), { defaultValue: "Users" })}>
29
30
  <UsersList
30
31
  client:load
31
32
  initialUsers={users}
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  import AdminLayout from "../../layouts/AdminLayout.astro";
3
+ import i18next from '../../lib/i18n';
3
4
  import { UserForm } from "../../components/users/UserForm";
4
5
 
5
6
  import { adminPath, apiPath } from "../../lib/paths";
6
7
  ---
7
8
 
8
- <AdminLayout title="New User">
9
+ <AdminLayout title={i18next.t("pages." + "New User".replace(/ /g, ""), { defaultValue: "New User" })}>
9
10
  <UserForm
10
11
  client:load
11
12
  mode="create"
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  import AdminLayout from "../layouts/AdminLayout.astro";
3
+ import i18next from '../lib/i18n';
3
4
  import { WebhookManager } from "../components/WebhookManager";
4
5
 
5
6
  import { adminPath, apiPath } from '../lib/paths';
6
7
 
7
8
  ---
8
9
 
9
- <AdminLayout title="Webhooks">
10
+ <AdminLayout title={i18next.t("pages." + "Webhooks".replace(/ /g, ""), { defaultValue: "Webhooks" })}>
10
11
  <WebhookManager client:only="react" />
11
12
  </AdminLayout>
@@ -1,3 +0,0 @@
1
- import { adminPath, apiPath } from '../lib/paths';
2
- return Astro.redirect(adminPath);
3
- ---