import { useState } from "react"; import { Link } from "react-router-dom"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import type { Project } from "@paperclip/shared"; import { StatusBadge } from "./StatusBadge"; import { formatDate } from "../lib/utils"; import { goalsApi } from "../api/goals"; import { projectsApi } from "../api/projects"; import { useCompany } from "../context/CompanyContext"; import { queryKeys } from "../lib/queryKeys"; import { Separator } from "@/components/ui/separator"; import { Button } from "@/components/ui/button"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Plus, Star, Trash2, X } from "lucide-react"; interface ProjectPropertiesProps { project: Project; onUpdate?: (data: Record) => void; } function PropertyRow({ label, children }: { label: string; children: React.ReactNode }) { return (
{label}
{children}
); } export function ProjectProperties({ project, onUpdate }: ProjectPropertiesProps) { const { selectedCompanyId } = useCompany(); const queryClient = useQueryClient(); const [goalOpen, setGoalOpen] = useState(false); const [workspaceName, setWorkspaceName] = useState(""); const [workspaceCwd, setWorkspaceCwd] = useState(""); const [workspaceRepoUrl, setWorkspaceRepoUrl] = useState(""); const { data: allGoals } = useQuery({ queryKey: queryKeys.goals.list(selectedCompanyId!), queryFn: () => goalsApi.list(selectedCompanyId!), enabled: !!selectedCompanyId, }); const linkedGoalIds = project.goalIds.length > 0 ? project.goalIds : project.goalId ? [project.goalId] : []; const linkedGoals = project.goals.length > 0 ? project.goals : linkedGoalIds.map((id) => ({ id, title: allGoals?.find((g) => g.id === id)?.title ?? id.slice(0, 8), })); const availableGoals = (allGoals ?? []).filter((g) => !linkedGoalIds.includes(g.id)); const workspaces = project.workspaces ?? []; const invalidateProject = () => { queryClient.invalidateQueries({ queryKey: queryKeys.projects.detail(project.id) }); if (selectedCompanyId) { queryClient.invalidateQueries({ queryKey: queryKeys.projects.list(selectedCompanyId) }); } }; const createWorkspace = useMutation({ mutationFn: (data: Record) => projectsApi.createWorkspace(project.id, data), onSuccess: () => { setWorkspaceName(""); setWorkspaceCwd(""); setWorkspaceRepoUrl(""); invalidateProject(); }, }); const updateWorkspace = useMutation({ mutationFn: (input: { workspaceId: string; data: Record }) => projectsApi.updateWorkspace(project.id, input.workspaceId, input.data), onSuccess: invalidateProject, }); const removeWorkspace = useMutation({ mutationFn: (workspaceId: string) => projectsApi.removeWorkspace(project.id, workspaceId), onSuccess: invalidateProject, }); const removeGoal = (goalId: string) => { if (!onUpdate) return; onUpdate({ goalIds: linkedGoalIds.filter((id) => id !== goalId) }); }; const addGoal = (goalId: string) => { if (!onUpdate || linkedGoalIds.includes(goalId)) return; onUpdate({ goalIds: [...linkedGoalIds, goalId] }); setGoalOpen(false); }; const submitWorkspace = () => { if (!workspaceName.trim() || !workspaceCwd.trim()) return; createWorkspace.mutate({ name: workspaceName.trim(), cwd: workspaceCwd.trim(), repoUrl: workspaceRepoUrl.trim() || null, isPrimary: workspaces.length === 0, }); }; return (
{project.leadAgentId && ( {project.leadAgentId.slice(0, 8)} )}
Goals
{linkedGoals.length === 0 ? ( None ) : (
{linkedGoals.map((goal) => ( {goal.title} {onUpdate && ( )} ))}
)} {onUpdate && ( {availableGoals.length === 0 ? (
All goals linked.
) : ( availableGoals.map((goal) => ( )) )}
)}
{project.targetDate && ( {formatDate(project.targetDate)} )}
Workspaces
{workspaces.length === 0 ? (

No project workspaces configured.

) : (
{workspaces.map((workspace) => (
{workspace.name}
{workspace.isPrimary ? ( Primary ) : ( )}

{workspace.cwd}

{workspace.repoUrl && (

{workspace.repoUrl}

)}
))}
)}
setWorkspaceName(e.target.value)} placeholder="Workspace name" /> setWorkspaceCwd(e.target.value)} placeholder="/absolute/path/to/workspace" /> setWorkspaceRepoUrl(e.target.value)} placeholder="Repo URL (optional)" />
{formatDate(project.createdAt)} {formatDate(project.updatedAt)}
); }