nexus/ui/src/pages/Projects.tsx
Mikkel Georgsen e4ddb2453a feat(06-02): replace Select a company empty states + CLI Paperclip strings
- 14 UI pages: all Select a company empty states use VOCAB.company.toLowerCase()
- AgentConfigForm: 3 error throws use VOCAB.company
- AgentDetail: additional Select a company upload error replaced
- CLI run.ts: Starting/Could not locate/failed to start messages use VOCAB.appName
- CLI deployment-auth-check: repairHint uses VOCAB.appName
- CLI agent-jwt-secret-check: repairHint uses VOCAB.appName
- CLI allowed-hostname: restart message uses VOCAB.appName
- Added VOCAB import to all files missing it
2026-04-02 15:08:37 +00:00

88 lines
2.8 KiB
TypeScript

import { useEffect, useMemo } from "react";
import { VOCAB } from "@paperclipai/branding";
import { useQuery } from "@tanstack/react-query";
import { projectsApi } from "../api/projects";
import { useCompany } from "../context/CompanyContext";
import { useDialog } from "../context/DialogContext";
import { useBreadcrumbs } from "../context/BreadcrumbContext";
import { queryKeys } from "../lib/queryKeys";
import { EntityRow } from "../components/EntityRow";
import { StatusBadge } from "../components/StatusBadge";
import { EmptyState } from "../components/EmptyState";
import { PageSkeleton } from "../components/PageSkeleton";
import { formatDate, projectUrl } from "../lib/utils";
import { Button } from "@/components/ui/button";
import { Hexagon, Plus } from "lucide-react";
export function Projects() {
const { selectedCompanyId } = useCompany();
const { openNewProject } = useDialog();
const { setBreadcrumbs } = useBreadcrumbs();
useEffect(() => {
setBreadcrumbs([{ label: "Projects" }]);
}, [setBreadcrumbs]);
const { data: allProjects, isLoading, error } = useQuery({
queryKey: queryKeys.projects.list(selectedCompanyId!),
queryFn: () => projectsApi.list(selectedCompanyId!),
enabled: !!selectedCompanyId,
});
const projects = useMemo(
() => (allProjects ?? []).filter((p) => !p.archivedAt),
[allProjects],
);
if (!selectedCompanyId) {
return <EmptyState icon={Hexagon} message={`Select a ${VOCAB.company.toLowerCase()} to view projects.`} />;
}
if (isLoading) {
return <PageSkeleton variant="list" />;
}
return (
<div className="space-y-4">
<div className="flex items-center justify-end">
<Button size="sm" variant="outline" onClick={openNewProject}>
<Plus className="h-4 w-4 mr-1" />
Add Project
</Button>
</div>
{error && <p className="text-sm text-destructive">{error.message}</p>}
{!isLoading && projects.length === 0 && (
<EmptyState
icon={Hexagon}
message="No projects yet."
action="Add Project"
onAction={openNewProject}
/>
)}
{projects.length > 0 && (
<div className="border border-border">
{projects.map((project) => (
<EntityRow
key={project.id}
title={project.name}
subtitle={project.description ?? undefined}
to={projectUrl(project)}
trailing={
<div className="flex items-center gap-3">
{project.targetDate && (
<span className="text-xs text-muted-foreground">
{formatDate(project.targetDate)}
</span>
)}
<StatusBadge status={project.status} />
</div>
}
/>
))}
</div>
)}
</div>
);
}