import { useEffect } from "react"; import { useParams } from "react-router-dom"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { goalsApi } from "../api/goals"; import { projectsApi } from "../api/projects"; import { assetsApi } from "../api/assets"; import { usePanel } from "../context/PanelContext"; import { useCompany } from "../context/CompanyContext"; import { useDialog } from "../context/DialogContext"; import { useBreadcrumbs } from "../context/BreadcrumbContext"; import { queryKeys } from "../lib/queryKeys"; import { GoalProperties } from "../components/GoalProperties"; import { GoalTree } from "../components/GoalTree"; import { StatusBadge } from "../components/StatusBadge"; import { InlineEditor } from "../components/InlineEditor"; import { EntityRow } from "../components/EntityRow"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Plus } from "lucide-react"; import type { Goal, Project } from "@paperclip/shared"; export function GoalDetail() { const { goalId } = useParams<{ goalId: string }>(); const { selectedCompanyId } = useCompany(); const { openNewGoal } = useDialog(); const { openPanel, closePanel } = usePanel(); const { setBreadcrumbs } = useBreadcrumbs(); const queryClient = useQueryClient(); const { data: goal, isLoading, error } = useQuery({ queryKey: queryKeys.goals.detail(goalId!), queryFn: () => goalsApi.get(goalId!), enabled: !!goalId }); const { data: allGoals } = useQuery({ queryKey: queryKeys.goals.list(selectedCompanyId!), queryFn: () => goalsApi.list(selectedCompanyId!), enabled: !!selectedCompanyId }); const { data: allProjects } = useQuery({ queryKey: queryKeys.projects.list(selectedCompanyId!), queryFn: () => projectsApi.list(selectedCompanyId!), enabled: !!selectedCompanyId }); const updateGoal = useMutation({ mutationFn: (data: Record) => goalsApi.update(goalId!, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.goals.detail(goalId!) }); if (selectedCompanyId) { queryClient.invalidateQueries({ queryKey: queryKeys.goals.list(selectedCompanyId) }); } } }); const uploadImage = useMutation({ mutationFn: async (file: File) => { if (!selectedCompanyId) throw new Error("No company selected"); return assetsApi.uploadImage( selectedCompanyId, file, `goals/${goalId ?? "draft"}` ); } }); const childGoals = (allGoals ?? []).filter((g) => g.parentId === goalId); const linkedProjects = (allProjects ?? []).filter((p) => { if (!goalId) return false; if (p.goalIds.includes(goalId)) return true; if (p.goals.some((goalRef) => goalRef.id === goalId)) return true; return p.goalId === goalId; }); useEffect(() => { setBreadcrumbs([ { label: "Goals", href: "/goals" }, { label: goal?.title ?? goalId ?? "Goal" } ]); }, [setBreadcrumbs, goal, goalId]); useEffect(() => { if (goal) { openPanel( updateGoal.mutate(data)} /> ); } return () => closePanel(); }, [goal]); // eslint-disable-line react-hooks/exhaustive-deps if (isLoading) return

Loading...

; if (error) return

{error.message}

; if (!goal) return null; return (
{goal.level}
updateGoal.mutate({ title })} as="h2" className="text-xl font-bold" /> updateGoal.mutate({ description })} as="p" className="text-sm text-muted-foreground" placeholder="Add a description..." multiline imageUploadHandler={async (file) => { const asset = await uploadImage.mutateAsync(file); return asset.contentPath; }} />
Sub-Goals ({childGoals.length}) Projects ({linkedProjects.length})
{childGoals.length === 0 ? (

No sub-goals.

) : ( `/goals/${g.id}`} /> )}
{linkedProjects.length === 0 ? (

No linked projects.

) : (
{linkedProjects.map((project) => ( } /> ))}
)}
); }