@vertesia/tools-admin-ui 1.3.0 → 1.4.0-dev.20260615.042549Z
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/LICENSE +198 -10
- package/lib/AdminApp.d.ts +1 -1
- package/lib/AdminApp.d.ts.map +1 -1
- package/lib/components/AdminTopBar.d.ts +1 -1
- package/lib/components/AdminTopBar.d.ts.map +1 -1
- package/lib/components/CollectionCard.d.ts +1 -1
- package/lib/components/CollectionCard.d.ts.map +1 -1
- package/lib/components/DetailPage.d.ts +1 -1
- package/lib/components/DetailPage.d.ts.map +1 -1
- package/lib/components/EndpointPanel.d.ts +1 -1
- package/lib/components/EndpointPanel.d.ts.map +1 -1
- package/lib/components/HeroSection.d.ts +1 -1
- package/lib/components/HeroSection.d.ts.map +1 -1
- package/lib/components/ResourceCard.d.ts +1 -1
- package/lib/components/ResourceCard.d.ts.map +1 -1
- package/lib/components/ResourceSection.d.ts +1 -1
- package/lib/components/ResourceSection.d.ts.map +1 -1
- package/lib/components/SearchBar.d.ts +1 -1
- package/lib/components/SearchBar.d.ts.map +1 -1
- package/lib/components/SummaryBadge.d.ts +1 -1
- package/lib/components/SummaryBadge.d.ts.map +1 -1
- package/lib/hooks.d.ts +2 -2
- package/lib/hooks.d.ts.map +1 -1
- package/lib/index.d.ts +4 -4
- package/lib/index.d.ts.map +1 -1
- package/lib/pages/ActivityCollection.d.ts +1 -1
- package/lib/pages/ActivityCollection.d.ts.map +1 -1
- package/lib/pages/HomePage.d.ts +1 -1
- package/lib/pages/HomePage.d.ts.map +1 -1
- package/lib/pages/InteractionCollection.d.ts +1 -1
- package/lib/pages/InteractionCollection.d.ts.map +1 -1
- package/lib/pages/InteractionDetail.d.ts +1 -1
- package/lib/pages/InteractionDetail.d.ts.map +1 -1
- package/lib/pages/SkillCollection.d.ts +1 -1
- package/lib/pages/SkillCollection.d.ts.map +1 -1
- package/lib/pages/SkillDetail.d.ts +1 -1
- package/lib/pages/SkillDetail.d.ts.map +1 -1
- package/lib/pages/TemplateCollection.d.ts +1 -1
- package/lib/pages/TemplateCollection.d.ts.map +1 -1
- package/lib/pages/TemplateDetail.d.ts +1 -1
- package/lib/pages/TemplateDetail.d.ts.map +1 -1
- package/lib/pages/ToolCollection.d.ts +1 -1
- package/lib/pages/ToolCollection.d.ts.map +1 -1
- package/lib/pages/TypeCollection.d.ts +1 -1
- package/lib/pages/TypeCollection.d.ts.map +1 -1
- package/lib/pages/TypeDetail.d.ts +1 -1
- package/lib/pages/TypeDetail.d.ts.map +1 -1
- package/lib/tools-admin-ui.js +1484 -969
- package/lib/tools-admin-ui.js.map +1 -1
- package/lib/types.d.ts.map +1 -1
- package/package.json +16 -14
- package/src/AdminApp.tsx +14 -15
- package/src/components/AdminTopBar.tsx +7 -7
- package/src/components/CollectionCard.tsx +3 -1
- package/src/components/DetailPage.tsx +10 -4
- package/src/components/EndpointPanel.tsx +2 -9
- package/src/components/HeroSection.tsx +12 -3
- package/src/components/ResourceCard.tsx +8 -6
- package/src/components/ResourceSection.tsx +1 -1
- package/src/components/SearchBar.tsx +1 -5
- package/src/components/SummaryBadge.tsx +2 -1
- package/src/dev/env.ts +8 -8
- package/src/dev/main.tsx +20 -15
- package/src/hooks.ts +19 -12
- package/src/index.ts +4 -4
- package/src/pages/ActivityCollection.tsx +30 -12
- package/src/pages/HomePage.tsx +65 -57
- package/src/pages/InteractionCollection.tsx +26 -11
- package/src/pages/InteractionDetail.tsx +28 -13
- package/src/pages/SkillCollection.tsx +38 -18
- package/src/pages/SkillDetail.tsx +37 -13
- package/src/pages/TemplateCollection.tsx +28 -17
- package/src/pages/TemplateDetail.tsx +13 -7
- package/src/pages/ToolCollection.tsx +21 -10
- package/src/pages/TypeCollection.tsx +26 -17
- package/src/pages/TypeDetail.tsx +12 -6
- package/src/types.ts +16 -35
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertesia/tools-admin-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0-dev.20260615.042549Z",
|
|
4
4
|
"description": "Shared admin UI for Vertesia tool servers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -16,24 +16,25 @@
|
|
|
16
16
|
"./theme.css": "./src/theme.css"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"react": "^19.
|
|
20
|
-
"react-dom": "^19.
|
|
19
|
+
"react": "^19.2.7",
|
|
20
|
+
"react-dom": "^19.2.7"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@tailwindcss/vite": "^4.
|
|
24
|
-
"@types/react": "^19.2.
|
|
23
|
+
"@tailwindcss/vite": "^4.3.0",
|
|
24
|
+
"@types/react": "^19.2.16",
|
|
25
25
|
"@types/react-dom": "^19.2.3",
|
|
26
|
-
"@vitejs/plugin-react": "^
|
|
27
|
-
"react": "^19.2.
|
|
28
|
-
"react-dom": "^19.2.
|
|
29
|
-
"tailwindcss": "^4.
|
|
30
|
-
"typescript": "^6.0.
|
|
31
|
-
"vite": "^
|
|
26
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
27
|
+
"react": "^19.2.7",
|
|
28
|
+
"react-dom": "^19.2.7",
|
|
29
|
+
"tailwindcss": "^4.3.0",
|
|
30
|
+
"typescript": "^6.0.3",
|
|
31
|
+
"vite": "^8.0.16",
|
|
32
|
+
"@vertesia/tsconfig": "0.1.0"
|
|
32
33
|
},
|
|
33
34
|
"dependencies": {
|
|
34
|
-
"lucide-react": "^
|
|
35
|
-
"@vertesia/common": "1.
|
|
36
|
-
"@vertesia/ui": "1.
|
|
35
|
+
"lucide-react": "^1.17.0",
|
|
36
|
+
"@vertesia/common": "1.4.0-dev.20260615.042549Z",
|
|
37
|
+
"@vertesia/ui": "1.4.0-dev.20260615.042549Z"
|
|
37
38
|
},
|
|
38
39
|
"repository": {
|
|
39
40
|
"type": "git",
|
|
@@ -48,6 +49,7 @@
|
|
|
48
49
|
"react"
|
|
49
50
|
],
|
|
50
51
|
"scripts": {
|
|
52
|
+
"lint": "biome lint src",
|
|
51
53
|
"build": "rm -rf lib tsconfig.tsbuildinfo && tsc --build && vite build --mode lib",
|
|
52
54
|
"dev": "vite dev"
|
|
53
55
|
}
|
package/src/AdminApp.tsx
CHANGED
|
@@ -47,10 +47,11 @@ export interface AdminAppProps {
|
|
|
47
47
|
*/
|
|
48
48
|
export function AdminApp({ baseUrl = '/api' }: AdminAppProps) {
|
|
49
49
|
const { data: serverInfo, isLoading: loadingInfo, error: infoError } = useServerInfo(baseUrl);
|
|
50
|
-
const {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
const {
|
|
51
|
+
data: resourceData,
|
|
52
|
+
isLoading: loadingData,
|
|
53
|
+
error: dataError,
|
|
54
|
+
} = useResourceData(baseUrl, serverInfo?.endpoints?.mcp);
|
|
54
55
|
|
|
55
56
|
const isLoading = loadingInfo || loadingData;
|
|
56
57
|
const error = infoError || dataError;
|
|
@@ -64,11 +65,7 @@ export function AdminApp({ baseUrl = '/api' }: AdminAppProps) {
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
if (error) {
|
|
67
|
-
return
|
|
68
|
-
<div className="p-6 text-destructive">
|
|
69
|
-
Failed to load server info. Is the API running?
|
|
70
|
-
</div>
|
|
71
|
-
);
|
|
68
|
+
return <div className="p-6 text-destructive">Failed to load server info. Is the API running?</div>;
|
|
72
69
|
}
|
|
73
70
|
|
|
74
71
|
if (!serverInfo || !resourceData) return null;
|
|
@@ -78,12 +75,14 @@ export function AdminApp({ baseUrl = '/api' }: AdminAppProps) {
|
|
|
78
75
|
return (
|
|
79
76
|
<div className="min-h-screen bg-background text-foreground">
|
|
80
77
|
<AdminTopBar title={title} />
|
|
81
|
-
<AdminContext.Provider
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
<AdminContext.Provider
|
|
79
|
+
value={{
|
|
80
|
+
serverInfo,
|
|
81
|
+
collections: resourceData.collections,
|
|
82
|
+
resources: resourceData.resources,
|
|
83
|
+
baseUrl,
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
87
86
|
<NestedRouterProvider routes={routes}>
|
|
88
87
|
<RouteComponent />
|
|
89
88
|
</NestedRouterProvider>
|
|
@@ -13,7 +13,12 @@ export function AdminTopBar({ title }: AdminTopBarProps) {
|
|
|
13
13
|
<header className="sticky top-0 z-40 flex h-14 items-center justify-between border-b border-border bg-background px-6">
|
|
14
14
|
<div className="flex items-center gap-3">
|
|
15
15
|
<div className="flex size-8 items-center justify-center rounded-lg bg-primary text-xs font-semibold uppercase tracking-wider text-primary-foreground">
|
|
16
|
-
{title
|
|
16
|
+
{title
|
|
17
|
+
.split(/\s+/)
|
|
18
|
+
.map((w) => w[0])
|
|
19
|
+
.filter(Boolean)
|
|
20
|
+
.slice(0, 2)
|
|
21
|
+
.join('')}
|
|
17
22
|
</div>
|
|
18
23
|
<span className="text-sm font-semibold text-foreground">{title}</span>
|
|
19
24
|
</div>
|
|
@@ -23,12 +28,7 @@ export function AdminTopBar({ title }: AdminTopBarProps) {
|
|
|
23
28
|
{user && (
|
|
24
29
|
<>
|
|
25
30
|
<Avatar size="sm" name={user.name} color="bg-primary" />
|
|
26
|
-
<Button
|
|
27
|
-
variant="outline"
|
|
28
|
-
size="sm"
|
|
29
|
-
onClick={() => logout()}
|
|
30
|
-
alt="Sign out"
|
|
31
|
-
>
|
|
31
|
+
<Button variant="outline" size="sm" onClick={() => logout()} alt="Sign out">
|
|
32
32
|
<LogOut className="size-4" />
|
|
33
33
|
</Button>
|
|
34
34
|
</>
|
|
@@ -12,7 +12,9 @@ export function CollectionCard({ collection }: { collection: CollectionInfo }) {
|
|
|
12
12
|
<NavLink href={href} className="block no-underline">
|
|
13
13
|
<Card className="cursor-pointer transition-all hover:-translate-y-0.5 hover:shadow-md">
|
|
14
14
|
<CardContent className="p-5">
|
|
15
|
-
<span
|
|
15
|
+
<span
|
|
16
|
+
className={`mb-2 inline-block rounded-full px-2 py-0.5 text-[0.7rem] font-semibold uppercase tracking-wide ${TYPE_VARIANTS[collection.type] ?? ''}`}
|
|
17
|
+
>
|
|
16
18
|
{collection.type}
|
|
17
19
|
</span>
|
|
18
20
|
<div className="font-semibold text-card-foreground">{collection.title}</div>
|
|
@@ -20,7 +20,9 @@ export function DetailPage({ type, title, description, tags, backHref = '/', chi
|
|
|
20
20
|
<div className="mx-auto max-w-5xl px-7 py-10">
|
|
21
21
|
<nav className="mb-5 flex items-center gap-4">
|
|
22
22
|
{backHref !== '/' && (
|
|
23
|
-
<NavLink href="/" className="text-sm text-primary hover:opacity-75">
|
|
23
|
+
<NavLink href="/" className="text-sm text-primary hover:opacity-75">
|
|
24
|
+
Home
|
|
25
|
+
</NavLink>
|
|
24
26
|
)}
|
|
25
27
|
<NavLink href={backHref} className="flex items-center gap-1 text-sm text-primary hover:opacity-75">
|
|
26
28
|
<ArrowLeft className="size-3.5" />
|
|
@@ -29,15 +31,19 @@ export function DetailPage({ type, title, description, tags, backHref = '/', chi
|
|
|
29
31
|
</nav>
|
|
30
32
|
|
|
31
33
|
<div className="mb-8">
|
|
32
|
-
<span
|
|
34
|
+
<span
|
|
35
|
+
className={`mb-2 inline-block rounded-full px-2 py-0.5 text-[0.7rem] font-semibold uppercase tracking-wide ${TYPE_VARIANTS[type] ?? ''}`}
|
|
36
|
+
>
|
|
33
37
|
{type}
|
|
34
38
|
</span>
|
|
35
39
|
<h1 className="-tracking-wide text-3xl font-bold text-foreground">{title}</h1>
|
|
36
40
|
{description && <p className="mt-1 text-sm leading-relaxed text-muted-foreground">{description}</p>}
|
|
37
41
|
{tags && tags.length > 0 && (
|
|
38
42
|
<div className="mt-3 flex flex-wrap gap-1.5">
|
|
39
|
-
{tags.map(tag => (
|
|
40
|
-
<Badge key={tag} variant="default">
|
|
43
|
+
{tags.map((tag) => (
|
|
44
|
+
<Badge key={tag} variant="default">
|
|
45
|
+
{tag}
|
|
46
|
+
</Badge>
|
|
41
47
|
))}
|
|
42
48
|
</div>
|
|
43
49
|
)}
|
|
@@ -14,17 +14,10 @@ export function EndpointPanel({ label, path }: { label: string; path: string })
|
|
|
14
14
|
|
|
15
15
|
return (
|
|
16
16
|
<div className="mb-3">
|
|
17
|
-
<div className="mb-1 text-[0.7rem] font-medium uppercase tracking-widest text-primary">
|
|
18
|
-
{label}
|
|
19
|
-
</div>
|
|
17
|
+
<div className="mb-1 text-[0.7rem] font-medium uppercase tracking-widest text-primary">{label}</div>
|
|
20
18
|
<div className="flex items-center gap-2 rounded-lg border border-border bg-muted-background px-3 py-2">
|
|
21
19
|
<code className="flex-1 font-mono text-sm text-foreground">{path}</code>
|
|
22
|
-
<Button
|
|
23
|
-
variant="ghost"
|
|
24
|
-
size="xs"
|
|
25
|
-
onClick={handleCopy}
|
|
26
|
-
aria-label="Copy full URL"
|
|
27
|
-
>
|
|
20
|
+
<Button variant="ghost" size="xs" onClick={handleCopy} aria-label="Copy full URL">
|
|
28
21
|
{copied ? <Check className="size-3.5" /> : <Copy className="size-3.5" />}
|
|
29
22
|
</Button>
|
|
30
23
|
</div>
|
|
@@ -12,7 +12,13 @@ interface HeroSectionProps {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
function getInitials(title: string): string {
|
|
15
|
-
return title
|
|
15
|
+
return title
|
|
16
|
+
.split(/\s+/)
|
|
17
|
+
.map((w) => w[0])
|
|
18
|
+
.filter(Boolean)
|
|
19
|
+
.slice(0, 2)
|
|
20
|
+
.join('')
|
|
21
|
+
.toUpperCase();
|
|
16
22
|
}
|
|
17
23
|
|
|
18
24
|
function countByType(resources: ResourceItem[]): Record<string, number> {
|
|
@@ -45,7 +51,9 @@ export function HeroSection({ title, version, resources }: HeroSectionProps) {
|
|
|
45
51
|
{getInitials(title)}
|
|
46
52
|
</div>
|
|
47
53
|
<div>
|
|
48
|
-
<p className="text-xs font-medium uppercase tracking-widest text-muted-foreground">
|
|
54
|
+
<p className="text-xs font-medium uppercase tracking-widest text-muted-foreground">
|
|
55
|
+
Tools Server
|
|
56
|
+
</p>
|
|
49
57
|
<h1 className="-tracking-wide text-2xl font-bold text-foreground">{title}</h1>
|
|
50
58
|
</div>
|
|
51
59
|
</div>
|
|
@@ -85,7 +93,8 @@ export function HeroSection({ title, version, resources }: HeroSectionProps) {
|
|
|
85
93
|
<EndpointPanel label="Package endpoint" path="/api/package" />
|
|
86
94
|
<p className="mt-2 text-xs leading-relaxed text-muted-foreground">
|
|
87
95
|
Use <strong className="text-foreground">POST /api/tools/<collection></strong> or{' '}
|
|
88
|
-
<strong className="text-foreground">POST /api/skills/<collection></strong> to call these
|
|
96
|
+
<strong className="text-foreground">POST /api/skills/<collection></strong> to call these
|
|
97
|
+
from your apps or agents.
|
|
89
98
|
</p>
|
|
90
99
|
<p className="mt-1 text-xs text-muted-foreground">v{version}</p>
|
|
91
100
|
</aside>
|
|
@@ -7,17 +7,19 @@ export function ResourceCard({ resource }: { resource: ResourceItem }) {
|
|
|
7
7
|
return (
|
|
8
8
|
<Card className="transition-all hover:-translate-y-0.5 hover:shadow-md">
|
|
9
9
|
<CardContent className="p-5">
|
|
10
|
-
<span
|
|
10
|
+
<span
|
|
11
|
+
className={`mb-2 inline-block rounded-full px-2 py-0.5 text-[0.7rem] font-semibold uppercase tracking-wide ${TYPE_VARIANTS[resource.type] ?? ''}`}
|
|
12
|
+
>
|
|
11
13
|
{resource.type}
|
|
12
14
|
</span>
|
|
13
15
|
<div className="font-semibold text-card-foreground">{resource.title}</div>
|
|
14
|
-
<div className="mt-1 text-sm text-muted-foreground">
|
|
15
|
-
{resource.description || 'No description'}
|
|
16
|
-
</div>
|
|
16
|
+
<div className="mt-1 text-sm text-muted-foreground">{resource.description || 'No description'}</div>
|
|
17
17
|
{resource.tags && resource.tags.length > 0 && (
|
|
18
18
|
<div className="mt-2 flex flex-wrap gap-1">
|
|
19
|
-
{resource.tags.map(tag => (
|
|
20
|
-
<Badge key={tag} variant="default">
|
|
19
|
+
{resource.tags.map((tag) => (
|
|
20
|
+
<Badge key={tag} variant="default">
|
|
21
|
+
{tag}
|
|
22
|
+
</Badge>
|
|
21
23
|
))}
|
|
22
24
|
</div>
|
|
23
25
|
)}
|
|
@@ -24,7 +24,7 @@ export function ResourceSection({ title, subtitle, resources, showDivider }: Res
|
|
|
24
24
|
<p className="mb-4 text-sm text-muted-foreground">{subtitle}</p>
|
|
25
25
|
</div>
|
|
26
26
|
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
27
|
-
{resources.map(r => (
|
|
27
|
+
{resources.map((r) => (
|
|
28
28
|
<ResourceCard key={`${r.type}:${r.name}`} resource={r} />
|
|
29
29
|
))}
|
|
30
30
|
</div>
|
|
@@ -27,11 +27,7 @@ export function SearchBar({ value, onChange, placeholder, resultCount, totalCoun
|
|
|
27
27
|
Showing {resultCount} of {totalCount} resources
|
|
28
28
|
</p>
|
|
29
29
|
)}
|
|
30
|
-
{noResults &&
|
|
31
|
-
<p className="mt-2 text-sm text-destructive">
|
|
32
|
-
No resources match this search.
|
|
33
|
-
</p>
|
|
34
|
-
)}
|
|
30
|
+
{noResults && <p className="mt-2 text-sm text-destructive">No resources match this search.</p>}
|
|
35
31
|
</div>
|
|
36
32
|
);
|
|
37
33
|
}
|
|
@@ -4,7 +4,8 @@ export function SummaryBadge({ count, label }: { count: number; label: string })
|
|
|
4
4
|
if (count === 0) return null;
|
|
5
5
|
return (
|
|
6
6
|
<DotBadge variant="success">
|
|
7
|
-
{count} {label}
|
|
7
|
+
{count} {label}
|
|
8
|
+
{count !== 1 ? 's' : ''}
|
|
8
9
|
</DotBadge>
|
|
9
10
|
);
|
|
10
11
|
}
|
package/src/dev/env.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { Env } from
|
|
1
|
+
import { Env } from '@vertesia/ui/env';
|
|
2
2
|
|
|
3
3
|
Env.init({
|
|
4
|
-
name:
|
|
5
|
-
version:
|
|
4
|
+
name: 'Tools Admin UI',
|
|
5
|
+
version: '1.0.0',
|
|
6
6
|
isLocalDev: true,
|
|
7
7
|
isDocker: true,
|
|
8
|
-
type:
|
|
8
|
+
type: 'development',
|
|
9
9
|
endpoints: {
|
|
10
|
-
studio:
|
|
11
|
-
zeno:
|
|
12
|
-
sts:
|
|
13
|
-
}
|
|
10
|
+
studio: 'https://api.us1.vertesia.io',
|
|
11
|
+
zeno: 'https://api.us1.vertesia.io',
|
|
12
|
+
sts: 'https://sts.us1.vertesia.io',
|
|
13
|
+
},
|
|
14
14
|
});
|
package/src/dev/main.tsx
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
2
|
import './index.css';
|
|
3
|
-
import
|
|
3
|
+
import './env.js';
|
|
4
4
|
|
|
5
|
-
import { I18nProvider } from '@vertesia/ui/i18n';
|
|
6
|
-
import { VertesiaShell } from '@vertesia/ui/shell';
|
|
7
5
|
import { RouterProvider } from '@vertesia/ui/router';
|
|
6
|
+
import { VertesiaShell } from '@vertesia/ui/shell';
|
|
8
7
|
import { StrictMode } from 'react';
|
|
9
8
|
import { createRoot } from 'react-dom/client';
|
|
10
9
|
|
|
@@ -12,6 +11,7 @@ import { AdminApp } from '../AdminApp.js';
|
|
|
12
11
|
|
|
13
12
|
const baseUrl = import.meta.env.VITE_API_BASE_URL;
|
|
14
13
|
|
|
14
|
+
// biome-ignore lint/style/noNonNullAssertion: intentional non-null assertion; TS can't prove narrowing here
|
|
15
15
|
const root = createRoot(document.getElementById('root')!);
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -22,22 +22,27 @@ const devRoutes = [{ path: '/*', Component: () => <AdminApp baseUrl={baseUrl} />
|
|
|
22
22
|
|
|
23
23
|
if (!baseUrl) {
|
|
24
24
|
root.render(
|
|
25
|
-
<
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
<StrictMode>
|
|
26
|
+
<div style={{ padding: '2rem', fontFamily: 'system-ui, sans-serif', color: '#ef4444' }}>
|
|
27
|
+
<h2>Missing environment variable</h2>
|
|
28
|
+
<p>
|
|
29
|
+
<code>VITE_API_BASE_URL</code> is not defined.
|
|
30
|
+
</p>
|
|
31
|
+
<p>
|
|
32
|
+
Create a <code>.env.local</code> file in this package with:
|
|
33
|
+
</p>
|
|
34
|
+
<pre style={{ background: '#f3f4f6', padding: '1rem', borderRadius: '8px', color: '#111827' }}>
|
|
35
|
+
VITE_API_BASE_URL=http://localhost:3000/api
|
|
36
|
+
</pre>
|
|
37
|
+
</div>
|
|
38
|
+
</StrictMode>,
|
|
32
39
|
);
|
|
33
40
|
} else {
|
|
34
41
|
root.render(
|
|
35
42
|
<StrictMode>
|
|
36
|
-
<
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
</VertesiaShell>
|
|
40
|
-
</I18nProvider>
|
|
43
|
+
<VertesiaShell>
|
|
44
|
+
<RouterProvider routes={devRoutes} />
|
|
45
|
+
</VertesiaShell>
|
|
41
46
|
</StrictMode>,
|
|
42
47
|
);
|
|
43
48
|
}
|
package/src/hooks.ts
CHANGED
|
@@ -7,14 +7,21 @@ import { useFetch } from '@vertesia/ui/core';
|
|
|
7
7
|
import type { ResourceData, ServerInfo } from './types.js';
|
|
8
8
|
import { buildResourceData } from './types.js';
|
|
9
9
|
|
|
10
|
+
type ResourceDataArgs = Parameters<typeof buildResourceData>;
|
|
11
|
+
|
|
12
|
+
async function fetchJson<T>(url: string): Promise<T> {
|
|
13
|
+
const response = await fetch(url);
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
throw new Error(`Request failed: ${response.status} ${response.statusText}`);
|
|
16
|
+
}
|
|
17
|
+
return response.json() as Promise<T>;
|
|
18
|
+
}
|
|
19
|
+
|
|
10
20
|
/**
|
|
11
21
|
* Fetches the tool server info (message, version, endpoints).
|
|
12
22
|
*/
|
|
13
23
|
export function useServerInfo(baseUrl: string) {
|
|
14
|
-
return useFetch<ServerInfo>(() =>
|
|
15
|
-
fetch(baseUrl).then(r => r.json()),
|
|
16
|
-
[baseUrl]
|
|
17
|
-
);
|
|
24
|
+
return useFetch<ServerInfo>(() => fetchJson<ServerInfo>(baseUrl), [baseUrl]);
|
|
18
25
|
}
|
|
19
26
|
|
|
20
27
|
/**
|
|
@@ -23,16 +30,16 @@ export function useServerInfo(baseUrl: string) {
|
|
|
23
30
|
*/
|
|
24
31
|
export function useResourceData(baseUrl: string, mcpEndpoints?: string[]) {
|
|
25
32
|
return useFetch<ResourceData>(() => {
|
|
26
|
-
const
|
|
33
|
+
const fetchResource = <T>(path: string) => fetchJson<T>(`${baseUrl}/${path}`);
|
|
27
34
|
return Promise.all([
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
fetchResource<ResourceDataArgs[0]>('interactions'),
|
|
36
|
+
fetchResource<ResourceDataArgs[1]>('tools'),
|
|
37
|
+
fetchResource<ResourceDataArgs[2]>('skills'),
|
|
38
|
+
fetchResource<ResourceDataArgs[3]>('activities'),
|
|
39
|
+
fetchResource<ResourceDataArgs[4]>('types'),
|
|
40
|
+
fetchResource<ResourceDataArgs[5]>('templates'),
|
|
34
41
|
]).then(([interactions, tools, skills, activities, types, templates]) =>
|
|
35
|
-
buildResourceData(interactions, tools, skills, activities, types, templates, mcpEndpoints)
|
|
42
|
+
buildResourceData(interactions, tools, skills, activities, types, templates, mcpEndpoints),
|
|
36
43
|
);
|
|
37
44
|
}, [baseUrl, mcpEndpoints]);
|
|
38
45
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { AdminApp } from './AdminApp.js';
|
|
2
1
|
export type { AdminAppProps } from './AdminApp.js';
|
|
3
|
-
export
|
|
4
|
-
export { buildResourceData, filterResources } from './types.js';
|
|
5
|
-
export { AdminContext, useAdminContext } from './AdminContext.js';
|
|
2
|
+
export { AdminApp } from './AdminApp.js';
|
|
6
3
|
export type { AdminContextValue } from './AdminContext.js';
|
|
4
|
+
export { AdminContext, useAdminContext } from './AdminContext.js';
|
|
5
|
+
export type { CollectionInfo, ResourceItem, ResourceType, ServerInfo } from './types.js';
|
|
6
|
+
export { buildResourceData, filterResources } from './types.js';
|
|
@@ -17,27 +17,41 @@ export function ActivityCollection() {
|
|
|
17
17
|
const { baseUrl } = useAdminContext();
|
|
18
18
|
|
|
19
19
|
const { data, error } = useFetch<ActivityCollectionResponse>(
|
|
20
|
-
() =>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
() =>
|
|
21
|
+
fetch(`${baseUrl}/activities/${collection}`).then((r) => {
|
|
22
|
+
if (!r.ok) throw new Error(`Failed to load collection: ${r.statusText}`);
|
|
23
|
+
return r.json();
|
|
24
|
+
}),
|
|
25
|
+
[baseUrl, collection],
|
|
25
26
|
);
|
|
26
27
|
|
|
27
|
-
if (error)
|
|
28
|
-
|
|
28
|
+
if (error)
|
|
29
|
+
return (
|
|
30
|
+
<div className="p-6 text-destructive">Failed to load activity collection “{collection}”.</div>
|
|
31
|
+
);
|
|
32
|
+
if (!data)
|
|
33
|
+
return (
|
|
34
|
+
<div className="flex h-64 items-center justify-center text-muted-foreground">
|
|
35
|
+
<Spinner />
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
29
38
|
|
|
30
39
|
return (
|
|
31
40
|
<DetailPage
|
|
32
41
|
type="activity"
|
|
33
42
|
title={data.title || collection}
|
|
34
|
-
description={
|
|
43
|
+
description={
|
|
44
|
+
data.description ||
|
|
45
|
+
`${data.activities.length} activit${data.activities.length !== 1 ? 'ies' : 'y'} in this collection.`
|
|
46
|
+
}
|
|
35
47
|
>
|
|
36
|
-
{data.activities.map(activity => (
|
|
48
|
+
{data.activities.map((activity) => (
|
|
37
49
|
<Card key={activity.name} className="mb-4">
|
|
38
50
|
<CardContent className="p-5">
|
|
39
51
|
<div className="mb-2 flex items-center gap-2">
|
|
40
|
-
<span
|
|
52
|
+
<span
|
|
53
|
+
className={`inline-block rounded-full px-2 py-0.5 text-[0.7rem] font-semibold uppercase tracking-wide ${TYPE_VARIANTS.activity}`}
|
|
54
|
+
>
|
|
41
55
|
activity
|
|
42
56
|
</span>
|
|
43
57
|
<span className="font-semibold text-card-foreground">{activity.name}</span>
|
|
@@ -45,7 +59,9 @@ export function ActivityCollection() {
|
|
|
45
59
|
<div className="text-sm text-muted-foreground">{activity.description || 'No description'}</div>
|
|
46
60
|
{activity.input_schema && (
|
|
47
61
|
<div className="mt-3">
|
|
48
|
-
<p className="mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
|
62
|
+
<p className="mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
|
63
|
+
Input Schema
|
|
64
|
+
</p>
|
|
49
65
|
<pre className="whitespace-pre-wrap wrap-break-word rounded-lg border border-border bg-muted-background p-4 font-mono text-sm text-foreground">
|
|
50
66
|
{JSON.stringify(activity.input_schema, null, 2)}
|
|
51
67
|
</pre>
|
|
@@ -53,7 +69,9 @@ export function ActivityCollection() {
|
|
|
53
69
|
)}
|
|
54
70
|
{activity.output_schema && (
|
|
55
71
|
<div className="mt-3">
|
|
56
|
-
<p className="mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
|
72
|
+
<p className="mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
|
73
|
+
Output Schema
|
|
74
|
+
</p>
|
|
57
75
|
<pre className="whitespace-pre-wrap wrap-break-word rounded-lg border border-border bg-muted-background p-4 font-mono text-sm text-foreground">
|
|
58
76
|
{JSON.stringify(activity.output_schema, null, 2)}
|
|
59
77
|
</pre>
|