@nsxbet/admin-sdk 0.1.0
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/README.md +680 -0
- package/dist/auth/client/in-memory.d.ts +27 -0
- package/dist/auth/client/in-memory.d.ts.map +1 -0
- package/dist/auth/client/in-memory.js +242 -0
- package/dist/auth/client/index.d.ts +7 -0
- package/dist/auth/client/index.d.ts.map +1 -0
- package/dist/auth/client/index.js +7 -0
- package/dist/auth/client/interface.d.ts +115 -0
- package/dist/auth/client/interface.d.ts.map +1 -0
- package/dist/auth/client/interface.js +7 -0
- package/dist/auth/client/keycloak.d.ts +19 -0
- package/dist/auth/client/keycloak.d.ts.map +1 -0
- package/dist/auth/client/keycloak.js +126 -0
- package/dist/auth/components/UserSelector.d.ts +19 -0
- package/dist/auth/components/UserSelector.d.ts.map +1 -0
- package/dist/auth/components/UserSelector.js +100 -0
- package/dist/auth/components/index.d.ts +5 -0
- package/dist/auth/components/index.d.ts.map +1 -0
- package/dist/auth/components/index.js +4 -0
- package/dist/auth/index.d.ts +7 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +7 -0
- package/dist/components/AuthProvider.d.ts +48 -0
- package/dist/components/AuthProvider.d.ts.map +1 -0
- package/dist/components/AuthProvider.js +117 -0
- package/dist/hooks/useAuth.d.ts +21 -0
- package/dist/hooks/useAuth.d.ts.map +1 -0
- package/dist/hooks/useAuth.js +34 -0
- package/dist/hooks/useFetch.d.ts +8 -0
- package/dist/hooks/useFetch.d.ts.map +1 -0
- package/dist/hooks/useFetch.js +31 -0
- package/dist/hooks/useI18n.d.ts +46 -0
- package/dist/hooks/useI18n.d.ts.map +1 -0
- package/dist/hooks/useI18n.js +95 -0
- package/dist/hooks/usePlatformAPI.d.ts +12 -0
- package/dist/hooks/usePlatformAPI.d.ts.map +1 -0
- package/dist/hooks/usePlatformAPI.js +10 -0
- package/dist/hooks/useTelemetry.d.ts +17 -0
- package/dist/hooks/useTelemetry.d.ts.map +1 -0
- package/dist/hooks/useTelemetry.js +36 -0
- package/dist/i18n/config.d.ts +26 -0
- package/dist/i18n/config.d.ts.map +1 -0
- package/dist/i18n/config.js +92 -0
- package/dist/i18n/index.d.ts +6 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +4 -0
- package/dist/i18n/locales/en-US.json +144 -0
- package/dist/i18n/locales/es.json +144 -0
- package/dist/i18n/locales/pt-BR.json +144 -0
- package/dist/i18n/locales/ro.json +144 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/registry/AdminShellRegistry.d.ts +140 -0
- package/dist/registry/AdminShellRegistry.d.ts.map +1 -0
- package/dist/registry/AdminShellRegistry.js +237 -0
- package/dist/registry/client/http.d.ts +21 -0
- package/dist/registry/client/http.d.ts.map +1 -0
- package/dist/registry/client/http.js +107 -0
- package/dist/registry/client/in-memory.d.ts +36 -0
- package/dist/registry/client/in-memory.d.ts.map +1 -0
- package/dist/registry/client/in-memory.js +242 -0
- package/dist/registry/client/index.d.ts +7 -0
- package/dist/registry/client/index.d.ts.map +1 -0
- package/dist/registry/client/index.js +5 -0
- package/dist/registry/client/interface.d.ts +96 -0
- package/dist/registry/client/interface.d.ts.map +1 -0
- package/dist/registry/client/interface.js +7 -0
- package/dist/registry/index.d.ts +12 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +8 -0
- package/dist/registry/types/index.d.ts +9 -0
- package/dist/registry/types/index.d.ts.map +1 -0
- package/dist/registry/types/index.js +6 -0
- package/dist/registry/types/manifest.d.ts +98 -0
- package/dist/registry/types/manifest.d.ts.map +1 -0
- package/dist/registry/types/manifest.js +81 -0
- package/dist/registry/types/module.d.ts +115 -0
- package/dist/registry/types/module.d.ts.map +1 -0
- package/dist/registry/types/module.js +6 -0
- package/dist/router/DynamicModule.d.ts +50 -0
- package/dist/router/DynamicModule.d.ts.map +1 -0
- package/dist/router/DynamicModule.js +141 -0
- package/dist/router/index.d.ts +2 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +1 -0
- package/dist/shell/AdminShell.d.ts +38 -0
- package/dist/shell/AdminShell.d.ts.map +1 -0
- package/dist/shell/AdminShell.js +299 -0
- package/dist/shell/BackofficeShell.d.ts +38 -0
- package/dist/shell/BackofficeShell.d.ts.map +1 -0
- package/dist/shell/BackofficeShell.js +299 -0
- package/dist/shell/components/CommandPalette.d.ts +8 -0
- package/dist/shell/components/CommandPalette.d.ts.map +1 -0
- package/dist/shell/components/CommandPalette.js +197 -0
- package/dist/shell/components/HomePage.d.ts +2 -0
- package/dist/shell/components/HomePage.d.ts.map +1 -0
- package/dist/shell/components/HomePage.js +32 -0
- package/dist/shell/components/LeftNav.d.ts +7 -0
- package/dist/shell/components/LeftNav.d.ts.map +1 -0
- package/dist/shell/components/LeftNav.js +247 -0
- package/dist/shell/components/MainContent.d.ts +9 -0
- package/dist/shell/components/MainContent.d.ts.map +1 -0
- package/dist/shell/components/MainContent.js +88 -0
- package/dist/shell/components/ModuleOverview.d.ts +7 -0
- package/dist/shell/components/ModuleOverview.d.ts.map +1 -0
- package/dist/shell/components/ModuleOverview.js +40 -0
- package/dist/shell/components/ProfilePage.d.ts +2 -0
- package/dist/shell/components/ProfilePage.d.ts.map +1 -0
- package/dist/shell/components/ProfilePage.js +30 -0
- package/dist/shell/components/RegistryPage.d.ts +8 -0
- package/dist/shell/components/RegistryPage.d.ts.map +1 -0
- package/dist/shell/components/RegistryPage.js +129 -0
- package/dist/shell/components/SettingsPage.d.ts +2 -0
- package/dist/shell/components/SettingsPage.d.ts.map +1 -0
- package/dist/shell/components/SettingsPage.js +60 -0
- package/dist/shell/components/TopBar.d.ts +8 -0
- package/dist/shell/components/TopBar.d.ts.map +1 -0
- package/dist/shell/components/TopBar.js +61 -0
- package/dist/shell/components/index.d.ts +10 -0
- package/dist/shell/components/index.d.ts.map +1 -0
- package/dist/shell/components/index.js +7 -0
- package/dist/shell/components/theme-provider.d.ts +15 -0
- package/dist/shell/components/theme-provider.d.ts.map +1 -0
- package/dist/shell/components/theme-provider.js +39 -0
- package/dist/shell/index.d.ts +9 -0
- package/dist/shell/index.d.ts.map +1 -0
- package/dist/shell/index.js +8 -0
- package/dist/shell/search/fuzzy.d.ts +18 -0
- package/dist/shell/search/fuzzy.d.ts.map +1 -0
- package/dist/shell/search/fuzzy.js +121 -0
- package/dist/shell/search/index.d.ts +3 -0
- package/dist/shell/search/index.d.ts.map +1 -0
- package/dist/shell/search/index.js +1 -0
- package/dist/shell/telemetry.d.ts +7 -0
- package/dist/shell/telemetry.d.ts.map +1 -0
- package/dist/shell/telemetry.js +25 -0
- package/dist/shell/types.d.ts +110 -0
- package/dist/shell/types.d.ts.map +1 -0
- package/dist/shell/types.js +4 -0
- package/dist/tailwind/index.d.ts +20 -0
- package/dist/tailwind/index.d.ts.map +1 -0
- package/dist/tailwind/index.js +42 -0
- package/dist/types/keycloak.d.ts +26 -0
- package/dist/types/keycloak.d.ts.map +1 -0
- package/dist/types/keycloak.js +1 -0
- package/dist/types/platform.d.ts +83 -0
- package/dist/types/platform.d.ts.map +1 -0
- package/dist/types/platform.js +5 -0
- package/dist/vite/config.d.ts +71 -0
- package/dist/vite/config.d.ts.map +1 -0
- package/dist/vite/config.js +87 -0
- package/dist/vite/index.d.ts +18 -0
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +17 -0
- package/dist/vite/plugins.d.ts +44 -0
- package/dist/vite/plugins.d.ts.map +1 -0
- package/dist/vite/plugins.js +74 -0
- package/package.json +86 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useLocation, useNavigate } from "react-router-dom";
|
|
3
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Badge, Icon, } from "@nsxbet/admin-ui";
|
|
4
|
+
import { useI18n } from "../../hooks/useI18n";
|
|
5
|
+
export function ModuleOverview({ modules }) {
|
|
6
|
+
const location = useLocation();
|
|
7
|
+
const navigate = useNavigate();
|
|
8
|
+
const { t, i18n } = useI18n();
|
|
9
|
+
// Helper to translate titles
|
|
10
|
+
// titleKey format: "namespace:key" (e.g., "tasks:module.title")
|
|
11
|
+
const translateTitle = (title, titleKey) => {
|
|
12
|
+
if (titleKey && titleKey.includes(':')) {
|
|
13
|
+
// Parse "namespace:key" format
|
|
14
|
+
const [ns, key] = titleKey.split(':');
|
|
15
|
+
// Use i18n.t with explicit namespace option
|
|
16
|
+
const translated = i18n.t(key, { ns, defaultValue: title });
|
|
17
|
+
return translated;
|
|
18
|
+
}
|
|
19
|
+
if (titleKey) {
|
|
20
|
+
// Fallback: try using the titleKey directly
|
|
21
|
+
const translated = t(titleKey, { defaultValue: title });
|
|
22
|
+
return translated;
|
|
23
|
+
}
|
|
24
|
+
return title;
|
|
25
|
+
};
|
|
26
|
+
// Extract moduleId from path: /_modules/xxx -> xxx
|
|
27
|
+
const moduleId = location.pathname.replace(/^\/_modules\//, "");
|
|
28
|
+
const module = modules.find((m) => m.id === moduleId);
|
|
29
|
+
if (!module) {
|
|
30
|
+
return (_jsx("div", { className: "p-6 max-w-3xl mx-auto space-y-6", children: _jsx(Card, { className: "border-destructive", children: _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "flex items-center gap-3 text-destructive", children: [_jsx(Icon, { name: "alert-circle", className: "h-6 w-6" }), _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-bold", children: t('errors.moduleNotFound') }), _jsx("p", { className: "text-muted-foreground mt-1", children: t('errors.moduleNotFoundDescription', { moduleId }) })] })] }) }) }) }));
|
|
31
|
+
}
|
|
32
|
+
const statusVariant = module.status === "active"
|
|
33
|
+
? "success"
|
|
34
|
+
: module.status === "deprecated"
|
|
35
|
+
? "warning"
|
|
36
|
+
: "secondary";
|
|
37
|
+
const moduleTitle = translateTitle(module.title, module.titleKey);
|
|
38
|
+
const moduleDescription = translateTitle(module.description, module.descriptionKey);
|
|
39
|
+
return (_jsxs("div", { className: "p-6 max-w-3xl mx-auto space-y-6", children: [_jsxs("div", { className: "flex items-start gap-4", children: [_jsx("div", { className: "flex-shrink-0 p-3 rounded-lg bg-muted", children: _jsx(Icon, { name: module.icon || "package", className: "h-8 w-8" }) }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-3 mb-1", children: [_jsx("h1", { className: "text-3xl font-bold", children: moduleTitle }), _jsx(Badge, { variant: statusVariant, className: "capitalize", children: module.status })] }), moduleDescription && (_jsx("p", { className: "text-muted-foreground", children: moduleDescription }))] })] }), module.commands && module.commands.length > 0 && (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "zap", className: "h-5 w-5 text-muted-foreground" }), _jsx(CardTitle, { className: "text-lg", children: t('moduleOverview.actions') })] }), _jsx(CardDescription, { children: t('moduleOverview.actionsDescription') })] }), _jsx(CardContent, { children: _jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3", children: module.commands.map((command) => (_jsxs("button", { onClick: () => navigate(command.route), className: "flex items-center gap-3 rounded-lg border border-border bg-card p-4 text-left transition-colors hover:bg-accent hover:border-accent", children: [_jsx("div", { className: "flex-shrink-0 p-2 rounded-md bg-muted", children: _jsx(Icon, { name: command.icon || "file-text", className: "h-5 w-5" }) }), _jsx("span", { className: "font-medium", children: translateTitle(command.title, command.titleKey) })] }, command.id))) }) })] })), _jsxs(Card, { children: [_jsx(CardHeader, { className: "pb-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "info", className: "h-5 w-5 text-muted-foreground" }), _jsx(CardTitle, { className: "text-lg", children: t('moduleOverview.information') })] }) }), _jsx(CardContent, { children: _jsxs("dl", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between py-2 border-b border-border", children: [_jsxs("dt", { className: "text-sm font-medium text-muted-foreground flex items-center gap-2", children: [_jsx(Icon, { name: "folder", className: "h-4 w-4" }), t('moduleOverview.category')] }), _jsx("dd", { className: "text-sm font-medium", children: module.category })] }), _jsxs("div", { className: "flex items-center justify-between py-2 border-b border-border", children: [_jsxs("dt", { className: "text-sm font-medium text-muted-foreground flex items-center gap-2", children: [_jsx(Icon, { name: "activity", className: "h-4 w-4" }), t('moduleOverview.status')] }), _jsx("dd", { children: _jsx(Badge, { variant: statusVariant, className: "capitalize", children: module.status }) })] }), module.owners?.team && (_jsxs("div", { className: "flex items-center justify-between py-2 border-b border-border", children: [_jsxs("dt", { className: "text-sm font-medium text-muted-foreground flex items-center gap-2", children: [_jsx(Icon, { name: "users", className: "h-4 w-4" }), t('moduleOverview.owner')] }), _jsx("dd", { className: "text-sm font-medium", children: module.owners.team })] })), module.owners?.supportChannel && (_jsxs("div", { className: "flex items-center justify-between py-2", children: [_jsxs("dt", { className: "text-sm font-medium text-muted-foreground flex items-center gap-2", children: [_jsx(Icon, { name: "message-circle", className: "h-4 w-4" }), t('moduleOverview.support')] }), _jsx("dd", { className: "text-sm font-medium", children: module.owners.supportChannel })] }))] }) })] })] }));
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProfilePage.d.ts","sourceRoot":"","sources":["../../../src/shell/components/ProfilePage.tsx"],"names":[],"mappings":"AA0BA,wBAAgB,WAAW,4CAgK1B"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { useNavigate } from "react-router-dom";
|
|
4
|
+
import { useAuth } from "../../hooks/useAuth";
|
|
5
|
+
import { useI18n } from "../../hooks/useI18n";
|
|
6
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Badge, Icon, Avatar, AvatarFallback, } from "@nsxbet/admin-ui";
|
|
7
|
+
export function ProfilePage() {
|
|
8
|
+
const { getUser } = useAuth();
|
|
9
|
+
const { t } = useI18n();
|
|
10
|
+
const user = getUser();
|
|
11
|
+
const navigate = useNavigate();
|
|
12
|
+
const [pinnedItems, setPinnedItems] = useState([]);
|
|
13
|
+
// Load pinned items from localStorage
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
try {
|
|
16
|
+
const stored = localStorage.getItem("adminPlatform.pinnedCommands");
|
|
17
|
+
if (stored) {
|
|
18
|
+
const parsed = JSON.parse(stored);
|
|
19
|
+
setPinnedItems(Array.isArray(parsed) ? parsed : []);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Ignore localStorage errors
|
|
24
|
+
}
|
|
25
|
+
}, []);
|
|
26
|
+
const handlePinnedClick = (route) => {
|
|
27
|
+
navigate(route);
|
|
28
|
+
};
|
|
29
|
+
return (_jsxs("div", { className: "p-6 max-w-3xl mx-auto space-y-6", "data-testid": "profile-page", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-3xl font-bold mb-1", "data-testid": "profile-heading", children: t('profilePage.title') }), _jsx("p", { className: "text-muted-foreground", children: t('profilePage.userInfo') })] }), _jsxs(Card, { children: [_jsx(CardHeader, { className: "pb-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "user", className: "h-5 w-5 text-muted-foreground" }), _jsx(CardTitle, { className: "text-lg", children: t('profilePage.userInfo') })] }) }), _jsxs(CardContent, { children: [_jsxs("div", { className: "flex items-center gap-4 mb-6", children: [_jsx(Avatar, { className: "h-16 w-16", children: _jsx(AvatarFallback, { className: "text-2xl font-bold bg-primary text-primary-foreground", children: user?.displayName?.charAt(0).toUpperCase() || "U" }) }), _jsxs("div", { children: [_jsx("h3", { className: "text-xl font-semibold", children: user?.displayName || "User" }), _jsx("p", { className: "text-muted-foreground", children: user?.email || "No email" })] })] }), _jsxs("dl", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between py-2 border-b border-border", children: [_jsx("dt", { className: "text-sm font-medium text-muted-foreground", children: t('profilePage.name') }), _jsx("dd", { "data-testid": "profile-display-name", className: `text-sm font-medium ${!user?.displayName ? "text-muted-foreground" : ""}`, children: user?.displayName || "N/A" })] }), _jsxs("div", { className: "flex items-center justify-between py-2 border-b border-border", children: [_jsx("dt", { className: "text-sm font-medium text-muted-foreground", children: t('profilePage.email') }), _jsx("dd", { "data-testid": "profile-email", className: `text-sm font-medium ${!user?.email ? "text-muted-foreground" : ""}`, children: user?.email || "N/A" })] }), _jsxs("div", { className: "flex items-center justify-between py-2", children: [_jsx("dt", { className: "text-sm font-medium text-muted-foreground", children: t('profilePage.roles') }), _jsx("dd", { "data-testid": "profile-roles", children: _jsx(Badge, { variant: "secondary", children: "Admin" }) })] })] })] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "pin", className: "h-5 w-5 text-muted-foreground" }), _jsx(CardTitle, { className: "text-lg", children: t('profilePage.pinnedPages') })] }), _jsx(CardDescription, { children: t('profilePage.pinnedPages') })] }), _jsx(CardContent, { children: pinnedItems.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground py-2", children: t('profilePage.noPinnedPages') })) : (_jsx("ul", { "data-testid": "pinned-pages-list", className: "space-y-1", children: pinnedItems.map((item) => (_jsx("li", { children: _jsxs("button", { "data-testid": `pinned-page-${item.commandId}`, onClick: () => handlePinnedClick(item.route), className: "w-full flex items-center gap-3 text-left px-3 py-2.5 rounded-md hover:bg-accent transition-colors", children: [_jsx(Icon, { name: item.commandIcon || item.moduleIcon || "file-text", className: "h-4 w-4 text-muted-foreground" }), _jsxs("span", { className: "text-sm", children: [item.moduleTitle, " ", _jsx("span", { className: "text-muted-foreground", children: "/" }), " ", item.commandTitle] })] }) }, `${item.moduleId}-${item.commandId}`))) })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { className: "pb-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "shield", className: "h-5 w-5 text-muted-foreground" }), _jsx(CardTitle, { className: "text-lg", children: t('profilePage.auditInfo') })] }) }), _jsxs(CardContent, { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between py-2", children: [_jsx("span", { className: "text-sm font-medium text-muted-foreground", children: t('profilePage.userId') }), _jsx("span", { "data-testid": "profile-user-id", className: `text-sm font-mono px-2 py-1 rounded ${user?.id ? "bg-muted" : "text-muted-foreground"}`, children: user?.id || "N/A" })] }), _jsx("p", { className: "text-xs text-muted-foreground", children: t('profilePage.auditInfo') })] })] })] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RegistryClient } from "../../registry";
|
|
2
|
+
interface RegistryPageProps {
|
|
3
|
+
apiUrl?: string;
|
|
4
|
+
registryClient?: RegistryClient;
|
|
5
|
+
}
|
|
6
|
+
export declare function RegistryPage({ apiUrl, registryClient }: RegistryPageProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=RegistryPage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RegistryPage.d.ts","sourceRoot":"","sources":["../../../src/shell/components/RegistryPage.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAoB,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEvE,UAAU,iBAAiB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,iBAAiB,2CAiZzE"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useCallback } from "react";
|
|
3
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Switch, Label, Button, Icon, Badge, Input, Textarea, Tabs, TabsList, TabsTrigger, TabsContent, } from "@nsxbet/admin-ui";
|
|
4
|
+
export function RegistryPage({ apiUrl, registryClient }) {
|
|
5
|
+
const [modules, setModules] = useState([]);
|
|
6
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
7
|
+
const [error, setError] = useState(null);
|
|
8
|
+
const [newModuleUrl, setNewModuleUrl] = useState("");
|
|
9
|
+
const [manifestJson, setManifestJson] = useState("");
|
|
10
|
+
const [isAdding, setIsAdding] = useState(false);
|
|
11
|
+
// Fetch modules using registry client
|
|
12
|
+
const fetchModules = useCallback(async () => {
|
|
13
|
+
if (!registryClient) {
|
|
14
|
+
setError("Registry client not available.");
|
|
15
|
+
setIsLoading(false);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
setIsLoading(true);
|
|
20
|
+
setError(null);
|
|
21
|
+
const data = await registryClient.modules.findAll();
|
|
22
|
+
setModules(data);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
setError(err instanceof Error ? err.message : "Failed to load modules");
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
setIsLoading(false);
|
|
29
|
+
}
|
|
30
|
+
}, [registryClient]);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
fetchModules();
|
|
33
|
+
}, [fetchModules]);
|
|
34
|
+
// Add module by URL
|
|
35
|
+
const handleAddModuleByUrl = async () => {
|
|
36
|
+
if (!newModuleUrl.trim() || !registryClient) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
setIsAdding(true);
|
|
41
|
+
setError(null);
|
|
42
|
+
await registryClient.modules.register(newModuleUrl.trim());
|
|
43
|
+
setNewModuleUrl("");
|
|
44
|
+
await fetchModules();
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
setError(err instanceof Error ? err.message : "Failed to add module");
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
setIsAdding(false);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
// Add module by manifest JSON
|
|
54
|
+
const handleAddModuleByManifest = async () => {
|
|
55
|
+
if (!manifestJson.trim() || !registryClient)
|
|
56
|
+
return;
|
|
57
|
+
try {
|
|
58
|
+
setIsAdding(true);
|
|
59
|
+
setError(null);
|
|
60
|
+
// Parse the manifest JSON
|
|
61
|
+
let manifest;
|
|
62
|
+
try {
|
|
63
|
+
manifest = JSON.parse(manifestJson.trim());
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
throw new Error("Invalid JSON format. Please check your manifest.");
|
|
67
|
+
}
|
|
68
|
+
await registryClient.modules.registerFromManifest(manifest, "");
|
|
69
|
+
setManifestJson("");
|
|
70
|
+
await fetchModules();
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
setError(err instanceof Error ? err.message : "Failed to add module");
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
setIsAdding(false);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
// Toggle module enabled state
|
|
80
|
+
const handleToggleEnabled = async (module) => {
|
|
81
|
+
if (!registryClient)
|
|
82
|
+
return;
|
|
83
|
+
try {
|
|
84
|
+
setError(null);
|
|
85
|
+
await registryClient.modules.update(module.id, {
|
|
86
|
+
enabled: !module.enabled,
|
|
87
|
+
});
|
|
88
|
+
await fetchModules();
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
setError(err instanceof Error ? err.message : "Failed to update module");
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
// Delete module
|
|
95
|
+
const handleDeleteModule = async (module) => {
|
|
96
|
+
if (!registryClient)
|
|
97
|
+
return;
|
|
98
|
+
const confirmed = window.confirm(`Are you sure you want to delete "${module.manifest.title}"? This action cannot be undone.`);
|
|
99
|
+
if (!confirmed)
|
|
100
|
+
return;
|
|
101
|
+
try {
|
|
102
|
+
setError(null);
|
|
103
|
+
await registryClient.modules.delete(module.id);
|
|
104
|
+
await fetchModules();
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
setError(err instanceof Error ? err.message : "Failed to delete module");
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
if (!registryClient) {
|
|
111
|
+
return (_jsxs("div", { className: "p-6 max-w-4xl mx-auto space-y-6", "data-testid": "registry-page", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-3xl font-bold mb-1", "data-testid": "registry-heading", children: "Module Registry" }), _jsx("p", { className: "text-muted-foreground", children: "Manage registered modules" })] }), _jsx(Card, { className: "border-destructive", children: _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "flex items-center gap-3 text-destructive", children: [_jsx(Icon, { name: "alert-circle", className: "h-5 w-5" }), _jsx("p", { children: "Registry client not available. Please check configuration." })] }) }) })] }));
|
|
112
|
+
}
|
|
113
|
+
return (_jsxs("div", { className: "p-6 max-w-4xl mx-auto space-y-6", "data-testid": "registry-page", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-3xl font-bold mb-1", "data-testid": "registry-heading", children: "Module Registry" }), _jsx("p", { className: "text-muted-foreground", children: "Manage registered modules" })] }), _jsx(Badge, { variant: apiUrl ? "default" : "secondary", children: apiUrl ? "API Mode" : "In-Memory Mode" })] }), error && (_jsx(Card, { className: "border-destructive bg-destructive/10", children: _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "flex items-center gap-3 text-destructive", children: [_jsx(Icon, { name: "alert-circle", className: "h-5 w-5" }), _jsx("p", { children: error })] }) }) })), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "plus", className: "h-5 w-5 text-muted-foreground" }), _jsx(CardTitle, { className: "text-lg", children: "Add Module" })] }), _jsx(CardDescription, { children: "Register a new module by URL or manifest" })] }), _jsx(CardContent, { children: _jsxs(Tabs, { defaultValue: "url", className: "w-full", children: [_jsxs(TabsList, { className: "grid w-full grid-cols-2 mb-4", children: [_jsxs(TabsTrigger, { value: "url", children: [_jsx(Icon, { name: "link", className: "h-4 w-4 mr-2" }), "From URL"] }), _jsxs(TabsTrigger, { value: "manifest", children: [_jsx(Icon, { name: "file-text", className: "h-4 w-4 mr-2" }), "From Manifest"] })] }), _jsx(TabsContent, { value: "url", children: _jsxs("form", { onSubmit: (e) => {
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
handleAddModuleByUrl();
|
|
116
|
+
}, className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "module-url", children: "Module Base URL" }), _jsx(Input, { id: "module-url", type: "text", placeholder: "http://localhost:3003/dist", value: newModuleUrl, onChange: (e) => setNewModuleUrl(e.target.value), disabled: isAdding }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Enter the base URL where the module is served. The system will fetch admin.module.json from this URL." })] }), _jsx("div", { className: "flex justify-end", children: _jsx(Button, { type: "submit", disabled: !newModuleUrl.trim() || isAdding, children: isAdding ? (_jsxs(_Fragment, { children: [_jsx(Icon, { name: "loader-2", className: "h-4 w-4 mr-2 animate-spin" }), "Adding..."] })) : (_jsxs(_Fragment, { children: [_jsx(Icon, { name: "plus", className: "h-4 w-4 mr-2" }), "Add Module"] })) }) })] }) }), _jsx(TabsContent, { value: "manifest", children: _jsxs("form", { onSubmit: (e) => {
|
|
117
|
+
e.preventDefault();
|
|
118
|
+
handleAddModuleByManifest();
|
|
119
|
+
}, className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "module-manifest", children: "Module Manifest (JSON)" }), _jsx(Textarea, { id: "module-manifest", placeholder: `{
|
|
120
|
+
"id": "@admin/my-module",
|
|
121
|
+
"title": "My Module",
|
|
122
|
+
"description": "Module description",
|
|
123
|
+
"category": "Tools",
|
|
124
|
+
"icon": "package",
|
|
125
|
+
"routeBase": "/admin/my-module",
|
|
126
|
+
"keywords": ["keyword1", "keyword2"],
|
|
127
|
+
"status": "active"
|
|
128
|
+
}`, value: manifestJson, onChange: (e) => setManifestJson(e.target.value), disabled: isAdding, rows: 10, className: "font-mono text-sm" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Paste the module manifest JSON. Required fields: id, title, routeBase." })] }), _jsx("div", { className: "flex justify-end", children: _jsx(Button, { type: "submit", disabled: !manifestJson.trim() || isAdding, children: isAdding ? (_jsxs(_Fragment, { children: [_jsx(Icon, { name: "loader-2", className: "h-4 w-4 mr-2 animate-spin" }), "Adding..."] })) : (_jsxs(_Fragment, { children: [_jsx(Icon, { name: "plus", className: "h-4 w-4 mr-2" }), "Add Module"] })) }) })] }) })] }) })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "package", className: "h-5 w-5 text-muted-foreground" }), _jsxs("h2", { className: "text-lg font-semibold", children: ["Registered Modules", " ", !isLoading && (_jsxs("span", { className: "text-muted-foreground font-normal", children: ["(", modules.length, ")"] }))] })] }), isLoading ? (_jsx(Card, { children: _jsxs(CardContent, { className: "py-8 text-center", children: [_jsx(Icon, { name: "loader-2", className: "h-8 w-8 mx-auto mb-3 animate-spin text-muted-foreground" }), _jsx("p", { className: "text-muted-foreground", children: "Loading modules..." })] }) })) : modules.length === 0 ? (_jsx(Card, { children: _jsxs(CardContent, { className: "py-8 text-center", children: [_jsx(Icon, { name: "package", className: "h-8 w-8 mx-auto mb-3 text-muted-foreground" }), _jsx("p", { className: "text-muted-foreground", children: "No modules registered yet." }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Add a module using the form above." })] }) })) : (_jsx("div", { className: "space-y-3", children: modules.map((module) => (_jsx(Card, { className: !module.enabled ? "opacity-60 border-dashed" : undefined, children: _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex items-start gap-4 flex-1 min-w-0", children: [_jsx("div", { className: "flex-shrink-0 p-2 rounded-lg bg-muted", children: _jsx(Icon, { name: module.manifest.icon || "package", className: "h-6 w-6" }) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2 mb-1", children: [_jsx("h3", { className: "font-semibold truncate", children: module.manifest.title }), _jsx(Badge, { variant: module.enabled ? "success" : "secondary", children: module.enabled ? "Enabled" : "Disabled" })] }), _jsx("p", { className: "text-sm text-muted-foreground mb-3", children: module.manifest.description || "No description" }), _jsxs("div", { className: "flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground", children: [_jsxs("span", { className: "flex items-center gap-1", children: [_jsx(Icon, { name: "hash", className: "h-3 w-3" }), module.moduleId] }), _jsxs("span", { className: "flex items-center gap-1", children: [_jsx(Icon, { name: "folder", className: "h-3 w-3" }), module.manifest.category || "General"] }), _jsxs("span", { className: "flex items-center gap-1", children: [_jsx(Icon, { name: "navigation", className: "h-3 w-3" }), module.manifest.routeBase] }), module.baseUrl && (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx(Icon, { name: "link", className: "h-3 w-3" }), module.baseUrl] }))] })] })] }), _jsxs("div", { className: "flex items-center gap-3 flex-shrink-0", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Label, { className: "text-sm text-muted-foreground", children: "Enabled" }), _jsx(Switch, { checked: module.enabled, onCheckedChange: () => handleToggleEnabled(module) })] }), _jsx(Button, { variant: "destructive", size: "sm", onClick: () => handleDeleteModule(module), children: _jsx(Icon, { name: "trash-2", className: "h-4 w-4" }) })] })] }) }) }, module.id))) }))] })] }));
|
|
129
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SettingsPage.d.ts","sourceRoot":"","sources":["../../../src/shell/components/SettingsPage.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,YAAY,4CAsO3B"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { useTheme } from "./theme-provider";
|
|
4
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Switch, Label, Button, Icon, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@nsxbet/admin-ui";
|
|
5
|
+
import { useI18n } from "../../hooks/useI18n";
|
|
6
|
+
export function SettingsPage() {
|
|
7
|
+
const { theme, setTheme } = useTheme();
|
|
8
|
+
const { t, locale } = useI18n();
|
|
9
|
+
const [showPinned, setShowPinned] = useState(true);
|
|
10
|
+
// Load preferences from localStorage
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
try {
|
|
13
|
+
const pinnedPref = localStorage.getItem("adminPlatform.showPinned");
|
|
14
|
+
if (pinnedPref !== null) {
|
|
15
|
+
setShowPinned(JSON.parse(pinnedPref));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// Ignore localStorage errors
|
|
20
|
+
}
|
|
21
|
+
}, []);
|
|
22
|
+
const handleShowPinnedChange = (checked) => {
|
|
23
|
+
setShowPinned(checked);
|
|
24
|
+
try {
|
|
25
|
+
localStorage.setItem("adminPlatform.showPinned", JSON.stringify(checked));
|
|
26
|
+
// Dispatch storage event for other components to react
|
|
27
|
+
window.dispatchEvent(new StorageEvent("storage", {
|
|
28
|
+
key: "adminPlatform.showPinned",
|
|
29
|
+
newValue: JSON.stringify(checked),
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Ignore localStorage errors
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const handleClearPinned = () => {
|
|
37
|
+
try {
|
|
38
|
+
localStorage.removeItem("adminPlatform.pinnedCommands");
|
|
39
|
+
// Dispatch storage event for other components to react
|
|
40
|
+
window.dispatchEvent(new StorageEvent("storage", {
|
|
41
|
+
key: "adminPlatform.pinnedCommands",
|
|
42
|
+
newValue: null,
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Ignore localStorage errors
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const getLanguageName = (loc) => {
|
|
50
|
+
switch (loc) {
|
|
51
|
+
case 'pt-BR': return 'Português (Brasil)';
|
|
52
|
+
case 'en-US': return 'English (US)';
|
|
53
|
+
case 'es': return 'Español';
|
|
54
|
+
case 'ro': return 'Română';
|
|
55
|
+
default: return loc;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
return (_jsxs("div", { className: "p-6 max-w-3xl mx-auto space-y-6", "data-testid": "settings-page", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-3xl font-bold mb-1", "data-testid": "settings-heading", children: t('settingsPage.title') }), _jsx("p", { className: "text-muted-foreground", children: t('settingsPage.appearanceDescription') })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "palette", className: "h-5 w-5 text-muted-foreground" }), _jsx(CardTitle, { className: "text-lg", children: t('settingsPage.appearance') })] }), _jsx(CardDescription, { children: t('settingsPage.appearanceDescription') })] }), _jsx(CardContent, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "space-y-0.5", children: [_jsx(Label, { className: "text-sm font-medium", children: t('settingsPage.theme') }), _jsx("p", { className: "text-xs text-muted-foreground", children: t('settingsPage.appearanceDescription') })] }), _jsxs(Select, { "data-testid": "theme-select", value: theme, onValueChange: (value) => setTheme(value), children: [_jsx(SelectTrigger, { className: "w-[140px]", children: _jsx(SelectValue, { placeholder: t('settingsPage.theme') }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "light", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "sun", className: "h-4 w-4" }), t('topBar.themeLight')] }) }), _jsx(SelectItem, { value: "dark", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "moon", className: "h-4 w-4" }), t('topBar.themeDark')] }) }), _jsx(SelectItem, { value: "system", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "settings", className: "h-4 w-4" }), t('topBar.themeSystem')] }) })] })] })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "panel-left", className: "h-5 w-5 text-muted-foreground" }), _jsx(CardTitle, { className: "text-lg", children: t('settingsPage.navigation') })] }), _jsx(CardDescription, { children: t('settingsPage.navigationDescription') })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "space-y-0.5", children: [_jsx(Label, { className: "text-sm font-medium", children: t('settingsPage.showPinnedItems') }), _jsx("p", { className: "text-xs text-muted-foreground", children: t('settingsPage.showPinnedItemsDescription') })] }), _jsx(Switch, { "data-testid": "show-pinned-toggle", checked: showPinned, onCheckedChange: handleShowPinnedChange })] }), _jsx("p", { className: "text-xs text-muted-foreground italic pt-2 border-t border-border", children: t('settingsPage.pinnedItemsNote') })] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "database", className: "h-5 w-5 text-muted-foreground" }), _jsx(CardTitle, { className: "text-lg", children: t('settingsPage.dataManagement') })] }), _jsx(CardDescription, { children: t('settingsPage.dataManagementDescription') })] }), _jsx(CardContent, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "space-y-0.5", children: [_jsx(Label, { className: "text-sm font-medium", children: t('settingsPage.clearPinnedItems') }), _jsx("p", { className: "text-xs text-muted-foreground", children: t('settingsPage.clearPinnedItemsConfirm') })] }), _jsxs(Button, { variant: "destructive", size: "sm", onClick: handleClearPinned, children: [_jsx(Icon, { name: "trash-2", className: "h-4 w-4 mr-2" }), t('common.delete')] })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "globe", className: "h-5 w-5 text-muted-foreground" }), _jsx(CardTitle, { className: "text-lg", children: t('topBar.language') })] }), _jsx(CardDescription, { children: t('topBar.language') })] }), _jsx(CardContent, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "space-y-0.5", children: [_jsx(Label, { className: "text-sm font-medium", children: t('topBar.language') }), _jsx("p", { className: "text-xs text-muted-foreground", children: t('topBar.language') })] }), _jsx("span", { className: "text-sm text-muted-foreground", children: getLanguageName(locale) })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "info", className: "h-5 w-5 text-muted-foreground" }), _jsx(CardTitle, { className: "text-lg", children: t('settingsPage.about') })] }), _jsx(CardDescription, { children: t('settingsPage.about') })] }), _jsxs(CardContent, { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between py-2 border-b border-border", children: [_jsx("span", { className: "text-sm font-medium text-muted-foreground", children: t('settingsPage.version') }), _jsx("span", { className: "text-sm font-mono bg-muted px-2 py-1 rounded", children: "0.1.0" })] }), _jsxs("div", { className: "flex items-center justify-between py-2", children: [_jsx("span", { className: "text-sm font-medium text-muted-foreground", children: t('settingsPage.environment') }), _jsx("span", { className: "text-sm font-mono bg-muted px-2 py-1 rounded", children: window.__ENV__
|
|
59
|
+
?.ENVIRONMENT || "local" })] })] })] })] }));
|
|
60
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface TopBarProps {
|
|
2
|
+
onSearchClick?: () => void;
|
|
3
|
+
environment?: string;
|
|
4
|
+
locale?: string;
|
|
5
|
+
onLocaleChange?: (locale: string) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function TopBar({ onSearchClick, environment, locale: externalLocale, onLocaleChange, }: TopBarProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=TopBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TopBar.d.ts","sourceRoot":"","sources":["../../../src/shell/components/TopBar.tsx"],"names":[],"mappings":"AAuCA,MAAM,WAAW,WAAW;IAC1B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAED,wBAAgB,MAAM,CAAC,EACrB,aAAa,EACb,WAAqB,EACrB,MAAM,EAAE,cAAc,EACtB,cAAc,GACf,EAAE,WAAW,2CAuGb"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { Search, Moon, Sun, Check } from "lucide-react";
|
|
4
|
+
import { useTheme } from "./theme-provider";
|
|
5
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Badge, Button, } from "@nsxbet/admin-ui";
|
|
6
|
+
import { SUPPORTED_LOCALES, LOCALE_FLAGS, LOCALE_NAMES } from "../../i18n";
|
|
7
|
+
import { useI18n } from "../../hooks/useI18n";
|
|
8
|
+
const ENV_COLORS = {
|
|
9
|
+
local: "bg-gray-500",
|
|
10
|
+
dev: "bg-blue-500",
|
|
11
|
+
e2e: "bg-purple-500",
|
|
12
|
+
staging: "bg-yellow-500",
|
|
13
|
+
production: "bg-red-500",
|
|
14
|
+
};
|
|
15
|
+
// Environment-specific header background colors
|
|
16
|
+
const ENV_HEADER_COLORS_LIGHT = {
|
|
17
|
+
local: "bg-blue-50 border-blue-200",
|
|
18
|
+
dev: "bg-green-50 border-green-200",
|
|
19
|
+
e2e: "bg-purple-50 border-purple-200",
|
|
20
|
+
staging: "bg-yellow-50 border-yellow-200",
|
|
21
|
+
production: "bg-red-100 border-red-300",
|
|
22
|
+
};
|
|
23
|
+
const ENV_HEADER_COLORS_DARK = {
|
|
24
|
+
local: "bg-blue-950 border-blue-800",
|
|
25
|
+
dev: "bg-green-950 border-green-800",
|
|
26
|
+
e2e: "bg-purple-950 border-purple-800",
|
|
27
|
+
staging: "bg-yellow-950 border-yellow-800",
|
|
28
|
+
production: "bg-red-950 border-red-800",
|
|
29
|
+
};
|
|
30
|
+
export function TopBar({ onSearchClick, environment = "local", locale: externalLocale, onLocaleChange, }) {
|
|
31
|
+
const { theme, setTheme } = useTheme();
|
|
32
|
+
const { t } = useI18n();
|
|
33
|
+
const [locale, setLocale] = useState(externalLocale || "pt-BR");
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (externalLocale && SUPPORTED_LOCALES.includes(externalLocale)) {
|
|
36
|
+
setLocale(externalLocale);
|
|
37
|
+
}
|
|
38
|
+
}, [externalLocale]);
|
|
39
|
+
const handleLocaleChange = (newLocale) => {
|
|
40
|
+
setLocale(newLocale);
|
|
41
|
+
onLocaleChange?.(newLocale);
|
|
42
|
+
};
|
|
43
|
+
const toggleTheme = () => {
|
|
44
|
+
const root = window.document.documentElement;
|
|
45
|
+
const isDark = root.classList.contains("dark");
|
|
46
|
+
setTheme(isDark ? "light" : "dark");
|
|
47
|
+
};
|
|
48
|
+
// Determine effective theme (resolve "system" to actual theme)
|
|
49
|
+
const effectiveTheme = theme === "system"
|
|
50
|
+
? window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
51
|
+
? "dark"
|
|
52
|
+
: "light"
|
|
53
|
+
: theme;
|
|
54
|
+
// Select color classes based on effective theme
|
|
55
|
+
const headerColorClasses = effectiveTheme === "dark"
|
|
56
|
+
? ENV_HEADER_COLORS_DARK[environment] || "bg-background border-b"
|
|
57
|
+
: ENV_HEADER_COLORS_LIGHT[environment] || "bg-background border-b";
|
|
58
|
+
// Get flag for current locale
|
|
59
|
+
const currentFlag = LOCALE_FLAGS[locale] || "🌐";
|
|
60
|
+
return (_jsxs("header", { className: `flex h-14 items-center justify-between px-4 ${headerColorClasses}`, role: "banner", "data-environment": environment, children: [_jsx(Badge, { className: `${ENV_COLORS[environment]} uppercase text-white`, children: environment }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsxs(Button, { variant: "outline", onClick: onSearchClick, "data-testid": "search-button", className: "relative h-8 w-40 justify-start rounded-md bg-muted/50 px-3 text-sm font-normal text-muted-foreground shadow-none sm:w-48", children: [_jsx(Search, { className: "mr-2 h-4 w-4" }), _jsx("span", { children: t('topBar.search') }), _jsxs("kbd", { className: "pointer-events-none absolute right-1.5 top-1 hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex", children: [_jsx("span", { className: "text-xs", children: "\u2318" }), "K"] })] }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { className: "flex items-center gap-1 text-xl transition-opacity hover:opacity-80 focus:outline-none", "data-testid": "language-selector", title: t('topBar.language'), children: _jsx("span", { children: currentFlag }) }), _jsx(DropdownMenuContent, { align: "end", className: "min-w-[180px]", children: SUPPORTED_LOCALES.map((localeOption) => (_jsxs(DropdownMenuItem, { onClick: () => handleLocaleChange(localeOption), className: "flex items-center justify-between", "data-testid": `language-option-${localeOption}`, children: [_jsxs("div", { className: "flex items-center", children: [_jsx("span", { className: "mr-2 text-lg", children: LOCALE_FLAGS[localeOption] }), _jsx("span", { children: LOCALE_NAMES[localeOption] })] }), locale === localeOption && (_jsx(Check, { className: "h-4 w-4 text-primary" }))] }, localeOption))) })] }), _jsxs(Button, { variant: "ghost", size: "icon", onClick: toggleTheme, title: t('topBar.theme'), children: [_jsx(Sun, { className: "h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" }), _jsx(Moon, { className: "absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" }), _jsx("span", { className: "sr-only", children: t('topBar.theme') })] })] })] }));
|
|
61
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { TopBar } from "./TopBar";
|
|
2
|
+
export type { TopBarProps } from "./TopBar";
|
|
3
|
+
export { LeftNav } from "./LeftNav";
|
|
4
|
+
export { MainContent } from "./MainContent";
|
|
5
|
+
export { CommandPalette } from "./CommandPalette";
|
|
6
|
+
export type { CommandPaletteProps } from "./CommandPalette";
|
|
7
|
+
export { ThemeProvider, useTheme } from "./theme-provider";
|
|
8
|
+
export { RegistryPage } from "./RegistryPage";
|
|
9
|
+
export { HomePage } from "./HomePage";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shell/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE5D,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { TopBar } from "./TopBar";
|
|
2
|
+
export { LeftNav } from "./LeftNav";
|
|
3
|
+
export { MainContent } from "./MainContent";
|
|
4
|
+
export { CommandPalette } from "./CommandPalette";
|
|
5
|
+
export { ThemeProvider, useTheme } from "./theme-provider";
|
|
6
|
+
export { RegistryPage } from "./RegistryPage";
|
|
7
|
+
export { HomePage } from "./HomePage";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type Theme = "dark" | "light" | "system";
|
|
2
|
+
type ThemeProviderProps = {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
defaultTheme?: Theme;
|
|
5
|
+
storageKey?: string;
|
|
6
|
+
};
|
|
7
|
+
type ThemeProviderState = {
|
|
8
|
+
theme: Theme;
|
|
9
|
+
setTheme: (theme: Theme) => void;
|
|
10
|
+
};
|
|
11
|
+
export declare function ThemeProvider({ children, defaultTheme, // Brasa Design System is dark-first
|
|
12
|
+
storageKey, ...props }: ThemeProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare const useTheme: () => ThemeProviderState;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=theme-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../../src/shell/components/theme-provider.tsx"],"names":[],"mappings":"AAEA,KAAK,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEzC,KAAK,kBAAkB,GAAG;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,YAAY,CAAC,EAAE,KAAK,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACxB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,CAAC;AASF,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,YAAqB,EAAE,oCAAoC;AAC3D,UAA6B,EAC7B,GAAG,KAAK,EACT,EAAE,kBAAkB,2CAoCpB;AAGD,eAAO,MAAM,QAAQ,0BAOpB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useEffect, useState } from "react";
|
|
3
|
+
const initialState = {
|
|
4
|
+
theme: "system",
|
|
5
|
+
setTheme: () => null,
|
|
6
|
+
};
|
|
7
|
+
const ThemeProviderContext = createContext(initialState);
|
|
8
|
+
export function ThemeProvider({ children, defaultTheme = "dark", // Brasa Design System is dark-first
|
|
9
|
+
storageKey = "admin-ui-theme", ...props }) {
|
|
10
|
+
const [theme, setTheme] = useState(() => localStorage.getItem(storageKey) || defaultTheme);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const root = window.document.documentElement;
|
|
13
|
+
root.classList.remove("light", "dark");
|
|
14
|
+
if (theme === "system") {
|
|
15
|
+
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
|
|
16
|
+
.matches
|
|
17
|
+
? "dark"
|
|
18
|
+
: "light";
|
|
19
|
+
root.classList.add(systemTheme);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
root.classList.add(theme);
|
|
23
|
+
}, [theme]);
|
|
24
|
+
const value = {
|
|
25
|
+
theme,
|
|
26
|
+
setTheme: (theme) => {
|
|
27
|
+
localStorage.setItem(storageKey, theme);
|
|
28
|
+
setTheme(theme);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
return (_jsx(ThemeProviderContext.Provider, { ...props, value: value, children: children }));
|
|
32
|
+
}
|
|
33
|
+
// eslint-disable-next-line react-refresh/only-export-components
|
|
34
|
+
export const useTheme = () => {
|
|
35
|
+
const context = useContext(ThemeProviderContext);
|
|
36
|
+
if (context === undefined)
|
|
37
|
+
throw new Error("useTheme must be used within a ThemeProvider");
|
|
38
|
+
return context;
|
|
39
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { AdminShell } from "./AdminShell";
|
|
2
|
+
export type { AdminShellProps } from "./AdminShell";
|
|
3
|
+
export { TopBar, LeftNav, MainContent, CommandPalette, ThemeProvider, useTheme, } from "./components";
|
|
4
|
+
export type { TopBarProps, CommandPaletteProps } from "./components";
|
|
5
|
+
export type { Module, ModuleCommand, ModuleStatus, Catalog, ImportMap, SearchableItem, SearchResult, } from "./types";
|
|
6
|
+
export { initTelemetry, track, trackError } from "./telemetry";
|
|
7
|
+
export { fuzzySearch } from "./search/fuzzy";
|
|
8
|
+
export type { SearchOptions } from "./search/fuzzy";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shell/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGpD,OAAO,EACL,MAAM,EACN,OAAO,EACP,WAAW,EACX,cAAc,EACd,aAAa,EACb,QAAQ,GACT,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGrE,YAAY,EACV,MAAM,EACN,aAAa,EACb,YAAY,EACZ,OAAO,EACP,SAAS,EACT,cAAc,EACd,YAAY,GACb,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG/D,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Main shell component
|
|
2
|
+
export { AdminShell } from "./AdminShell";
|
|
3
|
+
// Shell sub-components (for customization)
|
|
4
|
+
export { TopBar, LeftNav, MainContent, CommandPalette, ThemeProvider, useTheme, } from "./components";
|
|
5
|
+
// Telemetry
|
|
6
|
+
export { initTelemetry, track, trackError } from "./telemetry";
|
|
7
|
+
// Search
|
|
8
|
+
export { fuzzySearch } from "./search/fuzzy";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy search implementation for command palette
|
|
3
|
+
* Supports weighted scoring across multiple fields
|
|
4
|
+
*/
|
|
5
|
+
import type { SearchableItem, SearchResult } from "../types";
|
|
6
|
+
export interface SearchOptions {
|
|
7
|
+
maxResults?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Perform fuzzy search across searchable items
|
|
11
|
+
*
|
|
12
|
+
* @param items - Array of items to search
|
|
13
|
+
* @param query - Search query string
|
|
14
|
+
* @param options - Search options (maxResults)
|
|
15
|
+
* @returns Sorted array of search results with scores
|
|
16
|
+
*/
|
|
17
|
+
export declare function fuzzySearch(items: SearchableItem[], query: string, options?: SearchOptions): SearchResult[];
|
|
18
|
+
//# sourceMappingURL=fuzzy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fuzzy.d.ts","sourceRoot":"","sources":["../../../src/shell/search/fuzzy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7D,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAmHD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,cAAc,EAAE,EACvB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,aAAkB,GAC1B,YAAY,EAAE,CAyBhB"}
|