@kyro-cms/admin 0.12.4 → 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 (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 +9 -3
  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
@@ -10,6 +10,7 @@ import {
10
10
  import { PageHeader } from "./ui/PageHeader";
11
11
  import { Badge } from "./ui/Badge";
12
12
  import { Modal, ModalContent, ModalActions } from "./ui/Modal";
13
+ import { useTranslation } from "react-i18next";
13
14
 
14
15
  interface ApiKeyItem {
15
16
  id: string;
@@ -46,6 +47,7 @@ const ALL_PERMISSIONS: PermissionsOption[] = [
46
47
  ];
47
48
 
48
49
  export function ApiKeysManager() {
50
+ const { t } = useTranslation();
49
51
  const { items: keys, loading, create, update, remove, isCreateModalOpen, setIsCreateModalOpen } =
50
52
  useResourceManager<ApiKeyItem>(
51
53
  React.useMemo(() => ({
@@ -83,7 +85,7 @@ export function ApiKeysManager() {
83
85
  <div className="w-full space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700 pb-32">
84
86
 
85
87
  <PageHeader
86
- title="API Keys"
88
+ title={t("tooltips.apiKeys", { defaultValue: "API Keys" })}
87
89
  description="Programmatic tokens for secure infrastructure integration."
88
90
  icon={Key}
89
91
  actions={
@@ -329,7 +331,7 @@ export function ApiKeysManager() {
329
331
  type="button"
330
332
  onClick={() => remove(key.id, "API Key")}
331
333
  className="p-2 bg-red-500/5 border border-red-500/10 rounded-lg hover:bg-red-500/10 hover:border-red-500/30 transition-all opacity-0 group-hover:opacity-100 shrink-0"
332
- title="Revoke Access"
334
+ title={t("tooltips.revokeAccess", { defaultValue: "Revoke Access" })}
333
335
  >
334
336
  <Trash2 className="w-3.5 h-3.5 text-red-500/50 group-hover/delete:text-red-500" />
335
337
  </button>
@@ -342,7 +344,7 @@ export function ApiKeysManager() {
342
344
  </div>
343
345
 
344
346
  {/* Create Modal */}
345
- <Modal size="lg" open={isCreateModalOpen} onClose={() => setIsCreateModalOpen(false)} title="Create New API Key">
347
+ <Modal size="lg" open={isCreateModalOpen} onClose={() => setIsCreateModalOpen(false)} title={t("tooltips.createNewApiKey", { defaultValue: "Create New API Key" })}>
346
348
  <ModalContent>
347
349
  <div className="space-y-5">
348
350
  <div>
@@ -428,7 +430,7 @@ export function ApiKeysManager() {
428
430
  </Modal>
429
431
 
430
432
  {/* Help Modal */}
431
- <Modal size="lg" open={showHelpModal} onClose={() => setShowHelpModal(false)} title="How API Keys Work">
433
+ <Modal size="lg" open={showHelpModal} onClose={() => setShowHelpModal(false)} title={t("tooltips.howApiKeysWork", { defaultValue: "How API Keys Work" })}>
432
434
  <ModalContent>
433
435
  <div className="space-y-6">
434
436
  <div>
@@ -3,6 +3,7 @@ import React, { useState, useEffect, useCallback } from "react";
3
3
  import { fetchWithAuth } from "../lib/api";
4
4
  import { Modal } from "./ui/Modal";
5
5
  import { Shimmer } from "./ui/Shimmer";
6
+ import { useTranslation } from "react-i18next";
6
7
 
7
8
  interface AuditLog {
8
9
  id: string;
@@ -156,6 +157,7 @@ function MetadataRow({ label, value }: { label: string; value: unknown }) {
156
157
  }
157
158
 
158
159
  export function AuditLogsPage() {
160
+ const { t } = useTranslation();
159
161
  const [logs, setLogs] = useState<AuditLog[]>([]);
160
162
  const [total, setTotal] = useState(0);
161
163
  const [page, setPage] = useState(1);
@@ -234,7 +236,7 @@ export function AuditLogsPage() {
234
236
  <Search className="w-4 h-4" />
235
237
  <input
236
238
  type="text"
237
- placeholder="Search by user email..."
239
+ placeholder={t("fields.searchByUserEmail", { defaultValue: "Search by user email..." })}
238
240
  value={search}
239
241
  onChange={(e) => {
240
242
  setSearch(e.target.value);
@@ -524,7 +526,7 @@ export function AuditLogsPage() {
524
526
  <Modal
525
527
  open={!!selectedLog}
526
528
  onClose={() => setSelectedLog(null)}
527
- title="Audit Log Details"
529
+ title={t("tooltips.auditLogDetails", { defaultValue: "Audit Log Details" })}
528
530
  size="lg"
529
531
  >
530
532
  {selectedLog && (
@@ -567,8 +567,7 @@ export function AutoForm({
567
567
  if (response.ok) {
568
568
  const result = await response.json();
569
569
  const savedData = result.data || data;
570
- setFormData({ ...formData, ...savedData });
571
- markSaved();
570
+ useAutoFormStore.getState().loadDocument(savedData, savedData);
572
571
  lastAutoSaveTimeRef.current = Date.now();
573
572
  setAutoSaveStatus("success");
574
573
  setLocalSaveStatus("saved");
@@ -580,6 +579,9 @@ export function AutoForm({
580
579
  onActionSuccess?.(
581
580
  isPost ? "Document created successfully" : "Changes saved",
582
581
  );
582
+ if (globalSlug) {
583
+ window.dispatchEvent(new Event("kyro:soft-reload"));
584
+ }
583
585
  if (isPost) {
584
586
  setTimeout(() => {
585
587
  window.location.href = `${ADMIN_BASE}/${collectionSlug}/${result.data.id}`;
@@ -630,9 +632,8 @@ export function AutoForm({
630
632
  }
631
633
  const result = await response.json();
632
634
  const savedData = result.data || data;
633
- setFormData({ ...formData, ...savedData });
634
- markSaved();
635
- dataToPublish = { ...formData, ...savedData };
635
+ useAutoFormStore.getState().loadDocument(savedData, savedData);
636
+ dataToPublish = savedData;
636
637
  }
637
638
 
638
639
  // Save and publish (X-Draft: false writes to main doc + versions table)
@@ -642,8 +643,7 @@ export function AutoForm({
642
643
  if (response?.ok) {
643
644
  const result = await response.json().catch(() => ({}));
644
645
  const savedData = result.data || data;
645
- setFormData({ ...formData, ...savedData });
646
- markSaved();
646
+ useAutoFormStore.getState().loadDocument(savedData, savedData);
647
647
  setLocalSaveStatus("saved");
648
648
  onActionSuccess?.("Published successfully");
649
649
 
@@ -1137,6 +1137,9 @@ export function AutoForm({
1137
1137
  setFormData({ ...formData, ...savedData });
1138
1138
  setLastSavedData({ ...formData, ...savedData });
1139
1139
  onActionSuccess?.("Changes saved");
1140
+ if (globalSlug) {
1141
+ window.dispatchEvent(new Event("kyro:soft-reload"));
1142
+ }
1140
1143
  }
1141
1144
  } catch (e) {
1142
1145
  console.error("Save error exception:", e);
@@ -12,8 +12,10 @@ import {
12
12
  RefreshCcw,
13
13
  Sparkles,
14
14
  } from "./ui/icons";
15
+ import { useTranslation } from "react-i18next";
15
16
 
16
17
  export function BrandingHub() {
18
+ const { t } = useTranslation();
17
19
  const [siteName, setSiteName] = useState("Kyro CMS");
18
20
  const [adminTitle, setAdminTitle] = useState("Command Center");
19
21
  const [primaryColor, setPrimaryColor] = useState("#6366f1");
@@ -158,7 +160,7 @@ export function BrandingHub() {
158
160
  onChange={(e) => setDashboardGreeting(e.target.value)}
159
161
  rows={3}
160
162
  className="w-full bg-[var(--kyro-bg-secondary)] border border-[var(--kyro-border)] rounded-xl py-3 px-4 text-sm font-bold focus:outline-none focus:ring-2 focus:ring-[var(--kyro-primary)] transition-all resize-none"
161
- placeholder="Greeting text for the dashboard..."
163
+ placeholder={t("fields.greetingTextForThe", { defaultValue: "Greeting text for the dashboard..." })}
162
164
  />
163
165
  </div>
164
166
  </div>
@@ -4,6 +4,7 @@ import { useAuthStore } from "../lib/stores";
4
4
  import { authCollectionSlugs } from "../lib/config";
5
5
  import { PageHeader } from "./ui/PageHeader";
6
6
  import { Shimmer } from "./ui/Shimmer";
7
+ import { useTranslation } from "react-i18next";
7
8
 
8
9
 
9
10
  interface DashboardProps {
@@ -13,8 +14,14 @@ interface DashboardProps {
13
14
  }
14
15
 
15
16
  export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
17
+ const { t } = useTranslation();
16
18
  const { permissions } = useAuthStore();
17
- const [stats, setStats] = useState({
19
+ const [stats, setStats] = useState<{
20
+ totalDocs: number;
21
+ totalMedia: number;
22
+ totalUsers: number;
23
+ recentActivity: Array<{ id: number; type: string; user: string; doc: string; collection: string; time: string }>;
24
+ }>({
18
25
  totalDocs: 0,
19
26
  totalMedia: 0,
20
27
  totalUsers: 0,
@@ -78,10 +85,10 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
78
85
  return (
79
86
  <div className="w-full space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700 px-8 pb-12">
80
87
  <PageHeader
81
- title={`Welcome back, ${user?.email?.split("@")[0] || "Admin"}`}
82
- description="Everything looks great in your command center today."
88
+ title={t("dashboard.welcome", { defaultValue: "Welcome back, {{name}}", name: (user?.email as string)?.split("@")[0] || "Admin" })}
89
+ description={t("dashboard.description", { defaultValue: "Everything looks great in your command center today." })}
83
90
  action={collectionList.length > 0 && permissions?.collections?.[collectionList[0]?.[0]]?.create !== false ? {
84
- label: "New Document",
91
+ label: t("actions.newDocument", { defaultValue: "New Document" }),
85
92
  onClick: () => onNavigate("create", collectionList[0]?.[0]),
86
93
  icon: Plus,
87
94
  } : undefined}
@@ -92,25 +99,25 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
92
99
  <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
93
100
  {[
94
101
  {
95
- label: "Total Content",
102
+ label: t("dashboard.totalContent", { defaultValue: "Total Content" }),
96
103
  value: stats.totalDocs,
97
104
  icon: FileText,
98
105
  color: "text-blue-500",
99
106
  },
100
107
  {
101
- label: "Media Assets",
108
+ label: t("dashboard.mediaAssets", { defaultValue: "Media Assets" }),
102
109
  value: stats.totalMedia,
103
110
  icon: ImageIcon,
104
111
  color: "text-purple-500",
105
112
  },
106
113
  {
107
- label: "Active Users",
114
+ label: t("dashboard.activeUsers", { defaultValue: "Active Users" }),
108
115
  value: stats.totalUsers,
109
116
  icon: Users,
110
117
  color: "text-green-500",
111
118
  },
112
119
  {
113
- label: "System Health",
120
+ label: t("dashboard.systemHealth", { defaultValue: "System Health" }),
114
121
  value: "100%",
115
122
  icon: Activity,
116
123
  color: "text-amber-500",
@@ -143,10 +150,10 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
143
150
  <section className="surface-tile p-8">
144
151
  <h2 className="text-xl font-bold mb-1 tracking-tight flex items-center gap-2">
145
152
  <LayoutDashboard className="w-5 h-5 opacity-40" />
146
- Content Growth
153
+ {t("dashboard.contentGrowth", { defaultValue: "Content Growth" })}
147
154
  </h2>
148
155
  <p className="text-[10px] font-bold tracking-widest opacity-40 mb-8">
149
- Snapshot of document velocity over the last 7 days
156
+ {t("dashboard.contentGrowthDesc", { defaultValue: "Snapshot of document velocity over the last 7 days" })}
150
157
  </p>
151
158
 
152
159
  {/* SVG Line Chart */}
@@ -192,18 +199,18 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
192
199
  />
193
200
  </svg>
194
201
  <div className="flex justify-between mt-4 text-[10px] font-bold opacity-30 tracking-widest ">
195
- <span>Mon</span>
196
- <span>Tue</span>
197
- <span>Wed</span>
198
- <span>Thu</span>
199
- <span>Fri</span>
200
- <span>Sat</span>
201
- <span>Sun</span>
202
+ <span>{t("days.mon", { defaultValue: "Mon" })}</span>
203
+ <span>{t("days.tue", { defaultValue: "Tue" })}</span>
204
+ <span>{t("days.wed", { defaultValue: "Wed" })}</span>
205
+ <span>{t("days.thu", { defaultValue: "Thu" })}</span>
206
+ <span>{t("days.fri", { defaultValue: "Fri" })}</span>
207
+ <span>{t("days.sat", { defaultValue: "Sat" })}</span>
208
+ <span>{t("days.sun", { defaultValue: "Sun" })}</span>
202
209
  </div>
203
210
  </div>
204
211
 
205
212
  <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
206
- {collectionList.map(([slug, config]: [string, Record<string, unknown>]) => (
213
+ {collectionList.map(([slug, config]: [string, any]) => (
207
214
  <div
208
215
  key={slug}
209
216
  onClick={() => onNavigate("list", slug)}
@@ -211,7 +218,7 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
211
218
  >
212
219
  <div className="flex items-center justify-between mb-2">
213
220
  <h3 className="font-bold text-lg tracking-tight group-hover:text-[var(--kyro-primary)] transition-colors">
214
- {config.label || slug}
221
+ {(config.label as string) || slug}
215
222
  </h3>
216
223
  <ArrowUpRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-all" />
217
224
  </div>
@@ -222,7 +229,7 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
222
229
  />
223
230
  </div>
224
231
  <p className="text-sm text-[var(--kyro-text-secondary)] line-clamp-1">
225
- {config.admin?.description || `Manage ${slug} content.`}
232
+ {(config.admin as any)?.description || `Manage ${slug} content.`}
226
233
  </p>
227
234
  </div>
228
235
  ))}
@@ -234,7 +241,7 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
234
241
  <div className="flex items-center justify-between mb-4">
235
242
  <h3 className="text-xl font-bold tracking-tight group-hover:text-[var(--kyro-primary)] transition-colors flex items-center gap-2">
236
243
  <Plus className="w-5 h-5" />
237
- Quick Links
244
+ {t("dashboard.quickLinks", { defaultValue: "Quick Links" })}
238
245
  </h3>
239
246
  </div>
240
247
  <div className="space-y-3">
@@ -247,13 +254,13 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
247
254
  className="w-full flex items-center justify-between p-4 bg-[var(--kyro-bg-secondary)] border border-[var(--kyro-border)] rounded-xl hover:bg-[var(--kyro-surface-accent)] hover:border-[var(--kyro-primary)] transition-all group/btn"
248
255
  >
249
256
  <span className="text-sm font-medium text-[var(--kyro-text-primary)]">
250
- New {(collections[slug] as Record<string, unknown>)?.singularLabel || (collections[slug] as Record<string, unknown>)?.label || slug}
257
+ {t("dashboard.newAction", { defaultValue: "New {{item}}", item: (collections[slug] as Record<string, unknown>)?.singularLabel || (collections[slug] as Record<string, unknown>)?.label || slug })}
251
258
  </span>
252
259
  <Plus className="w-4 h-4 text-[var(--kyro-text-secondary)] group-hover/btn:text-[var(--kyro-primary)]" />
253
260
  </button>
254
261
  ))}
255
262
  {collectionList.every(([slug]) => permissions?.collections?.[slug]?.create === false) && (
256
- <p className="text-sm text-[var(--kyro-text-secondary)] py-2">No collections available for creation.</p>
263
+ <p className="text-sm text-[var(--kyro-text-secondary)] py-2">{t("dashboard.noCollections", { defaultValue: "No collections available for creation." })}</p>
257
264
  )}
258
265
  </div>
259
266
  </section>
@@ -261,13 +268,13 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
261
268
  <div className="surface-tile p-8 bg-[#0f172a] text-white border-none shadow-2xl shadow-blue-500/10 overflow-hidden relative group cursor-pointer">
262
269
  <div className="relative z-10">
263
270
  <h3 className="text-2xl font-bold tracking-tighter mb-2">
264
- Media Library
271
+ {t("dashboard.mediaLibrary", { defaultValue: "Media Library" })}
265
272
  </h3>
266
273
  <p className="opacity-80 text-sm font-medium mb-6">
267
- Manage high-fidelity assets with our liquid masonry gallery.
274
+ {t("dashboard.mediaLibraryDesc", { defaultValue: "Manage high-fidelity assets with our liquid masonry gallery." })}
268
275
  </p>
269
276
  <div className="flex items-center gap-2 font-bold text-xs tracking-widest text-blue-400">
270
- Open Assets{" "}
277
+ {t("dashboard.openAssets", { defaultValue: "Open Assets" })}{" "}
271
278
  <ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
272
279
  </div>
273
280
  </div>
@@ -281,10 +288,10 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
281
288
  <section className="surface-tile p-8">
282
289
  <h2 className="text-xl font-bold mb-6 tracking-tight flex items-center gap-2">
283
290
  <ClockIcon className="w-5 h-5 opacity-40" />
284
- Recent Activity
291
+ {t("dashboard.recentActivity", { defaultValue: "Recent Activity" })}
285
292
  </h2>
286
293
  <div className="space-y-6">
287
- {stats.recentActivity.map((act: Record<string, unknown>) => (
294
+ {stats.recentActivity.map((act: any) => (
288
295
  <div key={act.id} className="flex gap-4 group">
289
296
  <div className="mt-1">
290
297
  <div className="w-8 h-8 rounded-full bg-[var(--kyro-bg-secondary)] flex items-center justify-center border border-[var(--kyro-border)] group-hover:bg-[var(--kyro-primary)] transition-colors">
@@ -316,31 +323,31 @@ export function Dashboard({ collections, onNavigate, user }: DashboardProps) {
316
323
  ))}
317
324
  </div>
318
325
  <button type="button" className="w-full mt-6 py-3 text-xs font-bold tracking-widest text-[var(--kyro-text-secondary)] hover:text-[var(--kyro-text-primary)] transition-colors border-t border-[var(--kyro-border)] pt-6">
319
- View Audit Logs
326
+ {t("dashboard.viewAuditLogs", { defaultValue: "View Audit Logs" })}
320
327
  </button>
321
328
  </section>
322
329
 
323
330
  <section className="surface-tile p-8">
324
331
  <h2 className="text-xl font-bold mb-6 tracking-tight flex items-center gap-2">
325
332
  <Activity className="w-5 h-5 opacity-40" />
326
- System Status
333
+ {t("dashboard.systemStatus", { defaultValue: "System Status" })}
327
334
  </h2>
328
335
  <div className="space-y-4">
329
336
  {[
330
337
  {
331
- label: "Cloud API",
332
- status: "Optimal",
338
+ label: t("dashboard.cloudApi", { defaultValue: "Cloud API" }),
339
+ status: t("dashboard.statusOptimal", { defaultValue: "Optimal" }),
333
340
  pulse: "bg-green-500",
334
341
  },
335
342
  {
336
- label: "Database Node",
337
- status: "Healthy",
343
+ label: t("dashboard.dbNode", { defaultValue: "Database Node" }),
344
+ status: t("dashboard.statusHealthy", { defaultValue: "Healthy" }),
338
345
  pulse: "bg-green-500",
339
346
  },
340
- { label: "Media CDN", status: "Active", pulse: "bg-blue-500" },
347
+ { label: t("dashboard.mediaCdn", { defaultValue: "Media CDN" }), status: t("dashboard.statusActive", { defaultValue: "Active" }), pulse: "bg-blue-500" },
341
348
  {
342
- label: "Auth Service",
343
- status: "Secure",
349
+ label: t("dashboard.authService", { defaultValue: "Auth Service" }),
350
+ status: t("dashboard.statusSecure", { defaultValue: "Secure" }),
344
351
  pulse: "bg-green-500",
345
352
  },
346
353
  ].map((sys, index) => (