56 lines
2.4 KiB
TypeScript
56 lines
2.4 KiB
TypeScript
|
|
import type { ProjectReviewStatus } from '../../shared/types.js';
|
||
|
|
import { database, databaseDialect, type QueryContext } from '../database.js';
|
||
|
|
|
||
|
|
export interface ProjectReviewStatusResult {
|
||
|
|
reviewStatus: ProjectReviewStatus;
|
||
|
|
workCount: number;
|
||
|
|
approvedCount: number;
|
||
|
|
completedAt: string | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function deriveProjectReviewStatus(workCount: number, approvedCount: number): Exclude<ProjectReviewStatus, 'archived'> {
|
||
|
|
if (workCount === 0) return 'draft';
|
||
|
|
if (approvedCount === workCount) return 'completed';
|
||
|
|
return 'reviewing';
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function recalculateProjectReviewStatus(
|
||
|
|
projectId: number,
|
||
|
|
tx: QueryContext = database,
|
||
|
|
): Promise<ProjectReviewStatusResult | null> {
|
||
|
|
const project = await tx.one<{ status: string; review_status: ProjectReviewStatus; review_completed_at: string | null }>(
|
||
|
|
`SELECT status, review_status, review_completed_at FROM projects WHERE id = ?${databaseDialect === 'postgres' ? ' FOR NO KEY UPDATE' : ''}`,
|
||
|
|
[projectId],
|
||
|
|
);
|
||
|
|
if (!project) return null;
|
||
|
|
|
||
|
|
const counts = await tx.one<{ work_count: number | string; approved_count: number | string }>(
|
||
|
|
`SELECT COUNT(*) AS work_count,
|
||
|
|
SUM(CASE WHEN review_status = 'approved' THEN 1 ELSE 0 END) AS approved_count
|
||
|
|
FROM notes WHERE project_id = ? AND review_status != 'draft'`,
|
||
|
|
[projectId],
|
||
|
|
);
|
||
|
|
const workCount = Number(counts?.work_count ?? 0);
|
||
|
|
const approvedCount = Number(counts?.approved_count ?? 0);
|
||
|
|
|
||
|
|
if (project.status === 'archived') {
|
||
|
|
return { reviewStatus: 'archived', workCount, approvedCount, completedAt: project.review_completed_at };
|
||
|
|
}
|
||
|
|
|
||
|
|
const reviewStatus = deriveProjectReviewStatus(workCount, approvedCount);
|
||
|
|
const completedAt = reviewStatus === 'completed'
|
||
|
|
? project.review_completed_at ?? new Date().toISOString()
|
||
|
|
: null;
|
||
|
|
await tx.execute('UPDATE projects SET review_status = ?, review_completed_at = ? WHERE id = ?', [reviewStatus, completedAt, projectId]);
|
||
|
|
return { reviewStatus, workCount, approvedCount, completedAt };
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function ensureProjectCompatibilityCollection(projectId: number, tx: QueryContext = database): Promise<number> {
|
||
|
|
const existing = await tx.one<{ id: number }>('SELECT id FROM collections WHERE project_id = ? ORDER BY id LIMIT 1', [projectId]);
|
||
|
|
if (existing) return Number(existing.id);
|
||
|
|
return tx.insertId(
|
||
|
|
"INSERT INTO collections (project_id, name, client_description, status) VALUES (?, ?, '', 'draft')",
|
||
|
|
[projectId, '__project_default__'],
|
||
|
|
);
|
||
|
|
}
|