feat: Show project pills on initiative cards in list view
Add projects to the listInitiatives tRPC response and render them as outline badge pills between the initiative name and activity row.
This commit is contained in:
@@ -140,16 +140,31 @@ export function initiativeProcedures(publicProcedure: ProcedureBuilder) {
|
||||
)
|
||||
.map(a => ({ initiativeId: a.initiativeId ?? '', mode: a.mode ?? '', status: a.status }));
|
||||
|
||||
// Batch-fetch projects for all initiatives
|
||||
const projectRepo = ctx.projectRepository;
|
||||
const projectsByInitiativeId = new Map<string, Array<{ id: string; name: string }>>();
|
||||
if (projectRepo) {
|
||||
await Promise.all(initiatives.map(async (init) => {
|
||||
const projects = await projectRepo.findProjectsByInitiativeId(init.id);
|
||||
projectsByInitiativeId.set(init.id, projects.map(p => ({ id: p.id, name: p.name })));
|
||||
}));
|
||||
}
|
||||
|
||||
const addProjects = (init: typeof initiatives[0]) => ({
|
||||
projects: projectsByInitiativeId.get(init.id) ?? [],
|
||||
});
|
||||
|
||||
if (ctx.phaseRepository) {
|
||||
const phaseRepo = ctx.phaseRepository;
|
||||
return Promise.all(initiatives.map(async (init) => {
|
||||
const phases = await phaseRepo.findByInitiativeId(init.id);
|
||||
return { ...init, activity: deriveInitiativeActivity(init, phases, activeArchitectAgents) };
|
||||
return { ...init, ...addProjects(init), activity: deriveInitiativeActivity(init, phases, activeArchitectAgents) };
|
||||
}));
|
||||
}
|
||||
|
||||
return initiatives.map(init => ({
|
||||
...init,
|
||||
...addProjects(init),
|
||||
activity: deriveInitiativeActivity(init, [], activeArchitectAgents),
|
||||
}));
|
||||
}),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { MoreHorizontal } from "lucide-react";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -20,6 +21,7 @@ export interface SerializedInitiative {
|
||||
branch: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
projects?: Array<{ id: string; name: string }>;
|
||||
activity: {
|
||||
state: string;
|
||||
activePhase?: { id: string; name: string };
|
||||
@@ -113,6 +115,17 @@ export function InitiativeCard({ initiative, onClick }: InitiativeCardProps) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Project pills */}
|
||||
{initiative.projects && initiative.projects.length > 0 && (
|
||||
<div className="mt-1.5 flex flex-wrap gap-1">
|
||||
{initiative.projects.map((p) => (
|
||||
<Badge key={p.id} variant="outline" size="xs" className="font-normal">
|
||||
{p.name}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Row 2: Activity dot + label + active phase + progress */}
|
||||
<div className="mt-1.5 flex items-center gap-3">
|
||||
<StatusDot
|
||||
|
||||
Reference in New Issue
Block a user