Files
AgentCompany/src/renderer/components/dashboard/GoalProgress.tsx
T
ZacharyZhang-NY 16d10c0814 feat(01-03): decompose organization.tsx and dashboard.tsx into focused sub-components
- Split organization.tsx (1395 lines) into 12 files: AgentRoster, AgentCreator, AgentEditor, GoalList, GoalCreator, GoalEditor, ProjectList, ProjectCreator, ProjectEditor, OrgChart, helpers, orgTreeLayout
- Split dashboard.tsx (1053 lines) into 11 files: Dashboard, HealthCard, MetricsGrid, ActivityFeed, QuickActions, DepartmentOverview, TaskPipeline, SprintProgress, GoalProgress, KanbanBoard, NeedsAttention
- All sub-component files under 300 lines (max: 239 lines)
- Original files replaced with backward-compatible re-exports
- TypeScript compilation passes
2026-03-22 01:54:20 -04:00

48 lines
2.1 KiB
TypeScript

import { useMemo } from "react";
import { motion } from "framer-motion";
import { Target } from "@phosphor-icons/react";
import type { GoalRecord, SectionId, TaskRecord } from "@shared/types";
export function GoalProgress({ goals, tasks, onNavigate }: { goals: GoalRecord[]; tasks: TaskRecord[]; onNavigate: (section: SectionId) => void }) {
const activeGoals = useMemo(() => goals.filter((g) => g.status === "active"), [goals]);
if (activeGoals.length === 0) return null;
return (
<div className="space-y-2">
{activeGoals.slice(0, 5).map((goal) => {
const goalTasks = tasks.filter((t) => t.goalId === goal.id);
const done = goalTasks.filter((t) => t.status === "done").length;
const total = goalTasks.length;
const pct = total === 0 ? 0 : Math.round((done / total) * 100);
return (
<button
key={goal.id}
type="button"
onClick={() => onNavigate("goals")}
className="focus-ring w-full rounded-[8px] border border-[color:var(--line)] bg-[color:var(--panel)] px-4 py-3 text-left transition hover:border-[color:var(--line-strong)]"
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Target size={14} className="text-[color:var(--accent)]" />
<span className="text-[12px] font-medium text-[color:var(--text)]">{goal.title}</span>
</div>
<span className="text-[11px] font-bold text-[color:var(--accent)]">{pct}%</span>
</div>
<div className="mt-2 h-1.5 w-full overflow-hidden rounded-full bg-[color:var(--panel-soft)]">
<motion.div
className="h-full rounded-full bg-[color:var(--accent)]"
initial={{ width: 0 }}
animate={{ width: `${pct}%` }}
transition={{ duration: 0.8, ease: "easeOut" }}
/>
</div>
<div className="mt-1 text-[10px] text-[color:var(--muted)]">{done}/{total} tasks complete</div>
</button>
);
})}
</div>
);
}