From a12984d174bc7db7f87d3f46e52c855d83171741 Mon Sep 17 00:00:00 2001 From: ChickenPige0n <2336983354@qq.com> Date: Sat, 18 Jul 2026 12:32:45 +0800 Subject: [PATCH] feat(admin-web): add folder and project creation functionality in FolderNode and FolderTree components --- hub/.env.example | 4 ++ .../src/lib/components/FolderNode.svelte | 60 ++++++++++++++----- .../src/lib/components/FolderTree.svelte | 6 +- hub/admin-web/src/lib/components/Icon.svelte | 18 ++++++ .../admin/org/[slug]/projects/+page.svelte | 29 +++++++-- 5 files changed, 95 insertions(+), 22 deletions(-) diff --git a/hub/.env.example b/hub/.env.example index 3df1b33..e184460 100644 --- a/hub/.env.example +++ b/hub/.env.example @@ -34,6 +34,10 @@ HUB_FEISHU_EVENTS_PER_MINUTE="120" # to this path and rejects any overlap before installing the unit. HUB_PROJECT_WORKSPACE_ROOT="/var/lib/cph-hub/workspaces" +# Persistent root for the content-addressed agent skill store. Required at hub +# startup unless XDG_STATE_HOME is set (then defaults to $XDG_STATE_HOME/skills). +HUB_SKILL_STORE_ROOT="/var/lib/cph-hub/state/skills" + # This process is pinned to exactly one Organization. Feishu credentials are # resolved from that Organization's encrypted ACTIVE connection. HUB_SILO_ORGANIZATION_ID="" diff --git a/hub/admin-web/src/lib/components/FolderNode.svelte b/hub/admin-web/src/lib/components/FolderNode.svelte index 250422c..ade70d3 100644 --- a/hub/admin-web/src/lib/components/FolderNode.svelte +++ b/hub/admin-web/src/lib/components/FolderNode.svelte @@ -8,6 +8,8 @@ folders, projects, slug, + onCreateFolder, + onCreateProject, }: { folder: ExplorerFolder; folders: ExplorerFolder[]; @@ -19,30 +21,58 @@ binding: { chatId: string } | null; }[]; slug: string; + onCreateFolder?: (parentId: string) => void; + onCreateProject?: (folderId: string) => void; } = $props(); let open = $state(true);
- + {#if onCreateFolder || onCreateProject} + + {#if onCreateFolder} + + {/if} + {#if onCreateProject} + + {/if} + {/if} - +
{#if open}
- +
{/if} diff --git a/hub/admin-web/src/lib/components/FolderTree.svelte b/hub/admin-web/src/lib/components/FolderTree.svelte index d8ff45b..8576128 100644 --- a/hub/admin-web/src/lib/components/FolderTree.svelte +++ b/hub/admin-web/src/lib/components/FolderTree.svelte @@ -9,6 +9,8 @@ projects, parentId, slug, + onCreateFolder, + onCreateProject, }: { folders: ExplorerFolder[]; projects: { @@ -20,6 +22,8 @@ }[]; parentId: string | null; slug: string; + onCreateFolder?: (parentId: string) => void; + onCreateProject?: (folderId: string) => void; } = $props(); let childFolders = $derived(folders.filter((f) => f.parentId === parentId)); @@ -44,6 +48,6 @@ {/each} {#each childFolders as f (f.id)} - + {/each} diff --git a/hub/admin-web/src/lib/components/Icon.svelte b/hub/admin-web/src/lib/components/Icon.svelte index dadec19..b9ec72e 100644 --- a/hub/admin-web/src/lib/components/Icon.svelte +++ b/hub/admin-web/src/lib/components/Icon.svelte @@ -18,6 +18,8 @@ | 'arrow-left' | 'folder' | 'file' + | 'folder-plus' + | 'file-plus' | 'check' | 'roles'; class?: string; @@ -122,6 +124,22 @@ d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /> +{:else if name === 'folder-plus'} + + + +{:else if name === 'file-plus'} + + + {:else if name === 'check'} diff --git a/hub/admin-web/src/routes/admin/org/[slug]/projects/+page.svelte b/hub/admin-web/src/routes/admin/org/[slug]/projects/+page.svelte index d9829cb..b79e2f8 100644 --- a/hub/admin-web/src/routes/admin/org/[slug]/projects/+page.svelte +++ b/hub/admin-web/src/routes/admin/org/[slug]/projects/+page.svelte @@ -14,9 +14,7 @@ import { toastError, toastSuccess } from '$lib/toast'; const slug = $derived(page.params.slug ?? ''); - const org = $derived( - ($session.me?.organizations.find((o) => o.slug === slug) as OrgMembership | undefined) ?? null, - ); + const org = $derived(($session.me?.organizations.find((o) => o.slug === slug) as OrgMembership | undefined) ?? null); const isAdmin = $derived(!!org && (org.role === 'OWNER' || org.role === 'ADMIN')); let data = $state(null); @@ -32,6 +30,18 @@ let projectName = $state(''); let projectFolder = $state(''); + function openFolderModal(parentId: string) { + folderName = ''; + folderParent = parentId; + showFolderModal = true; + } + + function openProjectModal(folderId: string) { + projectName = ''; + projectFolder = folderId; + showProjectModal = true; + } + async function load() { loading = true; error = null; @@ -113,8 +123,8 @@ {:else if isAdmin && data} {#snippet actions()} - - + + {/snippet} @@ -122,7 +132,14 @@ {#if data.projects.filter((p) => !p.folderId).length === 0 && data.folders.filter((f) => !f.parentId).length === 0} {:else} - + {/if}