feat(ui): make project status clickable in properties pane
Add a ProjectStatusPicker component that renders the status badge as a clickable button opening a popover with all project statuses. Falls back to the read-only StatusBadge when no onUpdate handler is provided. Works in both desktop side panel and mobile sheet layout. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,11 +3,12 @@ import { Link } from "@/lib/router";
|
|||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import type { Project } from "@paperclipai/shared";
|
import type { Project } from "@paperclipai/shared";
|
||||||
import { StatusBadge } from "./StatusBadge";
|
import { StatusBadge } from "./StatusBadge";
|
||||||
import { formatDate } from "../lib/utils";
|
import { cn, formatDate } from "../lib/utils";
|
||||||
import { goalsApi } from "../api/goals";
|
import { goalsApi } from "../api/goals";
|
||||||
import { projectsApi } from "../api/projects";
|
import { projectsApi } from "../api/projects";
|
||||||
import { useCompany } from "../context/CompanyContext";
|
import { useCompany } from "../context/CompanyContext";
|
||||||
import { queryKeys } from "../lib/queryKeys";
|
import { queryKeys } from "../lib/queryKeys";
|
||||||
|
import { statusBadge, statusBadgeDefault } from "../lib/status-colors";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
@@ -15,6 +16,14 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip
|
|||||||
import { ExternalLink, Github, Plus, Trash2, X } from "lucide-react";
|
import { ExternalLink, Github, Plus, Trash2, X } from "lucide-react";
|
||||||
import { ChoosePathButton } from "./PathInstructionsModal";
|
import { ChoosePathButton } from "./PathInstructionsModal";
|
||||||
|
|
||||||
|
const PROJECT_STATUSES = [
|
||||||
|
{ value: "backlog", label: "Backlog" },
|
||||||
|
{ value: "planned", label: "Planned" },
|
||||||
|
{ value: "in_progress", label: "In Progress" },
|
||||||
|
{ value: "completed", label: "Completed" },
|
||||||
|
{ value: "cancelled", label: "Cancelled" },
|
||||||
|
];
|
||||||
|
|
||||||
interface ProjectPropertiesProps {
|
interface ProjectPropertiesProps {
|
||||||
project: Project;
|
project: Project;
|
||||||
onUpdate?: (data: Record<string, unknown>) => void;
|
onUpdate?: (data: Record<string, unknown>) => void;
|
||||||
@@ -31,6 +40,42 @@ function PropertyRow({ label, children }: { label: string; children: React.React
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ProjectStatusPicker({ status, onChange }: { status: string; onChange: (status: string) => void }) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const colorClass = statusBadge[status] ?? statusBadgeDefault;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<button
|
||||||
|
className={cn(
|
||||||
|
"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium whitespace-nowrap shrink-0 cursor-pointer hover:opacity-80 transition-opacity",
|
||||||
|
colorClass,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{status.replace("_", " ")}
|
||||||
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-40 p-1" align="start">
|
||||||
|
{PROJECT_STATUSES.map((s) => (
|
||||||
|
<Button
|
||||||
|
key={s.value}
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className={cn("w-full justify-start gap-2 text-xs", s.value === status && "bg-accent")}
|
||||||
|
onClick={() => {
|
||||||
|
onChange(s.value);
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{s.label}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function ProjectProperties({ project, onUpdate }: ProjectPropertiesProps) {
|
export function ProjectProperties({ project, onUpdate }: ProjectPropertiesProps) {
|
||||||
const { selectedCompanyId } = useCompany();
|
const { selectedCompanyId } = useCompany();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
@@ -212,7 +257,14 @@ export function ProjectProperties({ project, onUpdate }: ProjectPropertiesProps)
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<PropertyRow label="Status">
|
<PropertyRow label="Status">
|
||||||
<StatusBadge status={project.status} />
|
{onUpdate ? (
|
||||||
|
<ProjectStatusPicker
|
||||||
|
status={project.status}
|
||||||
|
onChange={(status) => onUpdate({ status })}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<StatusBadge status={project.status} />
|
||||||
|
)}
|
||||||
</PropertyRow>
|
</PropertyRow>
|
||||||
{project.leadAgentId && (
|
{project.leadAgentId && (
|
||||||
<PropertyRow label="Lead">
|
<PropertyRow label="Lead">
|
||||||
|
|||||||
Reference in New Issue
Block a user