HiringPipeline carried three panels that restated information already
shown — one with more emphasis — on the Team page or in the stage
filter directly above them:
1. "Pending approval" mini-Panel (lines 121-187): listed the same
candidates that the user reaches by clicking the `pending_approval`
chip in the stage filter at the top of the page. The mini-Panel
also carried its own Approve / Reject buttons, but those approvals
are resolvable from Approvals section too. Doubling the approval
path increases the chance of racing clicks between the two
surfaces. Deleted.
2. 4-card summary row at the bottom (lines 246-267): Total agents /
Pending hires / Departments / Monthly payroll. The first two are
rendered on the Team page by TeamHeaderSummary (commit 36bed1b).
Payroll can return via a Team-page dedicated surface when there
is a business reason; until then it is clutter. Deleted.
3. "Department headcount" Panel (lines 269-293): grid of active+
pending count per department. OrgChart already shows this through
the department colour on each agent card, and TeamHeaderSummary
covers the aggregate. Two places describing the same fact is one
too many. Deleted.
Also:
- `onRefresh` prop removed from HiringPipeline + OrganizationSections
caller — only the (now-deleted) approve/reject buttons used it.
- `Card`, `UserPlus`, `Check`, `unwrap` imports dropped because the
deletions made them unused.
- Unused `i` index in Dashboard.tsx activeWork map callback cleaned
up (left over from the previous commit that removed the
arbitrary animation gate).
File size: HiringPipeline.tsx 296 LOC → 175 LOC (net −121).
Typecheck + lint pass.
174 lines
7.7 KiB
TypeScript
174 lines
7.7 KiB
TypeScript
import type { SectionId } from "@shared/types";
|
|
import { NEW_ENTITY_ID } from "../../lib/constants";
|
|
import {
|
|
Target,
|
|
UserPlus,
|
|
Kanban,
|
|
} from "../../components/ui/icons";
|
|
import { AgentCreator } from "../../components/organization/AgentCreator";
|
|
import { AgentEditor } from "../../components/organization/AgentEditor";
|
|
import { AgentRoster } from "../../components/organization/AgentRoster";
|
|
import { GoalCreator } from "../../components/organization/GoalCreator";
|
|
import { GoalEditor } from "../../components/organization/GoalEditor";
|
|
import { GoalList } from "../../components/organization/GoalList";
|
|
import { OrgChart } from "../../components/organization/OrgChart";
|
|
import { TeamHeaderSummary } from "../../components/organization/TeamHeaderSummary";
|
|
import { ProjectCreator } from "../../components/organization/ProjectCreator";
|
|
import { ProjectEditor } from "../../components/organization/ProjectEditor";
|
|
import { ProjectList } from "../../components/organization/ProjectList";
|
|
import { HiringPipeline } from "../../components/hiring/HiringPipeline";
|
|
import { ActionButton, Modal, Panel } from "../../components/ui";
|
|
import { useT } from "../../i18n";
|
|
import { useAppStore } from "../../state/store";
|
|
import { useSectionContext } from "../../hooks/useSectionContext";
|
|
import { useAppActions } from "../../hooks/useAppActions";
|
|
|
|
interface OrganizationSectionsProps {
|
|
section: SectionId;
|
|
}
|
|
|
|
export function OrganizationSections({ section }: OrganizationSectionsProps) {
|
|
const t = useT();
|
|
const {
|
|
snapshot,
|
|
currentCompany,
|
|
companyAgents,
|
|
companyGoals,
|
|
companyProjects,
|
|
companyWorkspaces,
|
|
pendingApprovals,
|
|
editingAgent,
|
|
selectedGoal,
|
|
selectedProject,
|
|
} = useSectionContext();
|
|
const { refresh } = useAppActions();
|
|
|
|
const selectedAgentId = useAppStore((s) => s.selectedAgentId);
|
|
const setSelectedAgentId = useAppStore((s) => s.setSelectedAgentId);
|
|
const editingAgentId = useAppStore((s) => s.editingAgentId);
|
|
const setEditingAgentId = useAppStore((s) => s.setEditingAgentId);
|
|
const selectedGoalId = useAppStore((s) => s.selectedGoalId);
|
|
const setSelectedGoalId = useAppStore((s) => s.setSelectedGoalId);
|
|
const selectedProjectId = useAppStore((s) => s.selectedProjectId);
|
|
const setSelectedProjectId = useAppStore((s) => s.setSelectedProjectId);
|
|
const setSection = useAppStore((s) => s.setSection);
|
|
|
|
// Open the Edit modal on row / card click. `selectedAgentId` is updated
|
|
// alongside so the roster highlight follows the user's last interaction
|
|
// even after the modal closes.
|
|
const openAgentEditor = (id: string) => {
|
|
setSelectedAgentId(id);
|
|
setEditingAgentId(id);
|
|
};
|
|
|
|
if (!snapshot || !currentCompany) return null;
|
|
|
|
return (
|
|
<>
|
|
{section === "agents" ? (
|
|
<>
|
|
<TeamHeaderSummary agents={companyAgents} pendingApprovals={pendingApprovals} />
|
|
<OrgChart agents={companyAgents} connectors={snapshot.connectors} onSelectAgent={openAgentEditor} />
|
|
<Panel title={t("org.agentRoster")} action={<ActionButton label={t("org.newHireRequest")} icon={UserPlus} tone="accent" onClick={() => setEditingAgentId(NEW_ENTITY_ID)} />}>
|
|
<AgentRoster
|
|
agents={companyAgents}
|
|
connectors={snapshot.connectors}
|
|
workspaces={companyWorkspaces}
|
|
selectedAgentId={selectedAgentId}
|
|
onSelect={openAgentEditor}
|
|
/>
|
|
</Panel>
|
|
<Modal open={editingAgentId === NEW_ENTITY_ID} title={t("org.requestNewHire")} onClose={() => setEditingAgentId(null)}>
|
|
<AgentCreator
|
|
companyId={currentCompany.id}
|
|
connectors={snapshot.connectors}
|
|
workspaces={companyWorkspaces}
|
|
agents={companyAgents}
|
|
onSaved={async () => {
|
|
setEditingAgentId(null);
|
|
setSection("hiring");
|
|
await refresh();
|
|
}}
|
|
/>
|
|
</Modal>
|
|
<Modal
|
|
open={!!editingAgent}
|
|
title={editingAgent ? t("org.editAgentTitle").replace("{name}", editingAgent.name).replace("{role}", editingAgent.role) : ""}
|
|
onClose={() => setEditingAgentId(null)}
|
|
>
|
|
{editingAgent ? (
|
|
<AgentEditor
|
|
key={editingAgent.id}
|
|
agent={editingAgent}
|
|
connectors={snapshot.connectors}
|
|
workspaces={companyWorkspaces}
|
|
agents={companyAgents}
|
|
companyId={currentCompany.id}
|
|
onSaved={refresh}
|
|
onCancel={() => setEditingAgentId(null)}
|
|
onDeleted={async () => { setEditingAgentId(null); await refresh(); }}
|
|
/>
|
|
) : null}
|
|
</Modal>
|
|
</>
|
|
) : null}
|
|
|
|
{section === "hiring" && currentCompany ? (
|
|
<HiringPipeline
|
|
agents={companyAgents}
|
|
approvals={(snapshot?.approvals ?? []).filter(a => a.companyId === currentCompany.id)}
|
|
onNavigateToAgent={(id) => { setSelectedAgentId(id); setSection("agents"); }}
|
|
/>
|
|
) : null}
|
|
|
|
{section === "goals" ? (
|
|
<>
|
|
<Panel title={t("org.goalStack")} action={<ActionButton label={t("org.newGoal")} icon={Target} tone="accent" onClick={() => setSelectedGoalId(NEW_ENTITY_ID)} />}>
|
|
<GoalList goals={companyGoals} agents={companyAgents} selectedGoalId={selectedGoalId} onSelect={setSelectedGoalId} />
|
|
</Panel>
|
|
<Modal open={selectedGoalId === NEW_ENTITY_ID} title={t("org.newGoal")} onClose={() => setSelectedGoalId(null)}>
|
|
<GoalCreator companyId={currentCompany.id} agents={companyAgents} goals={companyGoals} onSaved={async () => { setSelectedGoalId(null); await refresh(); }} />
|
|
</Modal>
|
|
<Modal open={!!selectedGoal} title={selectedGoal ? `Edit: ${selectedGoal.title}` : ""} onClose={() => setSelectedGoalId(null)}>
|
|
{selectedGoal ? (
|
|
<GoalEditor
|
|
key={selectedGoalId}
|
|
goal={selectedGoal}
|
|
goals={companyGoals}
|
|
agents={companyAgents}
|
|
companyId={currentCompany.id}
|
|
onSaved={async () => { setSelectedGoalId(null); await refresh(); }}
|
|
onDeleted={async () => { setSelectedGoalId(null); await refresh(); }}
|
|
/>
|
|
) : null}
|
|
</Modal>
|
|
</>
|
|
) : null}
|
|
|
|
{section === "projects" ? (
|
|
<>
|
|
<Panel title={t("nav.projects")} action={<ActionButton label={t("org.newProject")} icon={Kanban} tone="accent" onClick={() => setSelectedProjectId(NEW_ENTITY_ID)} />}>
|
|
<ProjectList projects={companyProjects} goals={companyGoals} agents={companyAgents} workspaces={companyWorkspaces} selectedProjectId={selectedProjectId} onSelect={setSelectedProjectId} />
|
|
</Panel>
|
|
<Modal open={selectedProjectId === NEW_ENTITY_ID} title={t("org.newProject")} onClose={() => setSelectedProjectId(null)}>
|
|
<ProjectCreator companyId={currentCompany.id} goals={companyGoals} agents={companyAgents} onSaved={async () => { setSelectedProjectId(null); await refresh(); }} />
|
|
</Modal>
|
|
<Modal open={!!selectedProject} title={selectedProject ? `Edit: ${selectedProject.name}` : ""} onClose={() => setSelectedProjectId(null)}>
|
|
{selectedProject ? (
|
|
<ProjectEditor
|
|
key={selectedProjectId}
|
|
project={selectedProject}
|
|
goals={companyGoals}
|
|
agents={companyAgents}
|
|
companyId={currentCompany.id}
|
|
onSaved={async () => { setSelectedProjectId(null); await refresh(); }}
|
|
onDeleted={async () => { setSelectedProjectId(null); await refresh(); }}
|
|
/>
|
|
) : null}
|
|
</Modal>
|
|
</>
|
|
) : null}
|
|
</>
|
|
);
|
|
}
|