workspace upadate
This commit is contained in:
@@ -259,7 +259,7 @@ export default function AdminLayout() {
|
||||
<Shield className="mr-2 h-4 w-4" /> Security
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => navigate('/admin/workspace')} className="cursor-pointer">
|
||||
<LayoutGrid className="mr-2 h-4 w-4" /> Workspace & Skills
|
||||
<LayoutGrid className="mr-2 h-4 w-4" /> Workspace
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
@@ -308,7 +308,7 @@ export default function AdminLayout() {
|
||||
{[
|
||||
{ label: 'Settings', path: '/admin/settings' },
|
||||
{ label: 'Security', path: '/admin/profile#security' },
|
||||
{ label: 'Workspace & Skills', path: '/admin/workspace' },
|
||||
{ label: 'Workspace', path: '/admin/workspace' },
|
||||
].map((item) => (
|
||||
<Link
|
||||
key={item.label}
|
||||
|
||||
@@ -55,7 +55,7 @@ export default function AdminWorkspace() {
|
||||
|
||||
return (
|
||||
<AdminPage
|
||||
title="Workspace & Skills"
|
||||
title="Workspace"
|
||||
subtitle="Govern the AI agent capabilities that extend Owliver and KROW pages."
|
||||
>
|
||||
<div className="space-y-6 w-full">
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from '@/components/ds';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { usePreferences, useUpdatePreferences } from '@/lib/krowHooks';
|
||||
import { PAGE_KEYS, readSkillRegistry, skillsWithFacet } from '@/lib/skills/registry';
|
||||
import { PAGE_KEYS, parseSkill, readSkillRegistry, skillsWithFacet } from '@/lib/skills/registry';
|
||||
import { removeCustomSkill, upsertCustomSkill } from '@/lib/skills/customSkills';
|
||||
import { countSections } from '@/lib/skills/uiConfig';
|
||||
import {
|
||||
@@ -343,6 +343,51 @@ export default function WorkspaceSkills() {
|
||||
toast.success(`${skill.name} restored — switched off`);
|
||||
};
|
||||
|
||||
const handleFixDiagnostic = (d) => {
|
||||
if (!d.skillId) return;
|
||||
const isCustom = customSkills.some((cs) => {
|
||||
try {
|
||||
const parsed = parseSkill(typeof cs === 'string' ? cs : cs?.raw ?? '', { custom: true });
|
||||
return parsed?.id === d.skillId;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (isCustom) {
|
||||
const nextCustom = removeCustomSkill(customSkills, d.skillId);
|
||||
updatePreferences.mutate({ customSkills: nextCustom });
|
||||
toast.success(`Removed custom skill "${d.skillId}"`);
|
||||
} else {
|
||||
const patch = {
|
||||
removedSkills: [...new Set([...removedSkills, d.skillId])],
|
||||
disabledSkills: [...new Set([...disabledSkills, d.skillId])],
|
||||
};
|
||||
updatePreferences.mutate(patch);
|
||||
toast.success(`Removed "${d.skillId}" from workspace`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFixAllDiagnostics = () => {
|
||||
let nextCustom = [...customSkills];
|
||||
let nextRemoved = [...removedSkills];
|
||||
let nextDisabled = [...disabledSkills];
|
||||
|
||||
diagnostics.forEach((d) => {
|
||||
if (!d.skillId) return;
|
||||
nextCustom = removeCustomSkill(nextCustom, d.skillId);
|
||||
nextRemoved = [...new Set([...nextRemoved, d.skillId])];
|
||||
nextDisabled = [...new Set([...nextDisabled, d.skillId])];
|
||||
});
|
||||
|
||||
updatePreferences.mutate({
|
||||
customSkills: nextCustom,
|
||||
removedSkills: nextRemoved,
|
||||
disabledSkills: nextDisabled,
|
||||
});
|
||||
toast.success('Resolved all skill problems');
|
||||
};
|
||||
|
||||
/** A row opens the editor its own kind is authored in. */
|
||||
const editRoute = (skill) => (facet === 'owliver'
|
||||
? `/admin/workspace/skills/owliver/${skill.id}`
|
||||
@@ -403,15 +448,38 @@ export default function WorkspaceSkills() {
|
||||
tone={diagnostics.some((d) => d.level === 'error') ? 'destructive' : 'warning'}
|
||||
title={`${diagnostics.length} skill ${diagnostics.length === 1 ? 'problem' : 'problems'} in this workspace`}
|
||||
>
|
||||
<ul className="space-y-1">
|
||||
{diagnostics.map((d, i) => (
|
||||
<li key={`${d.kind}:${d.skillId ?? d.path}:${i}`} className="text-caption leading-relaxed">
|
||||
<span className="font-mono text-[11px]">{d.skillId || d.path}</span>
|
||||
{' — '}
|
||||
{d.message}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="flex flex-col gap-3">
|
||||
<ul className="space-y-2">
|
||||
{diagnostics.map((d, i) => (
|
||||
<li key={`${d.kind}:${d.skillId ?? d.path}:${i}`} className="text-caption leading-relaxed flex flex-col sm:flex-row sm:items-center justify-between gap-2">
|
||||
<div>
|
||||
<span className="font-mono text-[11px] font-semibold">{d.skillId || d.path}</span>
|
||||
{' — '}
|
||||
<span>{d.message}</span>
|
||||
</div>
|
||||
{d.skillId && (
|
||||
<Button
|
||||
size="xs"
|
||||
variant="outline"
|
||||
onClick={() => handleFixDiagnostic(d)}
|
||||
className="shrink-0 self-start sm:self-auto"
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="pt-2 border-t border-amber-200/50 flex justify-end">
|
||||
<Button
|
||||
size="xs"
|
||||
variant="outline"
|
||||
onClick={handleFixAllDiagnostics}
|
||||
>
|
||||
Resolve all problems
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user