feat(03-01): add skill section type wiring, Zustand slice, i18n, and sidebar nav entry
- Add "skills" to SectionId union in shared types - Add SkillRecord, SkillImportResult, SkillUpdateStatus types to shared/types.ts - Create SkillSlice with skills list, selection, file path, and import modal state - Wire SkillSlice into Zustand store composition - Add "nav.skills" i18n keys for English and Chinese - Add Skills sidebar nav entry with PuzzlePiece icon between Communication and Documents - Add section === "skills" rendering route in App.tsx - Clear skill state on company switch - Add skills case to navigateTo in NavigationSlice Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c1de05a7f1
commit
e8d642941f
9 files changed
+121
-1
No files matched your search
@@ -14,6 +14,7 @@ import {
|
||||
MagnifyingGlass,
|
||||
Megaphone,
|
||||
Plus,
|
||||
PuzzlePiece,
|
||||
UsersThree,
|
||||
ShieldCheck,
|
||||
Trash,
|
||||
@@ -75,6 +76,7 @@ import { SocialAccountsPanel } from "./components/social";
|
||||
import { AutomationPanel } from "./components/automation";
|
||||
import { CommunicationHub } from "./components/communication";
|
||||
import { HiringPipeline } from "./components/hiring";
|
||||
import { SkillsSection } from "./components/skills";
|
||||
import { DocumentCenter, KnowledgeBaseManager, MeetingsManager, SprintManager } from "./components/company-operations";
|
||||
import { ActionButton, ErrorBoundary, ListFrame, ListRow, Modal, Panel, SectionButton, Select, StatusPill, useFocusTrap, useReducedMotion } from "./components/ui";
|
||||
import ToastContainer from "./components/Toast";
|
||||
@@ -94,6 +96,7 @@ const sidebarNavDefs: NavItemDef[] = [
|
||||
{ id: "agents", labelKey: "nav.agents", icon: UsersThree },
|
||||
{ id: "tasks", labelKey: "nav.work", icon: ListChecks },
|
||||
{ id: "communication", labelKey: "nav.communication", icon: ChatCircle },
|
||||
{ id: "skills", labelKey: "nav.skills", icon: PuzzlePiece },
|
||||
{ id: "documents", labelKey: "nav.documents", icon: FileText },
|
||||
{ id: "approvals", labelKey: "nav.approvals", icon: ShieldCheck },
|
||||
{ id: "social", labelKey: "nav.social", icon: Megaphone },
|
||||
@@ -181,6 +184,10 @@ export function App() {
|
||||
store.setSocialAccounts([]);
|
||||
store.setBrowserActions([]);
|
||||
store.setBadges(null);
|
||||
store.setSkills([]);
|
||||
store.setSelectedSkillId(null);
|
||||
store.setSelectedSkillFilePath("SKILL.md");
|
||||
store.setSkillImportModalOpen(false);
|
||||
}, []);
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
@@ -1193,6 +1200,10 @@ export function App() {
|
||||
/>
|
||||
)}
|
||||
|
||||
{section === "skills" && currentCompany ? (
|
||||
<SkillsSection companyId={currentCompany.id} agents={companyAgents} onRefresh={refresh} />
|
||||
) : null}
|
||||
|
||||
{section === "automation" && currentCompany ? (
|
||||
<AutomationPanel
|
||||
key={currentCompany.id}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { AgentRecord } from "@shared/types";
|
||||
|
||||
interface SkillsSectionProps {
|
||||
companyId: string;
|
||||
agents: AgentRecord[];
|
||||
onRefresh: () => void;
|
||||
}
|
||||
|
||||
export function SkillsSection({ companyId: _companyId, agents: _agents, onRefresh: _onRefresh }: SkillsSectionProps) {
|
||||
return (
|
||||
<div className="h-full">
|
||||
<p className="text-[color:var(--muted)]">Skills section placeholder -- Task 2</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { SkillsSection } from "./SkillsSection";
|
||||
@@ -22,6 +22,7 @@ export const en: Record<string, string> = {
|
||||
"nav.costs": "Costs",
|
||||
"nav.activity": "Activity",
|
||||
"nav.social": "Social Media",
|
||||
"nav.skills": "Skills",
|
||||
"nav.connectors": "Connectors",
|
||||
"nav.workspaces": "Workspaces",
|
||||
"nav.settings": "Settings",
|
||||
|
||||
@@ -22,6 +22,7 @@ export const zh: Record<string, string> = {
|
||||
"nav.costs": "成本",
|
||||
"nav.activity": "活动",
|
||||
"nav.social": "社交媒体",
|
||||
"nav.skills": "技能",
|
||||
"nav.connectors": "连接器",
|
||||
"nav.workspaces": "工作区",
|
||||
"nav.settings": "设置",
|
||||
|
||||
@@ -105,6 +105,7 @@ export const createNavigationSlice: StateCreator<
|
||||
else if (section === "connectors") state.selectedConnectorId = entityId;
|
||||
else if (section === "communication")
|
||||
state.selectedCommunicationEntityId = entityId;
|
||||
else if (section === "skills") state.selectedSkillId = entityId;
|
||||
}
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import type { StateCreator } from "zustand";
|
||||
import type { AppState } from "../store";
|
||||
import type { SkillRecord } from "@shared/types";
|
||||
|
||||
export interface SkillSlice {
|
||||
skills: SkillRecord[];
|
||||
setSkills: (skills: SkillRecord[]) => void;
|
||||
selectedSkillId: string | null;
|
||||
setSelectedSkillId: (id: string | null) => void;
|
||||
selectedSkillFilePath: string;
|
||||
setSelectedSkillFilePath: (path: string) => void;
|
||||
skillImportModalOpen: boolean;
|
||||
setSkillImportModalOpen: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export const createSkillSlice: StateCreator<
|
||||
AppState,
|
||||
[["zustand/immer", never]],
|
||||
[],
|
||||
SkillSlice
|
||||
> = (set) => ({
|
||||
skills: [],
|
||||
setSkills: (skills) =>
|
||||
set((state) => {
|
||||
state.skills = skills;
|
||||
}),
|
||||
selectedSkillId: null,
|
||||
setSelectedSkillId: (id) =>
|
||||
set((state) => {
|
||||
state.selectedSkillId = id;
|
||||
}),
|
||||
selectedSkillFilePath: "SKILL.md",
|
||||
setSelectedSkillFilePath: (path) =>
|
||||
set((state) => {
|
||||
state.selectedSkillFilePath = path;
|
||||
}),
|
||||
skillImportModalOpen: false,
|
||||
setSkillImportModalOpen: (open) =>
|
||||
set((state) => {
|
||||
state.skillImportModalOpen = open;
|
||||
}),
|
||||
});
|
||||
@@ -16,13 +16,15 @@ import {
|
||||
type CommunicationSlice,
|
||||
} from "./slices/communication";
|
||||
import { createSocialSlice, type SocialSlice } from "./slices/social";
|
||||
import { createSkillSlice, type SkillSlice } from "./slices/skill";
|
||||
|
||||
export type AppState = NavigationSlice &
|
||||
CompanySlice &
|
||||
SelectionSlice &
|
||||
UiSlice &
|
||||
CommunicationSlice &
|
||||
SocialSlice;
|
||||
SocialSlice &
|
||||
SkillSlice;
|
||||
|
||||
export const useAppStore = create<AppState>()(
|
||||
devtools(
|
||||
@@ -33,6 +35,7 @@ export const useAppStore = create<AppState>()(
|
||||
...createUiSlice(...a),
|
||||
...createCommunicationSlice(...a),
|
||||
...createSocialSlice(...a),
|
||||
...createSkillSlice(...a),
|
||||
})),
|
||||
{
|
||||
name: "AgentCompany",
|
||||
|
||||
Reference in new issue
Block a user