feat: foldable PROJECTS section in sidebar with color support

- Add `color` (text) and `archivedAt` (timestamp) columns to projects table
- Add PROJECT_COLORS palette constant (10 colors) in shared package
- Add color/archivedAt to Project type interface and Zod validators
- Auto-assign next available color from palette on project creation
- New SidebarProjects component with:
  - Collapsible PROJECTS header above WORK section
  - Caret toggle visible on hover (left of header)
  - Always-visible plus button (right of header) opens NewProjectDialog
  - Lists non-archived projects with colored rounded squares
  - Active project highlighted based on URL match
- Remove Projects nav item from WORK section in sidebar

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Forgotten
2026-02-23 09:14:08 -06:00
parent 3392f4dbfa
commit a57d3427f7
10 changed files with 224 additions and 10 deletions

View File

@@ -1,6 +1,15 @@
export const COMPANY_STATUSES = ["active", "paused", "archived"] as const;
export type CompanyStatus = (typeof COMPANY_STATUSES)[number];
export const DEPLOYMENT_MODES = ["local_trusted", "authenticated"] as const;
export type DeploymentMode = (typeof DEPLOYMENT_MODES)[number];
export const DEPLOYMENT_EXPOSURES = ["private", "public"] as const;
export type DeploymentExposure = (typeof DEPLOYMENT_EXPOSURES)[number];
export const AUTH_BASE_URL_MODES = ["auto", "explicit"] as const;
export type AuthBaseUrlMode = (typeof AUTH_BASE_URL_MODES)[number];
export const AGENT_STATUSES = [
"active",
"paused",
@@ -59,6 +68,19 @@ export const PROJECT_STATUSES = [
] as const;
export type ProjectStatus = (typeof PROJECT_STATUSES)[number];
export const PROJECT_COLORS = [
"#6366f1", // indigo
"#8b5cf6", // violet
"#ec4899", // pink
"#ef4444", // red
"#f97316", // orange
"#eab308", // yellow
"#22c55e", // green
"#14b8a6", // teal
"#06b6d4", // cyan
"#3b82f6", // blue
] as const;
export const APPROVAL_TYPES = ["hire_agent", "approve_ceo_strategy"] as const;
export type ApprovalType = (typeof APPROVAL_TYPES)[number];
@@ -124,3 +146,34 @@ export const LIVE_EVENT_TYPES = [
"activity.logged",
] as const;
export type LiveEventType = (typeof LIVE_EVENT_TYPES)[number];
export const PRINCIPAL_TYPES = ["user", "agent"] as const;
export type PrincipalType = (typeof PRINCIPAL_TYPES)[number];
export const MEMBERSHIP_STATUSES = ["pending", "active", "suspended"] as const;
export type MembershipStatus = (typeof MEMBERSHIP_STATUSES)[number];
export const INSTANCE_USER_ROLES = ["instance_admin"] as const;
export type InstanceUserRole = (typeof INSTANCE_USER_ROLES)[number];
export const INVITE_TYPES = ["company_join", "bootstrap_ceo"] as const;
export type InviteType = (typeof INVITE_TYPES)[number];
export const INVITE_JOIN_TYPES = ["human", "agent", "both"] as const;
export type InviteJoinType = (typeof INVITE_JOIN_TYPES)[number];
export const JOIN_REQUEST_TYPES = ["human", "agent"] as const;
export type JoinRequestType = (typeof JOIN_REQUEST_TYPES)[number];
export const JOIN_REQUEST_STATUSES = ["pending_approval", "approved", "rejected"] as const;
export type JoinRequestStatus = (typeof JOIN_REQUEST_STATUSES)[number];
export const PERMISSION_KEYS = [
"agents:create",
"users:invite",
"users:manage_permissions",
"tasks:assign",
"tasks:assign_scope",
"joins:approve",
] as const;
export type PermissionKey = (typeof PERMISSION_KEYS)[number];

View File

@@ -1,5 +1,8 @@
export {
COMPANY_STATUSES,
DEPLOYMENT_MODES,
DEPLOYMENT_EXPOSURES,
AUTH_BASE_URL_MODES,
AGENT_STATUSES,
AGENT_ADAPTER_TYPES,
AGENT_ROLES,
@@ -8,6 +11,7 @@ export {
GOAL_LEVELS,
GOAL_STATUSES,
PROJECT_STATUSES,
PROJECT_COLORS,
APPROVAL_TYPES,
APPROVAL_STATUSES,
SECRET_PROVIDERS,
@@ -17,7 +21,18 @@ export {
WAKEUP_TRIGGER_DETAILS,
WAKEUP_REQUEST_STATUSES,
LIVE_EVENT_TYPES,
PRINCIPAL_TYPES,
MEMBERSHIP_STATUSES,
INSTANCE_USER_ROLES,
INVITE_TYPES,
INVITE_JOIN_TYPES,
JOIN_REQUEST_TYPES,
JOIN_REQUEST_STATUSES,
PERMISSION_KEYS,
type CompanyStatus,
type DeploymentMode,
type DeploymentExposure,
type AuthBaseUrlMode,
type AgentStatus,
type AgentAdapterType,
type AgentRole,
@@ -35,6 +50,14 @@ export {
type WakeupTriggerDetail,
type WakeupRequestStatus,
type LiveEventType,
type PrincipalType,
type MembershipStatus,
type InstanceUserRole,
type InviteType,
type InviteJoinType,
type JoinRequestType,
type JoinRequestStatus,
type PermissionKey,
} from "./constants.js";
export type {
@@ -68,6 +91,11 @@ export type {
DashboardSummary,
ActivityEvent,
SidebarBadges,
CompanyMembership,
PrincipalPermissionGrant,
Invite,
JoinRequest,
InstanceUserRoleGrant,
EnvBinding,
AgentEnvConfig,
CompanySecret,
@@ -139,9 +167,19 @@ export {
createCostEventSchema,
updateBudgetSchema,
createAssetImageMetadataSchema,
createCompanyInviteSchema,
acceptInviteSchema,
listJoinRequestsQuerySchema,
updateMemberPermissionsSchema,
updateUserCompanyAccessSchema,
type CreateCostEvent,
type UpdateBudget,
type CreateAssetImageMetadata,
type CreateCompanyInvite,
type AcceptInvite,
type ListJoinRequestsQuery,
type UpdateMemberPermissions,
type UpdateUserCompanyAccess,
} from "./validators/index.js";
export { API_PREFIX, API } from "./api.js";
@@ -153,6 +191,7 @@ export {
databaseConfigSchema,
loggingConfigSchema,
serverConfigSchema,
authConfigSchema,
secretsConfigSchema,
storageConfigSchema,
storageLocalDiskConfigSchema,
@@ -163,6 +202,7 @@ export {
type DatabaseConfig,
type LoggingConfig,
type ServerConfig,
type AuthConfig,
type StorageConfig,
type StorageLocalDiskConfig,
type StorageS3Config,

View File

@@ -17,6 +17,8 @@ export interface Project {
status: ProjectStatus;
leadAgentId: string | null;
targetDate: string | null;
color: string | null;
archivedAt: Date | null;
createdAt: Date;
updatedAt: Date;
}

View File

@@ -10,6 +10,8 @@ export const createProjectSchema = z.object({
status: z.enum(PROJECT_STATUSES).optional().default("backlog"),
leadAgentId: z.string().uuid().optional().nullable(),
targetDate: z.string().optional().nullable(),
color: z.string().optional().nullable(),
archivedAt: z.string().datetime().optional().nullable(),
});
export type CreateProject = z.infer<typeof createProjectSchema>;