forked from EduCraft/curriculum-project-hub
chore: drop superseded admin-panel agent-config prototype after rebase onto main
main now ships the canonical org-scoped agent-config implementation (OrganizationAgentRole/OrganizationAgentSkill + hub/src/agent/configuration.ts + hub/src/agent/skillStore.ts per ADR-0018, and envelope-encrypted OrganizationProviderConnection per ADR-0024). The earlier admin-panel prototype (OrgModel/OrgRole/simple ProviderConnection baseUrl+authToken, modelRoutes.ts, agentConfig.ts, migration 20260710120000, admin-web models/roles pages) is superseded and clashes with main's schema; drop it. Follow-up still needed: rewire admin-web SPA to the new config APIs (+layout.svelte nav still lists models/roles, RoleCard.svelte unused).
This commit is contained in:
@@ -1,169 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { api, type OrgModelRow } from '$lib/api';
|
||||
import { fmtDateOnly } from '$lib/format';
|
||||
import PageHeader from '$lib/components/PageHeader.svelte';
|
||||
import LoadingState from '$lib/components/LoadingState.svelte';
|
||||
import ErrorBanner from '$lib/components/ErrorBanner.svelte';
|
||||
import EmptyState from '$lib/components/EmptyState.svelte';
|
||||
import CheckboxControl from '$lib/components/CheckboxControl.svelte';
|
||||
import { toastError, toastSuccess } from '$lib/toast';
|
||||
|
||||
const slug = $derived(page.params.slug ?? '');
|
||||
|
||||
let models = $state<OrgModelRow[]>([]);
|
||||
let loading = $state(true);
|
||||
let error = $state<string | null>(null);
|
||||
|
||||
let newModelId = $state('');
|
||||
let newLabel = $state('');
|
||||
let newSortKey = $state('');
|
||||
let adding = $state(false);
|
||||
|
||||
async function load() {
|
||||
loading = true;
|
||||
error = null;
|
||||
try {
|
||||
const res = await api.models(slug);
|
||||
models = res.models;
|
||||
} catch (err) {
|
||||
error = err instanceof Error ? err.message : String(err);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function add() {
|
||||
if (!newModelId.trim() || !newLabel.trim()) return;
|
||||
adding = true;
|
||||
try {
|
||||
const m = await api.createModel(slug, {
|
||||
modelId: newModelId.trim(),
|
||||
label: newLabel.trim(),
|
||||
...(newSortKey.trim() ? { sortKey: newSortKey.trim() } : {})
|
||||
});
|
||||
models = [...models, m];
|
||||
newModelId = '';
|
||||
newLabel = '';
|
||||
newSortKey = '';
|
||||
toastSuccess('模型已添加');
|
||||
} catch (err) {
|
||||
toastError(err instanceof Error ? err.message : String(err));
|
||||
} finally {
|
||||
adding = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function save(m: OrgModelRow, patch: Partial<OrgModelRow>) {
|
||||
try {
|
||||
const updated = await api.updateModel(slug, m.id, patch);
|
||||
models = models.map((x) => (x.id === m.id ? updated : x));
|
||||
toastSuccess('已保存');
|
||||
} catch (err) {
|
||||
toastError(err instanceof Error ? err.message : String(err));
|
||||
await load();
|
||||
}
|
||||
}
|
||||
|
||||
async function remove(m: OrgModelRow) {
|
||||
if (!confirm(`删除模型 ${m.label}?`)) return;
|
||||
try {
|
||||
await api.deleteModel(slug, m.id);
|
||||
models = models.filter((x) => x.id !== m.id);
|
||||
toastSuccess('模型已删除');
|
||||
} catch (err) {
|
||||
toastError(err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (slug) load();
|
||||
});
|
||||
</script>
|
||||
|
||||
<PageHeader
|
||||
title="模型"
|
||||
description="本组织启用的模型清单。模型 ID 面向供应方接口;显示名面向教师侧切换。"
|
||||
/>
|
||||
|
||||
{#if loading}
|
||||
<LoadingState />
|
||||
{:else if error}
|
||||
<ErrorBanner message={error} onretry={load} />
|
||||
{:else}
|
||||
<div class="saas-card-pad mb-6">
|
||||
<h2 class="saas-section-title mb-4">添加模型</h2>
|
||||
<div class="grid gap-3 md:grid-cols-[1.4fr_1fr_auto_auto]">
|
||||
<input
|
||||
class="saas-input font-mono text-sm"
|
||||
placeholder="模型 ID(如 anthropic/claude-sonnet-5)"
|
||||
bind:value={newModelId}
|
||||
/>
|
||||
<input class="saas-input" placeholder="显示名(如 Claude Sonnet 5)" bind:value={newLabel} />
|
||||
<input class="saas-input w-28" placeholder="排序" bind:value={newSortKey} />
|
||||
<button class="saas-btn-primary" onclick={add} disabled={adding}>添加</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="saas-card overflow-hidden">
|
||||
<div class="flex items-center justify-between border-b border-surface-200 px-5 py-3">
|
||||
<h2 class="text-sm font-semibold">已启用模型</h2>
|
||||
<span class="saas-badge-neutral">{models.length}</span>
|
||||
</div>
|
||||
{#if models.length === 0}
|
||||
<EmptyState title="暂无已启用模型" description="空清单时会回退到环境默认模型。" />
|
||||
{:else}
|
||||
<div class="overflow-x-auto">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>显示名</th>
|
||||
<th>模型 ID</th>
|
||||
<th>工具能力</th>
|
||||
<th>排序</th>
|
||||
<th>创建</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each models as m (m.id)}
|
||||
<tr>
|
||||
<td>
|
||||
<input
|
||||
class="saas-input py-1.5"
|
||||
value={m.label}
|
||||
onchange={(e) => save(m, { label: (e.target as HTMLInputElement).value })}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
class="saas-input py-1.5 font-mono text-xs"
|
||||
value={m.modelId}
|
||||
onchange={(e) => save(m, { modelId: (e.target as HTMLInputElement).value })}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<CheckboxControl
|
||||
checked={m.toolCapable}
|
||||
onchange={(toolCapable) => save(m, { toolCapable })}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
class="saas-input w-20 py-1.5"
|
||||
value={m.sortKey}
|
||||
onchange={(e) => save(m, { sortKey: (e.target as HTMLInputElement).value })}
|
||||
/>
|
||||
</td>
|
||||
<td class="text-xs text-surface-600">{fmtDateOnly(m.createdAt)}</td>
|
||||
<td class="text-right">
|
||||
<button class="saas-btn-danger !py-1 text-xs" onclick={() => remove(m)}>删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -1,106 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { api, type OrgRoleRow, type OrgModelRow } from '$lib/api';
|
||||
import PageHeader from '$lib/components/PageHeader.svelte';
|
||||
import LoadingState from '$lib/components/LoadingState.svelte';
|
||||
import ErrorBanner from '$lib/components/ErrorBanner.svelte';
|
||||
import EmptyState from '$lib/components/EmptyState.svelte';
|
||||
import RoleCard from '$lib/components/RoleCard.svelte';
|
||||
import { toastError, toastSuccess } from '$lib/toast';
|
||||
|
||||
const slug = $derived(page.params.slug ?? '');
|
||||
|
||||
let roles = $state<OrgRoleRow[]>([]);
|
||||
let models = $state<OrgModelRow[]>([]);
|
||||
let loading = $state(true);
|
||||
let error = $state<string | null>(null);
|
||||
|
||||
let newRoleId = $state('');
|
||||
let newLabel = $state('');
|
||||
let adding = $state(false);
|
||||
|
||||
async function load() {
|
||||
loading = true;
|
||||
error = null;
|
||||
try {
|
||||
const [r, m] = await Promise.all([api.roles(slug), api.models(slug)]);
|
||||
roles = r.roles;
|
||||
models = m.models;
|
||||
} catch (err) {
|
||||
error = err instanceof Error ? err.message : String(err);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function add() {
|
||||
if (!newRoleId.trim() || !newLabel.trim()) return;
|
||||
adding = true;
|
||||
try {
|
||||
const r = await api.createRole(slug, { roleId: newRoleId.trim(), label: newLabel.trim() });
|
||||
roles = [...roles, r];
|
||||
newRoleId = '';
|
||||
newLabel = '';
|
||||
toastSuccess('角色已创建');
|
||||
} catch (err) {
|
||||
toastError(err instanceof Error ? err.message : String(err));
|
||||
} finally {
|
||||
adding = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function remove(r: OrgRoleRow) {
|
||||
if (!confirm(`删除角色 ${r.label} (${r.roleId})?`)) return;
|
||||
try {
|
||||
await api.deleteRole(slug, r.id);
|
||||
roles = roles.filter((x) => x.id !== r.id);
|
||||
toastSuccess('角色已删除');
|
||||
} catch (err) {
|
||||
toastError(err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (slug) load();
|
||||
});
|
||||
</script>
|
||||
|
||||
<PageHeader
|
||||
title="角色"
|
||||
description="角色按数据配置。角色 ID 同时是斜杠命令(如 /draft),可绑定默认模型、工具白名单与系统提示词。"
|
||||
/>
|
||||
|
||||
{#if loading}
|
||||
<LoadingState />
|
||||
{:else if error}
|
||||
<ErrorBanner message={error} onretry={load} />
|
||||
{:else}
|
||||
<div class="saas-card-pad mb-6">
|
||||
<h2 class="saas-section-title mb-4">新建角色</h2>
|
||||
<div class="grid gap-3 sm:grid-cols-[10rem_1fr_auto]">
|
||||
<input class="saas-input font-mono text-sm" placeholder="角色 ID(如 draft)" bind:value={newRoleId} />
|
||||
<input class="saas-input" placeholder="显示名(如 草稿)" bind:value={newLabel} />
|
||||
<button class="saas-btn-primary" onclick={add} disabled={adding}>新建</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if roles.length === 0}
|
||||
<div class="saas-card">
|
||||
<EmptyState title="暂无角色" description="未配置时回退环境默认角色(draft / review)。" />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="space-y-4">
|
||||
{#each roles as r (r.id)}
|
||||
<RoleCard
|
||||
{r}
|
||||
{models}
|
||||
{slug}
|
||||
onremoved={() => remove(r)}
|
||||
onupdated={(updated: OrgRoleRow) => {
|
||||
roles = roles.map((x) => (x.id === r.id ? updated : x));
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
Reference in New Issue
Block a user