@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.
- package/dist/index.cjs +64 -26
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +64 -26
- package/package.json +9 -3
- package/src/components/ActionBar.tsx +16 -16
- package/src/components/ApiHealth.tsx +522 -0
- package/src/components/ApiKeysManager.tsx +6 -4
- package/src/components/AuditLogsPage.tsx +4 -2
- package/src/components/AutoForm.tsx +10 -7
- package/src/components/BrandingHub.tsx +3 -1
- package/src/components/Dashboard.tsx +44 -37
- package/src/components/DashboardMetrics.tsx +168 -58
- package/src/components/DetailView.tsx +41 -38
- package/src/components/DeveloperCenter.tsx +5 -3
- package/src/components/GraphQLPlayground.tsx +10 -8
- package/src/components/ListView.tsx +66 -45
- package/src/components/LoginPage.tsx +21 -19
- package/src/components/MarketplaceManager.tsx +3 -1
- package/src/components/MediaGallery.tsx +12 -9
- package/src/components/PluginsManager.tsx +6 -4
- package/src/components/RestPlayground.tsx +8 -6
- package/src/components/SessionsManager.tsx +3 -1
- package/src/components/Sidebar.astro +180 -92
- package/src/components/UserManagement.tsx +8 -6
- package/src/components/UserMenu.tsx +19 -17
- package/src/components/VersionHistoryPanel.tsx +6 -4
- package/src/components/WebhookManager.tsx +11 -10
- package/src/components/autoform/AutoFormApiView.tsx +3 -1
- package/src/components/autoform/AutoFormEditView.tsx +3 -1
- package/src/components/autoform/AutoFormHeader.tsx +6 -6
- package/src/components/blocks/AccordionBlock.tsx +5 -3
- package/src/components/blocks/ArrayBlock.tsx +5 -3
- package/src/components/blocks/BlockWrapper.tsx +5 -3
- package/src/components/blocks/CodeBlock.tsx +4 -2
- package/src/components/blocks/FileBlock.tsx +5 -3
- package/src/components/blocks/HeroBlock.tsx +5 -3
- package/src/components/blocks/ImageBlock.tsx +6 -4
- package/src/components/blocks/ListBlock.tsx +5 -3
- package/src/components/blocks/ParagraphBlock.tsx +3 -1
- package/src/components/blocks/RelationshipBlock.tsx +5 -3
- package/src/components/blocks/RichTextBlock.tsx +5 -3
- package/src/components/blocks/VideoBlock.tsx +5 -3
- package/src/components/fields/AccordionField.tsx +10 -51
- package/src/components/fields/ArrayField.tsx +9 -7
- package/src/components/fields/ArrayLayout.tsx +3 -1
- package/src/components/fields/BlocksField.tsx +24 -75
- package/src/components/fields/ButtonField.tsx +3 -1
- package/src/components/fields/CardField.tsx +6 -4
- package/src/components/fields/GroupLayout.tsx +2 -49
- package/src/components/fields/HeadingField.tsx +3 -1
- package/src/components/fields/HeadingSubheadingField.tsx +4 -2
- package/src/components/fields/HeroField.tsx +8 -6
- package/src/components/fields/JSONField.tsx +9 -10
- package/src/components/fields/LinkField.tsx +4 -2
- package/src/components/fields/ListField.tsx +3 -1
- package/src/components/fields/MarkdownField.tsx +3 -17
- package/src/components/fields/RelationshipBlockField.tsx +7 -5
- package/src/components/fields/RelationshipField.tsx +3 -1
- package/src/components/fields/RichTextField.tsx +19 -16
- package/src/components/fields/SecretField.tsx +9 -69
- package/src/components/fields/SelectField.tsx +11 -6
- package/src/components/fields/TabsLayout.tsx +2 -48
- package/src/components/fields/TextField.tsx +9 -7
- package/src/components/fields/UploadField.tsx +3 -1
- package/src/components/fields/VideoField.tsx +6 -4
- package/src/components/ui/BlockDrawer.tsx +3 -1
- package/src/components/ui/CommandPalette.tsx +9 -7
- package/src/components/ui/CommandPaletteWrapper.tsx +3 -1
- package/src/components/ui/IconPickerModal.tsx +4 -2
- package/src/components/ui/PageHeader.tsx +48 -19
- package/src/components/users/UserDetail.tsx +3 -1
- package/src/components/users/UserForm.tsx +5 -3
- package/src/fields/examples/sample-field-2.tsx +3 -1
- package/src/fields/examples/sample-field.tsx +3 -1
- package/src/hooks/useAutoFormState.ts +19 -11
- package/src/integration.ts +21 -0
- package/src/layouts/AdminLayout.astro +252 -189
- package/src/layouts/AuthLayout.astro +52 -31
- package/src/lib/api.ts +7 -7
- package/src/lib/deep-equal.ts +3 -3
- package/src/lib/i18n.ts +44 -0
- package/src/lib/vite-shim-plugin.ts +3 -0
- package/src/locales/de.json +163 -0
- package/src/locales/en.json +163 -0
- package/src/locales/es.json +163 -0
- package/src/locales/fr.json +163 -0
- package/src/locales/pt.json +163 -0
- package/src/pages/403.astro +2 -1
- package/src/pages/[collection]/[id].astro +23 -10
- package/src/pages/[collection]/index.astro +48 -34
- package/src/pages/audit/index.astro +2 -1
- package/src/pages/graphql-explorer.astro +2 -1
- package/src/pages/graphql.astro +2 -1
- package/src/pages/health.astro +9 -0
- package/src/pages/index.astro +6 -5
- package/src/pages/keys.astro +2 -1
- package/src/pages/marketplace.astro +2 -1
- package/src/pages/media.astro +2 -1
- package/src/pages/plugins.astro +2 -1
- package/src/pages/rest-playground.astro +2 -1
- package/src/pages/roles/index.astro +16 -52
- package/src/pages/sessions.astro +2 -1
- package/src/pages/settings/[slug].astro +45 -30
- package/src/pages/users/[id].astro +1 -0
- package/src/pages/users/index.astro +2 -1
- package/src/pages/users/new.astro +2 -1
- package/src/pages/webhooks.astro +2 -1
- 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
|
-
|
|
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
|
-
|
|
634
|
-
|
|
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
|
-
|
|
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={
|
|
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,
|
|
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
|
|
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:
|
|
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) => (
|