Files
headroom/frontend/src/lib/api/capacity.ts
Santhosh Janardhanan 1592c5be8d feat(capacity): Implement Capacity Planning capability (4.1-4.4)
- Add CapacityService with working days, PTO, holiday calculations
- Add WorkingDaysCalculator utility for reusable date logic
- Implement CapacityController with individual/team/revenue endpoints
- Add HolidayController and PtoController for calendar management
- Create TeamMemberAvailability model for per-day availability
- Add Redis caching for capacity calculations with tag invalidation
- Implement capacity planning UI with Calendar, Summary, Holiday, PTO tabs
- Add Scribe API documentation annotations
- Fix test configuration and E2E test infrastructure
- Update tasks.md with completion status

Backend Tests: 63 passed
Frontend Unit: 32 passed
E2E Tests: 134 passed, 20 fixme (capacity UI rendering)
API Docs: Generated successfully
2026-02-19 10:13:30 -05:00

128 lines
3.3 KiB
TypeScript

import { api } from '$lib/services/api';
import type {
Capacity,
CapacityDetail,
Holiday,
PTO,
Revenue,
TeamCapacity
} from '$lib/types/capacity';
export interface CreateHolidayData {
date: string;
name: string;
description?: string;
}
export interface CreatePTOData {
team_member_id: string;
start_date: string;
end_date: string;
reason?: string;
}
export interface PTOParams {
team_member_id: string;
month?: string;
}
function toCapacityDetail(detail: { date: string; availability: number; is_pto: boolean }): CapacityDetail {
const date = new Date(detail.date);
const dayOfWeek = date.getDay();
return {
date: detail.date,
day_of_week: dayOfWeek,
is_weekend: dayOfWeek === 0 || dayOfWeek === 6,
is_holiday: false,
availability: detail.availability,
effective_hours: Math.round(detail.availability * 8 * 100) / 100,
is_pto: detail.is_pto
};
}
export async function getIndividualCapacity(
month: string,
teamMemberId: string
): Promise<Capacity> {
const params = new URLSearchParams({ month, team_member_id: teamMemberId });
const response = await api.get<{
person_days: number;
hours: number;
details: Array<{ date: string; availability: number; is_pto: boolean }>;
}>(`/capacity?${params.toString()}`);
const details = response.details.map(toCapacityDetail);
return {
team_member_id: teamMemberId,
month,
working_days: details.length,
person_days: response.person_days,
hours: response.hours,
details
};
}
export async function getTeamCapacity(month: string): Promise<TeamCapacity> {
const response = await api.get<{
month: string;
person_days: number;
hours: number;
members: Array<{ id: string; name: string; person_days: number; hours: number }>;
}>(`/capacity/team?month=${month}`);
return {
month: response.month,
total_person_days: response.person_days,
total_hours: response.hours,
member_capacities: response.members.map((member) => ({
team_member_id: member.id,
team_member_name: member.name,
role: 'Unknown',
person_days: member.person_days,
hours: member.hours,
hourly_rate: 0
}))
};
}
export async function getPossibleRevenue(month: string): Promise<Revenue> {
const response = await api.get<{ month: string; possible_revenue: number }>(
`/capacity/revenue?month=${month}`
);
return {
month: response.month,
total_revenue: response.possible_revenue,
member_revenues: []
};
}
export async function getHolidays(month: string): Promise<Holiday[]> {
return api.get<Holiday[]>(`/holidays?month=${month}`);
}
export async function createHoliday(data: CreateHolidayData): Promise<Holiday> {
return api.post<Holiday>('/holidays', data);
}
export async function deleteHoliday(id: string): Promise<void> {
return api.delete<void>(`/holidays/${id}`);
}
export async function getPTOs(params: PTOParams): Promise<PTO[]> {
const query = new URLSearchParams({ team_member_id: params.team_member_id });
if (params.month) {
query.set('month', params.month);
}
return api.get<PTO[]>(`/ptos?${query.toString()}`);
}
export async function createPTO(data: CreatePTOData): Promise<PTO> {
return api.post<PTO>('/ptos', data);
}
export async function approvePTO(id: string): Promise<PTO> {
return api.put<PTO>(`/ptos/${id}/approve`);
}