fix(projects): audit pass — 8 correctness and consistency fixes
- Project.to_dict() now includes user_id and auto_summary - Status validation unified to (active/completed/archived) on both create and update project routes; update route previously had none - Milestone routes: replace get_project (ownership-only) with get_project_for_user so shared viewers/editors can access milestones - Add get_milestone_in_project() to milestones service for project- scoped lookup without user_id filter; all milestone routes use it - Milestone PATCH now validates status as 'active'|'done'; fix tool enum which was wrongly ['active','completed','cancelled'] - Write mutation routes (POST/PATCH/DELETE milestones) now check can_write_project() and return 403 for read-only shared users - update_project tool now exposes title and color fields so projects can be renamed or recolored via chat - create_project tool now exposes color field - GET /api/projects?include_summary=true embeds summaries in one backend pass; ProjectListView switches to this, eliminating N+1 per-project fetches Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,11 +16,15 @@ interface MilestoneSummary {
|
||||
|
||||
interface Project {
|
||||
id: number;
|
||||
user_id: number;
|
||||
title: string;
|
||||
description: string | null;
|
||||
goal: string | null;
|
||||
status: "active" | "completed" | "archived";
|
||||
color: string | null;
|
||||
auto_summary: string | null;
|
||||
permission?: string;
|
||||
is_shared?: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
summary?: {
|
||||
@@ -54,19 +58,8 @@ async function loadProjects() {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const data = await apiGet<{ projects: Project[] }>("/api/projects");
|
||||
const data = await apiGet<{ projects: Project[] }>("/api/projects?include_summary=true");
|
||||
projects.value = data.projects;
|
||||
// Fetch summaries (including milestone_summary) in parallel
|
||||
await Promise.allSettled(
|
||||
projects.value.map(async (p) => {
|
||||
try {
|
||||
const full = await apiGet<Project>(`/api/projects/${p.id}`);
|
||||
p.summary = full.summary;
|
||||
} catch {
|
||||
// non-fatal
|
||||
}
|
||||
})
|
||||
);
|
||||
} catch {
|
||||
error.value = "Failed to load projects.";
|
||||
} finally {
|
||||
|
||||
@@ -19,11 +19,14 @@ interface Milestone {
|
||||
|
||||
interface Project {
|
||||
id: number;
|
||||
user_id: number;
|
||||
title: string;
|
||||
description: string | null;
|
||||
goal: string | null;
|
||||
status: "active" | "completed" | "archived";
|
||||
color: string | null;
|
||||
auto_summary: string | null;
|
||||
permission?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
summary?: {
|
||||
|
||||
Reference in New Issue
Block a user