docs(openspec): add reporting API contract documentation

Add comprehensive API documentation for the reporting endpoint:
- Request/response structure
- View type inference (did/is/will)
- Blank vs explicit zero semantics
- Status values and error responses

Related to enhanced-allocation change.
This commit is contained in:
2026-03-08 18:22:27 -04:00
parent 3324c4f156
commit b7bbfb45c0
27 changed files with 1632 additions and 35 deletions

View File

@@ -105,6 +105,23 @@
return allocations.reduce((sum, a) => sum + parseFloat(a.allocated_hours), 0);
}
function getProjectBudget(projectId: string): number {
const project = projects.find(p => p.id === projectId);
if (!project?.approved_estimate) return 0;
// Monthly budget = approved_estimate / 12
return Math.round(Number(project.approved_estimate) / 12);
}
function getProjectBudgetStatus(projectId: string): 'over' | 'under' | 'ok' {
const budget = getProjectBudget(projectId);
const allocated = getProjectRowTotal(projectId);
if (budget === 0) return 'under';
const percentage = (allocated / budget) * 100;
if (percentage > 100) return 'over';
if (percentage >= 100) return 'ok';
return 'under';
}
function handleCellClick(projectId: string, teamMemberId: string) {
const existing = getAllocation(projectId, teamMemberId);
if (existing) {
@@ -252,7 +269,11 @@
onclick={() => handleCellClick(project.id, member.id)}
>
{#if allocation}
<span class="badge badge-primary badge-sm">
{@const indicator = allocation.allocation_indicator || 'gray'}
<span
class="badge badge-sm {indicator === 'green' ? 'badge-success' : indicator === 'yellow' ? 'badge-warning' : indicator === 'red' ? 'badge-error' : 'badge-ghost'}"
title="{indicator === 'green' ? 'Fully allocated' : indicator === 'yellow' ? 'Under allocated' : indicator === 'red' ? 'Over allocated' : 'No budget'}"
>
{allocation.allocated_hours}h
</span>
{:else}
@@ -266,6 +287,34 @@
</tr>
{/each}
</tbody>
<!-- Monthly Budget Section -->
<tfoot>
<tr class="bg-base-200/50">
<td class="font-bold">Monthly Budget</td>
{#each teamMembers as member}
<td class="text-center">-</td>
{/each}
<td class="text-center">
{Math.round(projects.reduce((sum, p) => sum + (getProjectBudget(p.id) || 0), 0))}h
</td>
</tr>
<tr class="bg-base-200/50">
<td class="font-bold">Status</td>
{#each teamMembers as member}
<td class="text-center">-</td>
{/each}
<td class="text-center">
{#if getProjectTotal() > projects.reduce((sum, p) => sum + getProjectBudget(p.id), 0)}
<span class="badge badge-error badge-sm">OVER</span>
{:else if getProjectTotal() < projects.reduce((sum, p) => sum + getProjectBudget(p.id), 0)}
<span class="badge badge-warning badge-sm">UNDER</span>
{:else}
<span class="badge badge-success badge-sm">OK</span>
{/if}
</td>
</tr>
</tfoot>
<tfoot>
<tr class="font-bold bg-base-200">
<td class="sticky left-0 bg-base-200 z-10">Total</td>