@kyro-cms/admin 0.12.4 → 0.12.6

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 (109) 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 +10 -4
  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 +41 -38
  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 +11 -10
  29. package/src/components/autoform/AutoFormApiView.tsx +3 -1
  30. package/src/components/autoform/AutoFormEditView.tsx +3 -1
  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 -19
  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/deep-equal.ts +3 -3
  82. package/src/lib/i18n.ts +44 -0
  83. package/src/lib/vite-shim-plugin.ts +3 -0
  84. package/src/locales/de.json +163 -0
  85. package/src/locales/en.json +163 -0
  86. package/src/locales/es.json +163 -0
  87. package/src/locales/fr.json +163 -0
  88. package/src/locales/pt.json +163 -0
  89. package/src/pages/403.astro +2 -1
  90. package/src/pages/[collection]/[id].astro +23 -10
  91. package/src/pages/[collection]/index.astro +48 -34
  92. package/src/pages/audit/index.astro +2 -1
  93. package/src/pages/graphql-explorer.astro +2 -1
  94. package/src/pages/graphql.astro +2 -1
  95. package/src/pages/health.astro +9 -0
  96. package/src/pages/index.astro +6 -5
  97. package/src/pages/keys.astro +2 -1
  98. package/src/pages/marketplace.astro +2 -1
  99. package/src/pages/media.astro +2 -1
  100. package/src/pages/plugins.astro +2 -1
  101. package/src/pages/rest-playground.astro +2 -1
  102. package/src/pages/roles/index.astro +16 -52
  103. package/src/pages/sessions.astro +2 -1
  104. package/src/pages/settings/[slug].astro +45 -30
  105. package/src/pages/users/[id].astro +1 -0
  106. package/src/pages/users/index.astro +2 -1
  107. package/src/pages/users/new.astro +2 -1
  108. package/src/pages/webhooks.astro +2 -1
  109. package/src/pages/index_ALT.astro +0 -3
@@ -3,12 +3,14 @@ import { Dropdown, DropdownItem, DropdownSeparator } from "./ui/Dropdown";
3
3
  import { User, Shield, Key, Webhook, Clock, FileText, ExternalLink, HelpCircle, LogOut, Terminal, Zap } from "./ui/icons";
4
4
  import { useAuthStore } from "../lib/stores";
5
5
  import { apiGet } from "../lib/api";
6
+ import { useTranslation } from "react-i18next";
6
7
 
7
8
  interface UserMenuProps {
8
9
  adminPath: string;
9
10
  }
10
11
 
11
12
  export function UserMenu({ adminPath }: UserMenuProps) {
13
+ const { t } = useTranslation();
12
14
  const currentUser = useAuthStore((s) => s.user);
13
15
  const [avatarUrl, setAvatarUrl] = useState<string | null>(null);
14
16
  const [apiAccess, setApiAccess] = useState<{ graphqlEnabled?: boolean } | null>(null);
@@ -41,7 +43,7 @@ export function UserMenu({ adminPath }: UserMenuProps) {
41
43
  trigger={
42
44
  <div
43
45
  className="flex justify-center p-.5 text-[var(--kyro-text-secondary)] hover:text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface)] rounded-xl transition-all shadow-sm active:scale-95"
44
- title="Account"
46
+ title={t("userMenu.account", { defaultValue: "Account" })}
45
47
  >
46
48
  {avatarUrl ? (
47
49
  <img src={avatarUrl} alt="" className="w-8 h-8 rounded-full object-cover" />
@@ -53,7 +55,7 @@ export function UserMenu({ adminPath }: UserMenuProps) {
53
55
  >
54
56
  <div className="px-4 py-2 mb-1">
55
57
  <p className="text-[10px] font-medium tracking-[0.2em] text-[var(--kyro-text-secondary)] opacity-40">
56
- Account
58
+ {t("userMenu.account", { defaultValue: "Account" })}
57
59
  </p>
58
60
  </div>
59
61
 
@@ -68,21 +70,21 @@ export function UserMenu({ adminPath }: UserMenuProps) {
68
70
  }
69
71
  }}
70
72
  >
71
- Profile Settings
73
+ {t("userMenu.profileSettings", { defaultValue: "Profile Settings" })}
72
74
  </DropdownItem>
73
75
 
74
76
  <DropdownItem
75
77
  icon={<Shield className="w-4 h-4" />}
76
78
  onClick={() => window.location.href = `${adminPath}/roles`}
77
79
  >
78
- Permissions
80
+ {t("userMenu.permissions", { defaultValue: "Permissions" })}
79
81
  </DropdownItem>
80
82
 
81
83
  <DropdownSeparator />
82
84
 
83
85
  <div className="px-4 py-2 mb-1">
84
86
  <p className="text-[10px] font-medium tracking-[0.2em] text-[var(--kyro-text-secondary)] opacity-40">
85
- Developer
87
+ {t("userMenu.developer", { defaultValue: "Developer" })}
86
88
  </p>
87
89
  </div>
88
90
 
@@ -90,33 +92,33 @@ export function UserMenu({ adminPath }: UserMenuProps) {
90
92
  icon={<Key className="w-4 h-4" />}
91
93
  onClick={() => (window.location.href = `${adminPath}/keys`)}
92
94
  >
93
- API Keys
95
+ {t("userMenu.apiKeys", { defaultValue: "API Keys" })}
94
96
  </DropdownItem>
95
97
 
96
98
  <DropdownItem
97
99
  icon={<Webhook className="w-4 h-4" />}
98
100
  onClick={() => (window.location.href = `${adminPath}/webhooks`)}
99
101
  >
100
- Web Hooks
102
+ {t("userMenu.webHooks", { defaultValue: "Web Hooks" })}
101
103
  </DropdownItem>
102
104
  <DropdownItem
103
105
  icon={<Clock className="w-4 h-4" />}
104
106
  onClick={() => (window.location.href = `${adminPath}/sessions`)}
105
107
  >
106
- Sessions
108
+ {t("userMenu.sessions", { defaultValue: "Sessions" })}
107
109
  </DropdownItem>
108
110
  <DropdownItem
109
111
  icon={<FileText className="w-4 h-4" />}
110
112
  onClick={() => (window.location.href = `${adminPath}/audit`)}
111
113
  >
112
- Audit Logs
114
+ {t("userMenu.auditLogs", { defaultValue: "Audit Logs" })}
113
115
  </DropdownItem>
114
116
 
115
117
  <DropdownItem
116
118
  icon={<Terminal className="w-4 h-4" />}
117
119
  onClick={() => (window.location.href = `${adminPath}/rest-playground`)}
118
120
  >
119
- API Explorer
121
+ {t("userMenu.apiExplorer", { defaultValue: "API Explorer" })}
120
122
  </DropdownItem>
121
123
 
122
124
  {(apiAccess === null || apiAccess?.graphqlEnabled) && (
@@ -124,7 +126,7 @@ export function UserMenu({ adminPath }: UserMenuProps) {
124
126
  icon={<Zap className="w-4 h-4" />}
125
127
  onClick={() => (window.location.href = `${adminPath}/graphql`)}
126
128
  >
127
- GraphQL Playground
129
+ {t("userMenu.graphqlPlayground", { defaultValue: "GraphQL Playground" })}
128
130
  </DropdownItem>
129
131
  )}
130
132
 
@@ -132,22 +134,22 @@ export function UserMenu({ adminPath }: UserMenuProps) {
132
134
 
133
135
  <div className="px-4 py-2 mb-1">
134
136
  <p className="text-[10px] font-medium tracking-[0.2em] text-[var(--kyro-text-secondary)] opacity-40">
135
- Resources
137
+ {t("userMenu.resources", { defaultValue: "Resources" })}
136
138
  </p>
137
139
  </div>
138
140
 
139
141
  <DropdownItem
140
142
  icon={<ExternalLink className="w-4 h-4" />}
141
- onClick={() => window.open("https://docs.kyro.dev", "_blank")}
143
+ onClick={() => window.open("https://kyro-cms.com/getting-started.html", "_blank")}
142
144
  >
143
- Documentation
145
+ {t("userMenu.documentation", { defaultValue: "Documentation" })}
144
146
  </DropdownItem>
145
147
 
146
148
  <DropdownItem
147
149
  icon={<HelpCircle className="w-4 h-4" />}
148
- onClick={() => window.open("https://github.com/danielDozie/kyro-cms/issues", "_blank")}
150
+ onClick={() => window.open("https://github.com/danielDozie/kyro-cms/issues/new", "_blank")}
149
151
  >
150
- Get Support
152
+ {t("userMenu.getSupport", { defaultValue: "Get Support" })}
151
153
  </DropdownItem>
152
154
 
153
155
  <DropdownSeparator />
@@ -157,7 +159,7 @@ export function UserMenu({ adminPath }: UserMenuProps) {
157
159
  danger
158
160
  onClick={() => document.getElementById("logout-btn")?.click()}
159
161
  >
160
- Sign Out
162
+ {t("actions.signOut", { defaultValue: "Sign Out" })}
161
163
  </DropdownItem>
162
164
  </Dropdown>
163
165
  );
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { SlidePanel } from "./ui/SlidePanel";
3
3
  import { Spinner } from "./ui/Spinner";
4
4
  import { History, Eye, GitCompare, Undo2, CheckCircle2, Clock, User } from "lucide-react";
5
+ import { useTranslation } from "react-i18next";
5
6
 
6
7
  interface Version {
7
8
  id: string;
@@ -38,6 +39,7 @@ export function VersionHistoryPanel({
38
39
  onCompare,
39
40
  loading = false,
40
41
  }: VersionHistoryPanelProps) {
42
+ const { t } = useTranslation();
41
43
  const formatDate = (dateValue: any) => {
42
44
  if (!dateValue) return "Unknown date";
43
45
  const date = new Date(dateValue);
@@ -82,7 +84,7 @@ export function VersionHistoryPanel({
82
84
  <SlidePanel
83
85
  open={open}
84
86
  onClose={onClose}
85
- title="Version History"
87
+ title={t("tooltips.versionHistory", { defaultValue: "Version History" })}
86
88
  width="md"
87
89
  >
88
90
  {loading ? (
@@ -159,7 +161,7 @@ export function VersionHistoryPanel({
159
161
  <button type="button"
160
162
  onClick={() => onPreview(version)}
161
163
  className="p-2 text-[var(--kyro-text-secondary)] hover:text-[var(--kyro-primary)] hover:bg-[var(--kyro-primary)]/10 rounded-md transition-colors"
162
- title="Preview this version"
164
+ title={t("tooltips.previewThisVersion", { defaultValue: "Preview this version" })}
163
165
  >
164
166
  <Eye className="w-4 h-4" />
165
167
  </button>
@@ -172,7 +174,7 @@ export function VersionHistoryPanel({
172
174
  )
173
175
  }
174
176
  className="p-2 text-[var(--kyro-text-secondary)] hover:text-[var(--kyro-primary)] hover:bg-[var(--kyro-primary)]/10 rounded-md transition-colors"
175
- title="Compare with current"
177
+ title={t("tooltips.compareWithCurrent", { defaultValue: "Compare with current" })}
176
178
  >
177
179
  <GitCompare className="w-4 h-4" />
178
180
  </button>
@@ -181,7 +183,7 @@ export function VersionHistoryPanel({
181
183
  <button type="button"
182
184
  onClick={() => onRestore(version)}
183
185
  className="p-2 text-amber-600 hover:bg-amber-500/10 rounded-md transition-colors"
184
- title="Restore this version"
186
+ title={t("tooltips.restoreThisVersion", { defaultValue: "Restore this version" })}
185
187
  >
186
188
  <Undo2 className="w-4 h-4" />
187
189
  </button>
@@ -24,7 +24,7 @@ import { useUIStore, toast } from "../lib/stores";
24
24
  import { Modal, ModalContent, ModalActions } from "./ui/Modal";
25
25
  import { Badge } from "./ui/Badge";
26
26
  import { PageHeader } from "./ui/PageHeader";
27
-
27
+ import { useTranslation } from "react-i18next";
28
28
 
29
29
  interface WebhookConfigField {
30
30
  name: string;
@@ -76,6 +76,7 @@ const ACTION_ICONS: Record<string, string> = {
76
76
  };
77
77
 
78
78
  export function WebhookManager() {
79
+ const { t } = useTranslation();
79
80
  const {
80
81
  items: webhooks,
81
82
  loading,
@@ -265,7 +266,7 @@ export function WebhookManager() {
265
266
  return (
266
267
  <div className="w-full space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700 pb-32">
267
268
  <PageHeader
268
- title="Webhooks"
269
+ title={t("tooltips.webhooks", { defaultValue: "Webhooks" })}
269
270
  description="Get notified when your content changes."
270
271
  icon={Webhook}
271
272
  actions={[
@@ -410,7 +411,7 @@ export function WebhookManager() {
410
411
  type="button"
411
412
  onClick={() => handleViewHistory(webhook.id)}
412
413
  className="p-2 rounded-lg border border-[var(--kyro-border)] hover:border-[var(--kyro-primary)] transition-colors flex items-center gap-1.5"
413
- title="View delivery history"
414
+ title={t("tooltips.viewDeliveryHistory", { defaultValue: "View delivery history" })}
414
415
  >
415
416
  <Activity className="w-3.5 h-3.5" />
416
417
  <span className="text-[11px] font-medium hidden sm:inline">History</span>
@@ -420,7 +421,7 @@ export function WebhookManager() {
420
421
  type="button"
421
422
  onClick={() => handleTest(webhook.id)}
422
423
  className="p-2 rounded-lg border border-[var(--kyro-border)] hover:border-[var(--kyro-primary)] transition-colors flex items-center gap-1.5"
423
- title="Send test request"
424
+ title={t("tooltips.sendTestRequest", { defaultValue: "Send test request" })}
424
425
  >
425
426
  <Send className="w-3.5 h-3.5" />
426
427
  <span className="text-[11px] font-medium">Test</span>
@@ -439,7 +440,7 @@ export function WebhookManager() {
439
440
  type="button"
440
441
  onClick={() => remove(webhook.id, "Webhook")}
441
442
  className="p-2 rounded-lg border border-[var(--kyro-border)] hover:border-red-500/30 hover:bg-red-500/5 transition-colors"
442
- title="Delete"
443
+ title={t("tooltips.delete", { defaultValue: "Delete" })}
443
444
  >
444
445
  <Trash2 className="w-3.5 h-3.5 text-red-500/50 hover:text-red-500" />
445
446
  </button>
@@ -455,7 +456,7 @@ export function WebhookManager() {
455
456
  <Modal
456
457
  open={showCreateModal && step === "action"}
457
458
  onClose={() => setShowCreateModal(false)}
458
- title="New webhook"
459
+ title={t("tooltips.newWebhook", { defaultValue: "New webhook" })}
459
460
  >
460
461
  <ModalContent>
461
462
  <div className="space-y-4">
@@ -552,7 +553,7 @@ export function WebhookManager() {
552
553
  type="text"
553
554
  value={formData.secret}
554
555
  onChange={(e) => setFormData({ ...formData, secret: e.target.value })}
555
- placeholder="Optional"
556
+ placeholder={t("fields.optional", { defaultValue: "Optional" })}
556
557
  className="w-full px-3 py-2.5 bg-[var(--kyro-bg)] border border-[var(--kyro-border)] rounded-lg text-sm text-[var(--kyro-text-primary)] focus:outline-none focus:border-[var(--kyro-primary)]"
557
558
  />
558
559
  </div>
@@ -639,7 +640,7 @@ export function WebhookManager() {
639
640
  <Modal
640
641
  open={showTestModal}
641
642
  onClose={() => setShowTestModal(false)}
642
- title="Test webhook"
643
+ title={t("tooltips.testWebhook", { defaultValue: "Test webhook" })}
643
644
  >
644
645
  <ModalContent>
645
646
  <div className="p-6 rounded-xl bg-[var(--kyro-surface)] border border-[var(--kyro-border)] text-center">
@@ -681,7 +682,7 @@ export function WebhookManager() {
681
682
  <Modal
682
683
  open={showHelpModal}
683
684
  onClose={() => setShowHelpModal(false)}
684
- title="How webhooks work"
685
+ title={t("tooltips.howWebhooksWork", { defaultValue: "How webhooks work" })}
685
686
  >
686
687
  <ModalContent>
687
688
  <div className="space-y-6">
@@ -737,7 +738,7 @@ export function WebhookManager() {
737
738
  <Modal
738
739
  open={showHistoryModal}
739
740
  onClose={() => setShowHistoryModal(false)}
740
- title="Delivery History"
741
+ title={t("tooltips.deliveryHistory", { defaultValue: "Delivery History" })}
741
742
  >
742
743
  <ModalContent>
743
744
  <div className="space-y-4">
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import { useAutoFormStore } from "../../lib/autoform-store";
3
+ import { useTranslation } from "react-i18next";
3
4
 
4
5
  interface AutoFormApiViewProps {
5
6
  collectionSlug?: string;
@@ -10,6 +11,7 @@ export function AutoFormApiView({
10
11
  collectionSlug,
11
12
  globalSlug,
12
13
  }: AutoFormApiViewProps) {
14
+ const { t } = useTranslation();
13
15
  const { formData } = useAutoFormStore();
14
16
 
15
17
  return (
@@ -57,7 +59,7 @@ export function AutoFormApiView({
57
59
  navigator.clipboard.writeText(String(formData.id));
58
60
  }}
59
61
  className="p-1.5 hover:bg-[var(--kyro-bg-secondary)] rounded-lg transition-colors text-[var(--kyro-text-secondary)] hover:text-[var(--kyro-text-primary)]"
60
- title="Copy ID"
62
+ title={t("tooltips.copyId", { defaultValue: "Copy ID" })}
61
63
  >
62
64
  <svg
63
65
  width="14"
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import type { CollectionConfig, GlobalConfig, Field } from "@kyro-cms/core/client";
3
3
  import { useAutoFormStore } from "../../lib/autoform-store";
4
+ import { useTranslation } from "react-i18next";
4
5
 
5
6
  interface AutoFormEditViewProps {
6
7
  config: CollectionConfig | GlobalConfig;
@@ -15,6 +16,7 @@ export function AutoFormEditView({
15
16
  collectionSlug,
16
17
  renderField,
17
18
  }: AutoFormEditViewProps) {
19
+ const { t } = useTranslation();
18
20
  const { showPreview, sidebarCollapsed, formData, previewUrl } = useAutoFormStore();
19
21
 
20
22
  if (layout === "single") {
@@ -67,7 +69,7 @@ export function AutoFormEditView({
67
69
  <iframe
68
70
  src={previewUrl || ""}
69
71
  className="w-full h-full border-none"
70
- title="Live Preview"
72
+ title={t("tooltips.livePreview", { defaultValue: "Live Preview" })}
71
73
  />
72
74
  <div className="absolute inset-0 bg-transparent pointer-events-none border-[12px] border-[var(--kyro-surface)] rounded-3xl" />
73
75
  </div>
@@ -5,6 +5,7 @@ import { SplitButton } from "../ui/SplitButton";
5
5
  import type { SplitButtonStatus } from "../ui/SplitButton";
6
6
  import { useAutoFormStore } from "../../lib/autoform-store";
7
7
  import { adminPath as ADMIN_BASE } from "../../lib/paths";
8
+ import { useTranslation } from "react-i18next";
8
9
 
9
10
  type View = "edit" | "version" | "api";
10
11
 
@@ -41,6 +42,7 @@ export function AutoFormHeader({
41
42
  handleSchedulePublish,
42
43
  handleConflictOverride,
43
44
  }: AutoFormHeaderProps) {
45
+ const { t } = useTranslation();
44
46
  const [now, setNow] = useState(Date.now());
45
47
  const [showSchedulePicker, setShowSchedulePicker] = useState(false);
46
48
  const scheduleRef = useRef<HTMLDivElement>(null);
@@ -194,7 +196,7 @@ export function AutoFormHeader({
194
196
  <button
195
197
  type="button"
196
198
  className="kyro-btn p-2 md:p-2.5 rounded-xl border border-[var(--kyro-border)] hover:bg-[var(--kyro-bg-secondary)] transition-all"
197
- title="More actions"
199
+ title={t("tooltips.moreActions", { defaultValue: "More actions" })}
198
200
  >
199
201
  <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
200
202
  <circle cx="12" cy="5" r="1.5" />
@@ -377,8 +379,7 @@ export function AutoFormHeader({
377
379
  <button
378
380
  type="button"
379
381
  onClick={() => {
380
- setFormData(lastSavedData);
381
- markSaved();
382
+ useAutoFormStore.getState().loadDocument(lastSavedData, lastSavedData);
382
383
  }}
383
384
  className="text-[var(--kyro-primary)] text-[10px] font-medium hover:underline"
384
385
  >
@@ -424,8 +425,7 @@ export function AutoFormHeader({
424
425
  <button
425
426
  type="button"
426
427
  onClick={() => {
427
- setFormData(lastSavedData);
428
- markSaved();
428
+ useAutoFormStore.getState().loadDocument(lastSavedData, lastSavedData);
429
429
  }}
430
430
  className="text-[var(--kyro-primary)] hover:underline"
431
431
  >
@@ -475,7 +475,7 @@ export function AutoFormHeader({
475
475
  type="button"
476
476
  onClick={() => setShowPreview(!showPreview)}
477
477
  className={`kyro-btn p-2.5 rounded-xl transition-all flex items-center gap-2 ${showPreview ? "shadow-lg" : "text-[var(--kyro-text-secondary)] hover:bg-[var(--kyro-bg-secondary)]"}`}
478
- title="Live Preview"
478
+ title={t("tooltips.livePreview", { defaultValue: "Live Preview" })}
479
479
  >
480
480
  <ExternalLink className="w-4 h-4" />
481
481
  {showPreview && (
@@ -5,11 +5,13 @@ import {
5
5
  } from "../fields/extensions/blocksStore";
6
6
  import { ChevronRight, X } from "../ui/icons";
7
7
  import { AccordionField } from "../fields/AccordionField";
8
+ import { useTranslation } from "react-i18next";
8
9
 
9
10
  export const AccordionBlock: React.FC<{ block: Record<string, unknown>; index: number }> = ({
10
11
  block,
11
12
  index,
12
13
  }) => {
14
+ const { t } = useTranslation();
13
15
  const blockData = useBlockById(block.id);
14
16
  const { updateBlock, removeBlock, moveBlock } = useBlockActions();
15
17
 
@@ -36,7 +38,7 @@ export const AccordionBlock: React.FC<{ block: Record<string, unknown>; index: n
36
38
  type="button"
37
39
  onClick={() => moveBlock(block.id, "up")}
38
40
  className="p-1 hover:bg-[var(--kyro-surface-accent)] rounded"
39
- title="Move up"
41
+ title={t("tooltips.moveUp", { defaultValue: "Move up" })}
40
42
  >
41
43
  <ChevronRight className="w-3 h-3 rotate-90" />
42
44
  </button>
@@ -44,7 +46,7 @@ export const AccordionBlock: React.FC<{ block: Record<string, unknown>; index: n
44
46
  type="button"
45
47
  onClick={() => moveBlock(block.id, "down")}
46
48
  className="p-1 hover:bg-[var(--kyro-surface-accent)] rounded"
47
- title="Move down"
49
+ title={t("tooltips.moveDown", { defaultValue: "Move down" })}
48
50
  >
49
51
  <ChevronRight className="w-3 h-3" />
50
52
  </button>
@@ -52,7 +54,7 @@ export const AccordionBlock: React.FC<{ block: Record<string, unknown>; index: n
52
54
  type="button"
53
55
  onClick={() => removeBlock(block.id)}
54
56
  className="p-1 hover:bg-[var(--kyro-danger-bg)] rounded text-[var(--kyro-danger)]"
55
- title="Remove"
57
+ title={t("tooltips.remove", { defaultValue: "Remove" })}
56
58
  >
57
59
  <X className="w-3 h-3" />
58
60
  </button>
@@ -5,11 +5,13 @@ import {
5
5
  } from "../fields/extensions/blocksStore";
6
6
  import { ChevronRight, X } from "../ui/icons";
7
7
  import { ChildBlocksTree } from "./ChildBlocksTree";
8
+ import { useTranslation } from "react-i18next";
8
9
 
9
10
  export const ArrayBlock: React.FC<{ block: Record<string, unknown>; index: number }> = ({
10
11
  block,
11
12
  index,
12
13
  }) => {
14
+ const { t } = useTranslation();
13
15
  const blockData = useBlockById(block.id);
14
16
  const { updateBlock, removeBlock, moveBlock } = useBlockActions();
15
17
 
@@ -32,7 +34,7 @@ export const ArrayBlock: React.FC<{ block: Record<string, unknown>; index: numbe
32
34
  type="button"
33
35
  onClick={() => moveBlock(block.id, "up")}
34
36
  className="p-1 hover:bg-[var(--kyro-surface-accent)] rounded"
35
- title="Move up"
37
+ title={t("tooltips.moveUp", { defaultValue: "Move up" })}
36
38
  >
37
39
  <ChevronRight className="w-3 h-3 rotate-90" />
38
40
  </button>
@@ -40,7 +42,7 @@ export const ArrayBlock: React.FC<{ block: Record<string, unknown>; index: numbe
40
42
  type="button"
41
43
  onClick={() => moveBlock(block.id, "down")}
42
44
  className="p-1 hover:bg-[var(--kyro-surface-accent)] rounded"
43
- title="Move down"
45
+ title={t("tooltips.moveDown", { defaultValue: "Move down" })}
44
46
  >
45
47
  <ChevronRight className="w-3 h-3" />
46
48
  </button>
@@ -48,7 +50,7 @@ export const ArrayBlock: React.FC<{ block: Record<string, unknown>; index: numbe
48
50
  type="button"
49
51
  onClick={() => removeBlock(block.id)}
50
52
  className="p-1 hover:bg-[var(--kyro-danger-bg)] rounded text-[var(--kyro-danger)]"
51
- title="Remove"
53
+ title={t("tooltips.remove", { defaultValue: "Remove" })}
52
54
  >
53
55
  <X className="w-3 h-3" />
54
56
  </button>
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { ChevronRight, X } from "../ui/icons";
3
3
  import { useBlockActions } from "../fields/extensions/blocksStore";
4
+ import { useTranslation } from "react-i18next";
4
5
 
5
6
  interface BlockWrapperProps {
6
7
  id: string;
@@ -17,6 +18,7 @@ export const BlockWrapper: React.FC<BlockWrapperProps> = ({
17
18
  children,
18
19
  className = "",
19
20
  }) => {
21
+ const { t } = useTranslation();
20
22
  const { moveBlock, removeBlock } = useBlockActions();
21
23
 
22
24
  return (
@@ -30,7 +32,7 @@ export const BlockWrapper: React.FC<BlockWrapperProps> = ({
30
32
  type="button"
31
33
  onClick={() => moveBlock(id, "up")}
32
34
  className="p-1 hover:bg-[var(--kyro-surface-accent)] rounded text-[var(--kyro-text-muted)] hover:text-[var(--kyro-primary)]"
33
- title="Move up"
35
+ title={t("tooltips.moveUp", { defaultValue: "Move up" })}
34
36
  >
35
37
  <ChevronRight className="w-3 h-3 rotate-90" />
36
38
  </button>
@@ -38,7 +40,7 @@ export const BlockWrapper: React.FC<BlockWrapperProps> = ({
38
40
  type="button"
39
41
  onClick={() => moveBlock(id, "down")}
40
42
  className="p-1 hover:bg-[var(--kyro-surface-accent)] rounded text-[var(--kyro-text-muted)] hover:text-[var(--kyro-primary)]"
41
- title="Move down"
43
+ title={t("tooltips.moveDown", { defaultValue: "Move down" })}
42
44
  >
43
45
  <ChevronRight className="w-3 h-3" />
44
46
  </button>
@@ -46,7 +48,7 @@ export const BlockWrapper: React.FC<BlockWrapperProps> = ({
46
48
  type="button"
47
49
  onClick={() => removeBlock(id)}
48
50
  className="p-1 hover:bg-[var(--kyro-error)]/10 rounded text-[var(--kyro-error)]"
49
- title="Remove"
51
+ title={t("tooltips.remove", { defaultValue: "Remove" })}
50
52
  >
51
53
  <X className="w-3 h-3" />
52
54
  </button>
@@ -5,11 +5,13 @@ import {
5
5
  } from "../fields/extensions/blocksStore";
6
6
  import { ChevronRight, X, Code2 } from "../ui/icons";
7
7
  import { CodeField } from "../fields/CodeField";
8
+ import { useTranslation } from "react-i18next";
8
9
 
9
10
  export const CodeBlock: React.FC<{ block: Record<string, unknown>; index: number }> = ({
10
11
  block,
11
12
  index,
12
13
  }) => {
14
+ const { t } = useTranslation();
13
15
  const blockData = useBlockById(block.id);
14
16
  const { updateBlock, removeBlock, moveBlock } = useBlockActions();
15
17
  const data = blockData?.data ?? block.data ?? {};
@@ -39,7 +41,7 @@ export const CodeBlock: React.FC<{ block: Record<string, unknown>; index: number
39
41
  type="button"
40
42
  onClick={() => moveBlock(block.id, "up")}
41
43
  className="p-1.5 hover:bg-[var(--kyro-surface)] rounded-lg transition-all text-[var(--kyro-text-muted)] hover:text-[var(--kyro-primary)]"
42
- title="Move up"
44
+ title={t("tooltips.moveUp", { defaultValue: "Move up" })}
43
45
  >
44
46
  <ChevronRight className="w-4 h-4 rotate-[-90deg]" />
45
47
  </button>
@@ -48,7 +50,7 @@ export const CodeBlock: React.FC<{ block: Record<string, unknown>; index: number
48
50
  type="button"
49
51
  onClick={() => removeBlock(block.id)}
50
52
  className="p-1.5 hover:bg-[var(--kyro-danger-bg)] rounded-lg transition-all text-[var(--kyro-text-muted)] hover:text-[var(--kyro-danger)]"
51
- title="Remove"
53
+ title={t("tooltips.remove", { defaultValue: "Remove" })}
52
54
  >
53
55
  <X className="w-4 h-4" />
54
56
  </button>
@@ -5,11 +5,13 @@ import {
5
5
  } from "../fields/extensions/blocksStore";
6
6
  import { ChevronRight, X } from "../ui/icons";
7
7
  import { UploadField } from "../fields/UploadField";
8
+ import { useTranslation } from "react-i18next";
8
9
 
9
10
  export const FileBlock: React.FC<{ block: Record<string, unknown>; index: number }> = ({
10
11
  block,
11
12
  index,
12
13
  }) => {
14
+ const { t } = useTranslation();
13
15
  const blockData = useBlockById(block.id);
14
16
  const { updateBlock, removeBlock, moveBlock } = useBlockActions();
15
17
 
@@ -30,7 +32,7 @@ export const FileBlock: React.FC<{ block: Record<string, unknown>; index: number
30
32
  type="button"
31
33
  onClick={() => moveBlock(block.id, "up")}
32
34
  className="p-1 hover:bg-[var(--kyro-surface-accent)] rounded"
33
- title="Move up"
35
+ title={t("tooltips.moveUp", { defaultValue: "Move up" })}
34
36
  >
35
37
  <ChevronRight className="w-3 h-3 rotate-90" />
36
38
  </button>
@@ -38,7 +40,7 @@ export const FileBlock: React.FC<{ block: Record<string, unknown>; index: number
38
40
  type="button"
39
41
  onClick={() => moveBlock(block.id, "down")}
40
42
  className="p-1 hover:bg-[var(--kyro-surface-accent)] rounded"
41
- title="Move down"
43
+ title={t("tooltips.moveDown", { defaultValue: "Move down" })}
42
44
  >
43
45
  <ChevronRight className="w-3 h-3" />
44
46
  </button>
@@ -46,7 +48,7 @@ export const FileBlock: React.FC<{ block: Record<string, unknown>; index: number
46
48
  type="button"
47
49
  onClick={() => removeBlock(block.id)}
48
50
  className="p-1 hover:bg-[var(--kyro-danger-bg)] rounded text-[var(--kyro-danger)]"
49
- title="Remove"
51
+ title={t("tooltips.remove", { defaultValue: "Remove" })}
50
52
  >
51
53
  <X className="w-3 h-3" />
52
54
  </button>
@@ -7,11 +7,13 @@ import { ChevronRight, X } from "../ui/icons";
7
7
  import { HeroField } from "../fields/HeroField";
8
8
  import { UploadField } from "../fields/UploadField";
9
9
  import { ChildBlocksTree } from "./ChildBlocksTree";
10
+ import { useTranslation } from "react-i18next";
10
11
 
11
12
  export const HeroBlock: React.FC<{ block: Record<string, unknown>; index: number }> = ({
12
13
  block,
13
14
  index,
14
15
  }) => {
16
+ const { t } = useTranslation();
15
17
  const blockData = useBlockById(block.id);
16
18
  const { updateBlock, removeBlock, moveBlock } = useBlockActions();
17
19
 
@@ -35,7 +37,7 @@ export const HeroBlock: React.FC<{ block: Record<string, unknown>; index: number
35
37
  type="button"
36
38
  onClick={() => moveBlock(block.id, "up")}
37
39
  className="p-1 hover:bg-[var(--kyro-surface-accent)] rounded"
38
- title="Move up"
40
+ title={t("tooltips.moveUp", { defaultValue: "Move up" })}
39
41
  >
40
42
  <ChevronRight className="w-3 h-3 rotate-90" />
41
43
  </button>
@@ -43,7 +45,7 @@ export const HeroBlock: React.FC<{ block: Record<string, unknown>; index: number
43
45
  type="button"
44
46
  onClick={() => moveBlock(block.id, "down")}
45
47
  className="p-1 hover:bg-[var(--kyro-surface-accent)] rounded"
46
- title="Move down"
48
+ title={t("tooltips.moveDown", { defaultValue: "Move down" })}
47
49
  >
48
50
  <ChevronRight className="w-3 h-3" />
49
51
  </button>
@@ -51,7 +53,7 @@ export const HeroBlock: React.FC<{ block: Record<string, unknown>; index: number
51
53
  type="button"
52
54
  onClick={() => removeBlock(block.id)}
53
55
  className="p-1 hover:bg-[var(--kyro-danger-bg)] rounded text-[var(--kyro-danger)]"
54
- title="Remove"
56
+ title={t("tooltips.remove", { defaultValue: "Remove" })}
55
57
  >
56
58
  <X className="w-3 h-3" />
57
59
  </button>