@kyro-cms/admin 0.3.1 → 0.3.4
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/EditorClient-XEUOVAAC.js +466 -0
- package/dist/EditorClient-XEUOVAAC.js.map +1 -0
- package/dist/EditorClient-YLCGVDXY.cjs +468 -0
- package/dist/EditorClient-YLCGVDXY.cjs.map +1 -0
- package/dist/chunk-7KPIUCGT.js +384 -0
- package/dist/chunk-7KPIUCGT.js.map +1 -0
- package/dist/chunk-GOACG6R7.cjs +473 -0
- package/dist/chunk-GOACG6R7.cjs.map +1 -0
- package/dist/index.cjs +14861 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +1661 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +563 -0
- package/dist/index.js +14784 -0
- package/dist/index.js.map +1 -0
- package/package.json +19 -19
- package/src/components/ActionBar.tsx +7 -43
- package/src/components/Admin.tsx +138 -277
- package/src/components/ApiKeysManager.tsx +428 -419
- package/src/components/AuditLogsPage.tsx +35 -39
- package/src/components/AuthBridge.tsx +51 -0
- package/src/components/AutoForm.tsx +495 -1230
- package/src/components/BrandingHub.tsx +18 -19
- package/src/components/BulkActionsBar.tsx +1 -1
- package/src/components/CreateView.tsx +22 -36
- package/src/components/Dashboard.tsx +60 -84
- package/src/components/DetailView.tsx +113 -91
- package/src/components/DeveloperCenter.tsx +200 -198
- package/src/components/FieldRenderer.tsx +206 -0
- package/src/components/GraphQLPlayground.tsx +340 -480
- package/src/components/ListView.tsx +828 -254
- package/src/components/LoginPage.tsx +3 -4
- package/src/components/MarketplaceManager.tsx +254 -0
- package/src/components/MediaGallery.tsx +856 -1192
- package/src/components/PluginsManager.tsx +277 -0
- package/src/components/RestPlayground.tsx +398 -560
- package/src/components/SessionsManager.tsx +211 -0
- package/src/components/Sidebar.astro +179 -151
- package/src/components/ThemeProvider.tsx +7 -161
- package/src/components/UserManagement.tsx +162 -146
- package/src/components/UserMenu.tsx +110 -0
- package/src/components/WebhookManager.tsx +305 -367
- package/src/components/blocks/AccordionBlock.tsx +4 -4
- package/src/components/blocks/ArrayBlock.tsx +3 -3
- package/src/components/blocks/BlockEditModal.tsx +8 -8
- package/src/components/blocks/BlockWrapper.tsx +61 -0
- package/src/components/blocks/ButtonBlock.tsx +4 -4
- package/src/components/blocks/ChildBlocksTree.tsx +23 -25
- package/src/components/blocks/CodeBlock.tsx +15 -15
- package/src/components/blocks/ColumnsBlock.tsx +6 -44
- package/src/components/blocks/DividerBlock.tsx +3 -3
- package/src/components/blocks/FileBlock.tsx +4 -4
- package/src/components/blocks/HeadingBlock.tsx +6 -38
- package/src/components/blocks/HeroBlock.tsx +4 -4
- package/src/components/blocks/ImageBlock.tsx +4 -4
- package/src/components/blocks/LinkBlock.tsx +4 -4
- package/src/components/blocks/ListBlock.tsx +3 -3
- package/src/components/blocks/ParagraphBlock.tsx +12 -42
- package/src/components/blocks/RelationshipBlock.tsx +4 -4
- package/src/components/blocks/RichTextBlock.tsx +4 -4
- package/src/components/blocks/VStackBlock.tsx +5 -37
- package/src/components/blocks/VideoBlock.tsx +4 -4
- package/src/components/blocks/types.ts +11 -0
- package/src/components/fields/AccordionField.tsx +1 -1
- package/src/components/fields/ArrayField.tsx +2 -2
- package/src/components/fields/ArrayLayout.tsx +93 -0
- package/src/components/fields/BlocksField.tsx +122 -111
- package/src/components/fields/ButtonField.tsx +1 -1
- package/src/components/fields/CheckboxField.tsx +14 -15
- package/src/components/fields/ChildrenField.tsx +2 -2
- package/src/components/fields/CodeField.tsx +3 -3
- package/src/components/fields/ColumnsField.tsx +2 -2
- package/src/components/fields/DateField.tsx +13 -26
- package/src/components/fields/EditorClient.tsx +26 -28
- package/src/components/fields/FieldLayout.tsx +52 -0
- package/src/components/fields/GroupLayout.tsx +35 -0
- package/src/components/fields/JSONField.tsx +7 -7
- package/src/components/fields/LinkField.tsx +1 -1
- package/src/components/fields/MarkdownField.tsx +1 -1
- package/src/components/fields/NumberField.tsx +13 -26
- package/src/components/fields/PortableTextField.tsx +4 -4
- package/src/components/fields/PortableTextRenderer.tsx +1 -1
- package/src/components/fields/RelationshipBlockField.tsx +31 -23
- package/src/components/fields/RelationshipField.tsx +14 -14
- package/src/components/fields/SelectField.tsx +17 -26
- package/src/components/fields/TabsLayout.tsx +69 -0
- package/src/components/fields/TextField.tsx +85 -38
- package/src/components/fields/UploadField.tsx +71 -41
- package/src/components/fields/VideoField.tsx +1 -1
- package/src/components/fields/extensions/blockComponents.tsx +2 -2
- package/src/components/fields/extensions/blocksStore.ts +207 -193
- package/src/components/fields/types.ts +22 -0
- package/src/components/layout/Layout.tsx +1 -1
- package/src/components/ui/ActionMenu.tsx +63 -0
- package/src/components/ui/Badge.tsx +59 -5
- package/src/components/ui/BlockDrawer.tsx +4 -5
- package/src/components/ui/CommandPalette.tsx +58 -36
- package/src/components/ui/CommandPaletteWrapper.tsx +18 -17
- package/src/components/ui/Dropdown.tsx +18 -16
- package/src/components/ui/EmptyState.tsx +25 -0
- package/src/components/ui/GlobalModal.tsx +49 -0
- package/src/components/ui/IconButton.tsx +44 -0
- package/src/components/ui/Modal.tsx +19 -20
- package/src/components/ui/PageHeader.tsx +158 -0
- package/src/components/ui/Pagination.tsx +61 -0
- package/src/components/ui/PromptModal.tsx +1 -1
- package/src/components/ui/SearchInput.tsx +57 -0
- package/src/components/ui/SeoPreview.tsx +31 -0
- package/src/components/ui/SessionModal.tsx +0 -0
- package/src/components/ui/SlidePanel.tsx +2 -0
- package/src/components/ui/Toast.tsx +65 -122
- package/src/components/ui/Toaster.tsx +18 -0
- package/src/components/ui/icons.tsx +112 -0
- package/src/components/users/UserDetail.tsx +290 -0
- package/src/components/users/UserForm.tsx +242 -0
- package/src/components/users/UsersList.tsx +338 -0
- package/src/env.d.ts +13 -13
- package/src/fields/index.ts +2 -1
- package/src/global.d.ts +7 -0
- package/src/hooks/data.ts +2 -9
- package/src/hooks/useAsyncData.ts +36 -0
- package/src/hooks/useAutoFormState.ts +527 -0
- package/src/hooks/useSelection.ts +49 -0
- package/src/hooks/useSession.ts +0 -0
- package/src/index.ts +11 -1
- package/src/integration.ts +86 -11
- package/src/kyro-cms.d.ts +209 -0
- package/src/layouts/AdminLayout.astro +128 -11
- package/src/layouts/AuthLayout.astro +21 -5
- package/src/lib/api.ts +175 -55
- package/src/lib/autoform-store.ts +435 -0
- package/src/lib/config.ts +82 -34
- package/src/lib/createRegistry.ts +29 -0
- package/src/lib/default-kyro-config.ts +4 -0
- package/src/lib/globals.ts +50 -0
- package/src/lib/media-utils.ts +18 -0
- package/src/lib/object-utils.ts +77 -0
- package/src/lib/paths.ts +61 -0
- package/src/lib/stores/index.ts +370 -0
- package/src/lib/types.ts +43 -0
- package/src/lib/useResourceManager.ts +105 -0
- package/src/pages/403.astro +67 -0
- package/src/pages/[collection]/[id].astro +14 -180
- package/src/pages/[collection]/index.astro +11 -6
- package/src/pages/api-explorer.astro +173 -0
- package/src/pages/audit/index.astro +2 -0
- package/src/pages/auth/login.astro +122 -0
- package/src/pages/auth/register.astro +167 -0
- package/src/pages/graphql-explorer.astro +59 -0
- package/src/pages/{admin/graphql.astro → graphql.astro} +51 -17
- package/src/pages/index.astro +577 -0
- package/src/pages/index_ALT.astro +3 -0
- package/src/pages/keys.astro +11 -0
- package/src/pages/marketplace.astro +11 -0
- package/src/pages/media.astro +3 -0
- package/src/pages/plugins.astro +8 -0
- package/src/pages/preview/[collection]/[id].astro +188 -123
- package/src/pages/rest-playground.astro +62 -0
- package/src/pages/roles/index.astro +183 -76
- package/src/pages/sessions.astro +8 -0
- package/src/pages/settings/[slug].astro +92 -114
- package/src/pages/settings/index.astro +5 -3
- package/src/pages/users/[id].astro +25 -154
- package/src/pages/users/index.astro +19 -130
- package/src/pages/users/new.astro +9 -86
- package/src/pages/webhooks.astro +11 -0
- package/src/routes.ts +80 -0
- package/src/styles/main.css +119 -79
- package/src/theme/tokens.ts +1 -0
- package/src/vite-env.d.ts +14 -0
- package/src/collections/auth/index.ts +0 -155
- package/src/collections/portfolio/index.ts +0 -343
- package/src/components/ApiExplorer.tsx +0 -325
- package/src/components/EnhancedListView.tsx +0 -889
- package/src/components/GraphQLExplorer.tsx +0 -675
- package/src/components/Icons.tsx +0 -23
- package/src/components/StatusBadge.tsx +0 -76
- package/src/lib/MediaService.ts +0 -541
- package/src/lib/auth/sqlite-adapter.ts +0 -319
- package/src/lib/dataStore.ts +0 -226
- package/src/lib/db/adapter.ts +0 -54
- package/src/lib/db/drizzle-mysql-adapter.ts +0 -194
- package/src/lib/db/drizzle-mysql-auth-adapter.ts +0 -327
- package/src/lib/db/drizzle-postgres-adapter.ts +0 -202
- package/src/lib/db/drizzle-postgres-auth-adapter.ts +0 -304
- package/src/lib/db/drizzle-sqlite-adapter.ts +0 -227
- package/src/lib/db/drizzle-sqlite-auth-adapter.ts +0 -548
- package/src/lib/db/index.ts +0 -449
- package/src/lib/db/mongodb-adapter.ts +0 -207
- package/src/lib/db/mongodb-auth-adapter.ts +0 -305
- package/src/lib/db/schema/mysql-auth.ts +0 -113
- package/src/lib/db/schema/mysql-content.ts +0 -20
- package/src/lib/db/schema/postgres-auth.ts +0 -116
- package/src/lib/db/schema/postgres-content.ts +0 -35
- package/src/lib/db/schema/postgres-media.ts +0 -52
- package/src/lib/db/schema/postgres-settings.ts +0 -11
- package/src/lib/db/schema/sqlite-auth.ts +0 -112
- package/src/lib/db/schema/sqlite-content.ts +0 -20
- package/src/lib/db/version-adapter.ts +0 -248
- package/src/lib/graphql/index.ts +0 -1
- package/src/lib/graphql/schema.ts +0 -443
- package/src/lib/rate-limit.ts +0 -267
- package/src/lib/storage.ts +0 -374
- package/src/lib/store.ts +0 -85
- package/src/middleware.ts +0 -177
- package/src/pages/admin/api-explorer.astro +0 -98
- package/src/pages/admin/graphql-explorer.astro +0 -40
- package/src/pages/admin/index.astro +0 -286
- package/src/pages/admin/keys.astro +0 -8
- package/src/pages/admin/rest-playground.astro +0 -44
- package/src/pages/admin/webhooks.astro +0 -8
- package/src/pages/api/[collection]/[id]/publish.ts +0 -52
- package/src/pages/api/[collection]/[id]/unpublish.ts +0 -42
- package/src/pages/api/[collection]/[id]/versions.ts +0 -66
- package/src/pages/api/[collection]/[id].ts +0 -213
- package/src/pages/api/[collection]/index.ts +0 -209
- package/src/pages/api/auth/[id].ts +0 -121
- package/src/pages/api/auth/audit-logs.ts +0 -57
- package/src/pages/api/auth/login.ts +0 -211
- package/src/pages/api/auth/logout.ts +0 -66
- package/src/pages/api/auth/me.ts +0 -36
- package/src/pages/api/auth/refresh.ts +0 -119
- package/src/pages/api/auth/register.ts +0 -188
- package/src/pages/api/auth/users.ts +0 -97
- package/src/pages/api/collections.ts +0 -59
- package/src/pages/api/globals/[slug].ts +0 -42
- package/src/pages/api/graphql.ts +0 -90
- package/src/pages/api/health.ts +0 -426
- package/src/pages/api/keys/[id].ts +0 -26
- package/src/pages/api/keys/index.ts +0 -75
- package/src/pages/api/media/[id].ts +0 -309
- package/src/pages/api/media/folders.ts +0 -609
- package/src/pages/api/media/index.ts +0 -146
- package/src/pages/api/media/resize.ts +0 -267
- package/src/pages/api/search.ts +0 -82
- package/src/pages/api/slug-availability.ts +0 -70
- package/src/pages/api/storage-config.ts +0 -20
- package/src/pages/api/storage-status.ts +0 -206
- package/src/pages/api/upload.ts +0 -334
- package/src/pages/api/webhooks/index.ts +0 -71
- package/src/pages/login.astro +0 -82
- package/src/pages/register.astro +0 -102
|
@@ -10,7 +10,7 @@ interface LocalToast {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
interface LoginPageProps {
|
|
13
|
-
onAuth: (token: string, user:
|
|
13
|
+
onAuth: (token: string, user: Record<string, unknown>) => void;
|
|
14
14
|
theme?: ThemeMode;
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -31,7 +31,7 @@ export function LoginPage({ onAuth, theme = "light" }: LoginPageProps) {
|
|
|
31
31
|
|
|
32
32
|
const checkIfFirstUser = async () => {
|
|
33
33
|
try {
|
|
34
|
-
await apiGet("/api/
|
|
34
|
+
await apiGet("/api/users");
|
|
35
35
|
} catch {
|
|
36
36
|
setIsFirstUser(true);
|
|
37
37
|
setMode("register");
|
|
@@ -58,13 +58,12 @@ export function LoginPage({ onAuth, theme = "light" }: LoginPageProps) {
|
|
|
58
58
|
body.confirmPassword = confirmPassword;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
const data = await apiPost(endpoint, body);
|
|
61
|
+
const data = await apiPost<any>(endpoint, body);
|
|
62
62
|
|
|
63
63
|
if (data.isFirstUser) {
|
|
64
64
|
setIsFirstUser(true);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
localStorage.setItem("kyro_token", data.token);
|
|
68
67
|
localStorage.setItem("kyro_user", JSON.stringify(data.user));
|
|
69
68
|
addToast(
|
|
70
69
|
"success",
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Search,
|
|
4
|
+
DownloadCloud,
|
|
5
|
+
Star,
|
|
6
|
+
CheckCircle2,
|
|
7
|
+
TrendingUp,
|
|
8
|
+
Zap,
|
|
9
|
+
X,
|
|
10
|
+
ExternalLink,
|
|
11
|
+
ChevronRight
|
|
12
|
+
} from "./ui/icons";
|
|
13
|
+
import { useUIStore, toast } from "../lib/stores";
|
|
14
|
+
import { Badge } from "./ui/Badge";
|
|
15
|
+
|
|
16
|
+
interface Extension {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
developer: string;
|
|
21
|
+
rating: number;
|
|
22
|
+
downloads: string;
|
|
23
|
+
price: string;
|
|
24
|
+
tags: string[];
|
|
25
|
+
installed: boolean;
|
|
26
|
+
featured?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const mockExtensions: Extension[] = [
|
|
30
|
+
{
|
|
31
|
+
id: "ext-ecommerce",
|
|
32
|
+
name: "Commerce Suite",
|
|
33
|
+
description: "Full e-commerce with cart, checkout, Stripe, and inventory management.",
|
|
34
|
+
developer: "Kyro Official",
|
|
35
|
+
rating: 4.9,
|
|
36
|
+
downloads: "12k+",
|
|
37
|
+
price: "Free",
|
|
38
|
+
tags: ["E-commerce", "Stripe"],
|
|
39
|
+
installed: false,
|
|
40
|
+
featured: true,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "ext-seo",
|
|
44
|
+
name: "SEO Optimizer Pro",
|
|
45
|
+
description: "Meta tags, sitemaps, and rich snippets for better search visibility.",
|
|
46
|
+
developer: "Kyro Team",
|
|
47
|
+
rating: 4.8,
|
|
48
|
+
downloads: "45k+",
|
|
49
|
+
price: "Free",
|
|
50
|
+
tags: ["SEO", "Marketing"],
|
|
51
|
+
installed: true,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "ext-algolia",
|
|
55
|
+
name: "Algolia Search",
|
|
56
|
+
description: "Fast search with Algolia for your collections and site.",
|
|
57
|
+
developer: "SearchBots",
|
|
58
|
+
rating: 4.6,
|
|
59
|
+
downloads: "5k+",
|
|
60
|
+
price: "$19/mo",
|
|
61
|
+
tags: ["Search", "API"],
|
|
62
|
+
installed: false,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "ext-openai",
|
|
66
|
+
name: "AI Content Writer",
|
|
67
|
+
description: "Generate content with GPT models directly in the editor.",
|
|
68
|
+
developer: "AI Tools",
|
|
69
|
+
rating: 4.7,
|
|
70
|
+
downloads: "8k+",
|
|
71
|
+
price: "$9/mo",
|
|
72
|
+
tags: ["AI", "Content"],
|
|
73
|
+
installed: false,
|
|
74
|
+
featured: true,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "ext-backup",
|
|
78
|
+
name: "Auto Backup",
|
|
79
|
+
description: "Scheduled backups to S3, Dropbox, or Google Drive.",
|
|
80
|
+
developer: "SafeData",
|
|
81
|
+
rating: 4.9,
|
|
82
|
+
downloads: "22k+",
|
|
83
|
+
price: "Free",
|
|
84
|
+
tags: ["Utility", "Security"],
|
|
85
|
+
installed: false,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: "ext-slack",
|
|
89
|
+
name: "Slack Notifications",
|
|
90
|
+
description: "Get notified in Slack when content changes.",
|
|
91
|
+
developer: "Kyro Team",
|
|
92
|
+
rating: 4.5,
|
|
93
|
+
downloads: "3k+",
|
|
94
|
+
price: "Free",
|
|
95
|
+
tags: ["Notifications"],
|
|
96
|
+
installed: false,
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
export function MarketplaceManager() {
|
|
101
|
+
const [extensions, setExtensions] = useState<Extension[]>(mockExtensions);
|
|
102
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
103
|
+
const [activeCategory, setActiveCategory] = useState("All");
|
|
104
|
+
const { confirm, alert } = useUIStore();
|
|
105
|
+
|
|
106
|
+
const categories = ["All", "Official", "E-commerce", "SEO", "AI", "Utility"];
|
|
107
|
+
|
|
108
|
+
const handleInstallRequest = (ext: Extension) => {
|
|
109
|
+
confirm({
|
|
110
|
+
title: `Integrate ${ext.name}?`,
|
|
111
|
+
message: `Connect ${ext.name} to your dashboard? It will have scoped access to your data.`,
|
|
112
|
+
confirmLabel: "Connect Extension",
|
|
113
|
+
onConfirm: async () => {
|
|
114
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
115
|
+
setExtensions((prev) =>
|
|
116
|
+
prev.map((e) => e.id === ext.id ? { ...e, installed: true } : e)
|
|
117
|
+
);
|
|
118
|
+
toast.success(`Extension initialized: ${ext.name}`);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const filteredExtensions = extensions.filter((ext) => {
|
|
124
|
+
const matchesSearch =
|
|
125
|
+
ext.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
126
|
+
ext.description.toLowerCase().includes(searchQuery.toLowerCase());
|
|
127
|
+
const matchesCategory =
|
|
128
|
+
activeCategory === "All" ||
|
|
129
|
+
ext.tags.includes(activeCategory) ||
|
|
130
|
+
(activeCategory === "Official" && ext.developer.includes("Kyro"));
|
|
131
|
+
return matchesSearch && matchesCategory;
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<div className="w-full space-y-6 animate-in fade-in duration-700">
|
|
136
|
+
{/* Header Panel */}
|
|
137
|
+
<div className="surface-tile p-5 flex flex-col md:flex-row md:items-center justify-between gap-6">
|
|
138
|
+
<div>
|
|
139
|
+
<div className="flex items-center gap-2 mb-1">
|
|
140
|
+
<div className="w-2 h-2 rounded-full bg-amber-500 shadow-[0_0_8px_rgba(245,158,11,0.5)]" />
|
|
141
|
+
<h1 className="text-lg font-bold tracking-tight text-[var(--kyro-text-primary)]">
|
|
142
|
+
Extension Marketplace
|
|
143
|
+
</h1>
|
|
144
|
+
</div>
|
|
145
|
+
<p className="text-[10px] font-bold text-[var(--kyro-text-secondary)] opacity-50 tracking-widest uppercase">
|
|
146
|
+
Expand your ecosystem with community extensions
|
|
147
|
+
</p>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<div className="flex items-center gap-3">
|
|
151
|
+
<div className="relative group">
|
|
152
|
+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-[var(--kyro-text-secondary)] opacity-40 group-focus-within:opacity-100 transition-opacity" />
|
|
153
|
+
<input
|
|
154
|
+
type="text"
|
|
155
|
+
placeholder="Search marketplace..."
|
|
156
|
+
value={searchQuery}
|
|
157
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
158
|
+
className="pl-9 pr-4 py-2 bg-[var(--kyro-surface-accent)] border border-[var(--kyro-border)] rounded-xl text-xs font-bold w-64 focus:outline-none focus:ring-2 focus:ring-[var(--kyro-sidebar-active)] transition-all"
|
|
159
|
+
/>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
{/* Categories */}
|
|
165
|
+
<div className="flex items-center gap-1.5 overflow-x-auto pb-1 no-scrollbar">
|
|
166
|
+
{categories.map((category) => (
|
|
167
|
+
<button
|
|
168
|
+
key={category}
|
|
169
|
+
onClick={() => setActiveCategory(category)}
|
|
170
|
+
className={`px-4 py-1.5 rounded-xl text-[10px] font-bold tracking-widest uppercase transition-all border ${activeCategory === category
|
|
171
|
+
? "bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)] border-[var(--kyro-sidebar-active)] shadow-lg"
|
|
172
|
+
: "bg-[var(--kyro-surface-accent)] text-[var(--kyro-text-secondary)] border-[var(--kyro-border)] hover:bg-[var(--kyro-surface)]"
|
|
173
|
+
}`}
|
|
174
|
+
>
|
|
175
|
+
{category}
|
|
176
|
+
</button>
|
|
177
|
+
))}
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
{/* Extension Grid */}
|
|
181
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
|
182
|
+
{filteredExtensions.length > 0 ? (
|
|
183
|
+
filteredExtensions.map((ext) => (
|
|
184
|
+
<div
|
|
185
|
+
key={ext.id}
|
|
186
|
+
className="surface-tile p-5 group hover:shadow-xl transition-all duration-300 relative flex flex-col"
|
|
187
|
+
>
|
|
188
|
+
<div className="flex items-start justify-between mb-4">
|
|
189
|
+
<div className="w-10 h-10 rounded-xl bg-[var(--kyro-surface-accent)] border border-[var(--kyro-border)] flex items-center justify-center text-[var(--kyro-text-primary)] group-hover:scale-105 transition-transform">
|
|
190
|
+
<DownloadCloud className="w-5 h-5 opacity-70" />
|
|
191
|
+
</div>
|
|
192
|
+
{ext.featured && (
|
|
193
|
+
<Badge variant="warning" className="text-[8px] font-bold tracking-widest uppercase">
|
|
194
|
+
FEATURED
|
|
195
|
+
</Badge>
|
|
196
|
+
)}
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<div className="mb-4">
|
|
200
|
+
<h3 className="text-xs font-bold text-[var(--kyro-text-primary)] mb-1 group-hover:text-[var(--kyro-primary)] transition-colors">
|
|
201
|
+
{ext.name}
|
|
202
|
+
</h3>
|
|
203
|
+
<p className="text-[9px] font-bold text-[var(--kyro-text-secondary)] opacity-50 tracking-widest uppercase mb-3">
|
|
204
|
+
BY {ext.developer}
|
|
205
|
+
</p>
|
|
206
|
+
<p className="text-[11px] text-[var(--kyro-text-secondary)] line-clamp-2 leading-relaxed">
|
|
207
|
+
{ext.description}
|
|
208
|
+
</p>
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
<div className="flex items-center gap-4 mb-5">
|
|
212
|
+
<div className="flex items-center gap-1">
|
|
213
|
+
<Star className="w-3 h-3 text-amber-500 fill-amber-500" />
|
|
214
|
+
<span className="text-[10px] font-bold text-[var(--kyro-text-primary)]">{ext.rating}</span>
|
|
215
|
+
</div>
|
|
216
|
+
<div className="flex items-center gap-1 text-[var(--kyro-text-secondary)] opacity-50">
|
|
217
|
+
<TrendingUp className="w-3 h-3" />
|
|
218
|
+
<span className="text-[10px] font-bold">{ext.downloads}</span>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
<div className="mt-auto pt-4 border-t border-[var(--kyro-border)] flex items-center justify-between">
|
|
223
|
+
<span className="text-[10px] font-bold text-[var(--kyro-text-primary)] uppercase tracking-widest">
|
|
224
|
+
{ext.price}
|
|
225
|
+
</span>
|
|
226
|
+
{ext.installed ? (
|
|
227
|
+
<div className="flex items-center gap-1.5 px-2 py-1 rounded-lg bg-green-500/10 text-green-500 text-[9px] font-bold tracking-widest uppercase">
|
|
228
|
+
<CheckCircle2 className="w-3 h-3" />
|
|
229
|
+
Active
|
|
230
|
+
</div>
|
|
231
|
+
) : (
|
|
232
|
+
<button
|
|
233
|
+
type="button"
|
|
234
|
+
onClick={() => handleInstallRequest(ext)}
|
|
235
|
+
className="flex items-center gap-1.5 text-[10px] font-bold text-[var(--kyro-primary)] hover:translate-x-1 transition-transform"
|
|
236
|
+
>
|
|
237
|
+
Install <ChevronRight className="w-3 h-3" />
|
|
238
|
+
</button>
|
|
239
|
+
)}
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
))
|
|
243
|
+
) : (
|
|
244
|
+
<div className="col-span-full py-20 text-center surface-tile">
|
|
245
|
+
<Search className="w-10 h-10 mx-auto mb-4 text-[var(--kyro-text-secondary)] opacity-20" />
|
|
246
|
+
<p className="text-xs font-bold text-[var(--kyro-text-secondary)] opacity-50 tracking-widest uppercase">
|
|
247
|
+
No results match your search
|
|
248
|
+
</p>
|
|
249
|
+
</div>
|
|
250
|
+
)}
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
);
|
|
254
|
+
}
|