PAP-71: default new projects to neutral gray (remove auto/random color)

Part 2 of PAP-68. Stop auto-assigning a project color on create so new
projects persist color = null (and icon = null) and render the neutral
gray-on-gray tile in the list and sidebar via ProjectTile.

- server: remove the auto-color palette block in projectService.create;
  color now stays null unless explicitly supplied. Drop the now-unused
  PROJECT_COLORS import.
- ui: NewProjectDialog no longer sends a random PROJECT_COLORS value on
  create. Drop the now-unused import.
- test: assert create defaults color to null (no auto-assign) and still
  persists an explicit color when supplied.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
scotttongandClaude Opus 4.8 committed 2026-06-05 15:18:54 -07:00
1 parent f5e6d346e6
commit d8d3918b0e
3 files changed
+27 -10

No files matched your search

@@ -68,4 +68,28 @@ describeEmbeddedPostgres("project icon persistence", () => {
const fetched = await projects.getById(created.id);
expect(fetched?.icon).toBeNull();
});
// PAP-71: new projects must NOT auto-assign a color — they stay neutral gray
// (color = null) unless an explicit color is supplied on create.
it("defaults color to null when none is provided (no auto-assign)", async () => {
companyId = await seedCompany();
const projects = projectService(db);
const created = await projects.create(companyId, { name: "Gray" });
expect(created.color).toBeNull();
const fetched = await projects.getById(created.id);
expect(fetched?.color).toBeNull();
});
it("still persists an explicit color when one is supplied", async () => {
companyId = await seedCompany();
const projects = projectService(db);
const created = await projects.create(companyId, { name: "Blue", color: "#3b82f6" });
expect(created.color).toBe("#3b82f6");
const fetched = await projects.getById(created.id);
expect(fetched?.color).toBe("#3b82f6");
});
});
+2 -8
View File
@@ -12,7 +12,6 @@ import {
workspaceRuntimeServices,
} from "@paperclipai/db";
import {
PROJECT_COLORS,
deriveProjectUrlKey,
hasNonAsciiContent,
isUuidLike,
@@ -549,13 +548,8 @@ export function projectService(db: Db) {
const { goalIds: inputGoalIds, ...projectData } = data;
const ids = resolveGoalIds({ goalIds: inputGoalIds, goalId: projectData.goalId });
// Auto-assign a color from the palette if none provided
if (!projectData.color) {
const existing = await db.select({ color: projects.color }).from(projects).where(eq(projects.companyId, companyId));
const usedColors = new Set(existing.map((r) => r.color).filter(Boolean));
const nextColor = PROJECT_COLORS.find((c) => !usedColors.has(c)) ?? PROJECT_COLORS[existing.length % PROJECT_COLORS.length];
projectData.color = nextColor;
}
// Note: color is intentionally NOT auto-assigned. New projects default to
// `color = null` (neutral gray) unless an explicit color is supplied. See PAP-68.
const existingProjects = await db
.select({ id: projects.id, name: projects.name })
+1 -2
View File
@@ -33,7 +33,6 @@ import {
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { PROJECT_COLORS } from "@paperclipai/shared";
import { cn } from "../lib/utils";
import { MarkdownEditor, type MarkdownEditorRef, type MentionOption } from "./MarkdownEditor";
import { StatusBadge } from "./StatusBadge";
@@ -165,7 +164,7 @@ export function NewProjectDialog() {
name: name.trim(),
description: description.trim() || undefined,
status,
color: PROJECT_COLORS[Math.floor(Math.random() * PROJECT_COLORS.length)],
// No color is sent — new projects persist color = null (neutral gray). See PAP-68.
...(goalIds.length > 0 ? { goalIds } : {}),
...(targetDate ? { targetDate } : {}),
});