- 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
128 lines
3.3 KiB
TypeScript
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`);
|
|
}
|