Files
PaperClipAI/ui/src/components/ProjectTile.tsx
T
eaef47f4c7 Information Architecture + project/agent visual refresh (experimental) (#7543)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board UI is the control surface for issues, projects, agents,
goals, workspaces, and operator settings.
> - The existing navigation and list surfaces make several
high-frequency workflows feel harder to scan than they should,
especially around projects and agents.
> - The product direction is to improve those surfaces without breaking
the existing route model or forcing a new IA on every operator at once.
> - This pull request now keeps the dependent IA, project identity, and
agent-list visual refresh work together while the Issue-to-Task copy
migration is split into #7651.
> - The benefit is a clearer left nav, better project identity, denser
agent/project list rows, and brand-aligned status treatment while
preserving the classic default experience behind a flag.

## Linked Issues or Issue Description

Refs #7645
Refs #7651

Internal planning/work references: PAP-53, PAP-56, PAP-58, PAP-59,
PAP-60, PAP-61, PAP-68, PAP-69, PAP-70, PAP-71, PAP-72, PAP-75, PAP-76,
PAP-80, PAP-85, PAP-86, PAP-87, PAP-88, PAP-89.

## What Changed

- Adds `enableStreamlinedLeftNavigation`, defaulting off, and gates
sidebar presentation so classic navigation remains the default.
- Adds project icon persistence, validation, portability, picker UI, and
`ProjectTile` rendering while defaulting new projects to neutral gray.
- Adds projects-list task-count and budget summary data with focused
server/shared/UI coverage.
- Refreshes agent list rows, row actions, active/recent sidebar
behavior, and status capsule/chip styling for the approved brand state
system.
- Removes the placeholder Conference room and Artifacts nav/routes from
the finalized experimental nav direction.
- Removes `pnpm-lock.yaml` and the Issue-to-Task copy migration from
this PR diff; the copy migration now lives in #7651.

## Verification

- Existing branch verification from the authored commits: UI typecheck,
targeted unit tests, and light/dark visual checks for `/agents`, agent
detail, and design-guide status states.
- Maintainer cleanup verification on `75e34e5`: `git diff --check
origin/master...HEAD` passed, the `design/` diff is empty, and the PR
diff is 61 files, below Greptile's 100-file review limit.
- `pnpm --filter @paperclipai/ui build` passed.
- `NODE_ENV=test pnpm exec vitest run
ui/src/components/Sidebar.test.tsx` passed: 1 file, 8 tests.
- CI and Greptile should rerun on the latest push.

## Risks

- Broad UI surface area: the experimental flag keeps the classic nav
default, but changed shared components such as `EntityRow`,
`ProjectTile`, and agent status badges could affect multiple pages.
- Database migration: `projects.icon` is additive and nullable, but
migration ordering and portability import/export must stay aligned.
- The Issue-to-Task copy migration is now separated into #7651, so
reviewers should evaluate this PR as IA/project/agent presentation work
only.
- Visual regressions are possible across smaller widths because the PR
intentionally changes dense list-row layouts.

## Model Used

Claude Opus 4.8 assisted the original feature commits.
Paperclip-Paperclip agents assisted some planning/design commits. Codex
/ GPT-5-class coding agent with shell, GitHub CLI, and repository access
performed this PR-readiness cleanup and split.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Dotta <bippadotta@protonmail.com>
2026-06-06 09:17:27 -05:00

53 lines
1.7 KiB
TypeScript

import { cn } from "../lib/utils";
import { getProjectIcon } from "../lib/project-icons";
/**
* Reusable project tile (IA Phase 3 — PAP-58; icon picker added PAP-68 part 3).
*
* Default render is a neutral gray rounded rectangle with a folder icon.
* An optional `color` tints the background; an optional `icon` selects which
* Lucide glyph to render (defaults to `folder`).
*
* Used by the Projects list rows and the project detail header. Both `color`
* and `icon` live on the project itself (`project.color` / `project.icon`).
*/
export type ProjectTileSize = "xs" | "sm" | "md" | "lg";
const SIZE_STYLES: Record<ProjectTileSize, { box: string; icon: string }> = {
xs: { box: "h-4 w-4 rounded-sm", icon: "h-2.5 w-2.5" },
sm: { box: "h-6 w-6 rounded-md", icon: "h-3.5 w-3.5" },
md: { box: "h-7 w-7 rounded-lg", icon: "h-4 w-4" },
lg: { box: "h-9 w-9 rounded-lg", icon: "h-5 w-5" },
};
export interface ProjectTileProps {
/** Optional project color. When unset, the tile stays neutral gray. */
color?: string | null;
/** Optional Lucide icon name. When unset, defaults to `folder`. */
icon?: string | null;
size?: ProjectTileSize;
className?: string;
}
export function ProjectTile({ color, icon, size = "md", className }: ProjectTileProps) {
const dims = SIZE_STYLES[size];
const tinted = Boolean(color);
const Icon = getProjectIcon(icon);
return (
<span
aria-hidden
className={cn(
"inline-flex shrink-0 items-center justify-center",
dims.box,
tinted ? "text-white" : "bg-muted text-muted-foreground",
className,
)}
style={tinted ? { backgroundColor: color ?? undefined } : undefined}
>
<Icon className={dims.icon} />
</span>
);
}