feat(frontend): update allocation UI and services

Enhance allocation management interface:
- Allocations page: modal-based editing, variance display
- Updated services: allocationService with bulk ops, projectMonthPlanService
- Project and team member pages: reconciliation status indicators
- Navigation config: add planning and reports links

Part of enhanced-allocation change.
This commit is contained in:
2026-03-08 18:23:00 -04:00
parent 9b0f42fdf5
commit dd8055f6b7
7 changed files with 180 additions and 89 deletions

View File

@@ -1,18 +1,39 @@
/**
* Allocation Service
*
*
* API operations for resource allocation management.
*/
import { api } from './api';
export type VarianceStatus = 'OVER' | 'UNDER' | 'MATCH' | null;
export interface RowVariance {
allocated_total: number;
planned_month: number;
variance: number;
status: VarianceStatus;
}
export interface ColumnVariance {
allocated: number;
capacity: number;
variance: number;
status: VarianceStatus;
}
export interface Allocation {
id: string;
project_id: string;
team_member_id: string | null;
month: string;
allocated_hours: string;
is_untracked?: boolean;
row_variance?: RowVariance;
column_variance?: ColumnVariance | null;
allocation_indicator?: 'green' | 'yellow' | 'red' | 'gray';
warnings?: string[];
utilization?: number;
created_at: string;
updated_at: string;
project?: {
@@ -28,7 +49,7 @@ export interface Allocation {
export interface CreateAllocationRequest {
project_id: string;
team_member_id: string;
team_member_id: string | null;
month: string;
allocated_hours: number;
}
@@ -41,6 +62,12 @@ export interface BulkAllocationRequest {
allocations: CreateAllocationRequest[];
}
export interface BulkAllocationResponse {
data: Array<{ index: number; id: string; status: string }>;
failed: Array<{ index: number; errors: Record<string, string[]> }>;
summary: { created: number; failed: number };
}
// Allocation API methods
export const allocationService = {
/**
@@ -54,32 +81,29 @@ export const allocationService = {
/**
* Get a single allocation by ID
*/
getById: (id: string) =>
api.get<Allocation>(`/allocations/${id}`),
getById: (id: string) => api.get<Allocation>(`/allocations/${id}`),
/**
* Create a new allocation
*/
create: (data: CreateAllocationRequest) =>
api.post<Allocation>('/allocations', data),
create: (data: CreateAllocationRequest) => api.post<Allocation>('/allocations', data),
/**
* Update an existing allocation
*/
update: (id: string, data: UpdateAllocationRequest) =>
update: (id: string, data: UpdateAllocationRequest) =>
api.put<Allocation>(`/allocations/${id}`, data),
/**
* Delete an allocation
*/
delete: (id: string) =>
api.delete<{ message: string }>(`/allocations/${id}`),
delete: (id: string) => api.delete<{ message: string }>(`/allocations/${id}`),
/**
* Bulk create allocations
*/
bulkCreate: (data: BulkAllocationRequest) =>
api.post<Allocation[]>('/allocations/bulk', data),
bulkCreate: (data: BulkAllocationRequest) =>
api.post<BulkAllocationResponse>('/allocations/bulk', data),
};
/**

View File

@@ -2,7 +2,9 @@ import { api } from '$lib/services/api';
export interface ProjectMonthPlan {
project_id: string;
project_code: string;
project_name: string;
project_status: string | null;
approved_estimate: number;
months: Record<string, {
id: string;
@@ -13,13 +15,6 @@ export interface ProjectMonthPlan {
reconciliation_status: 'OVER' | 'UNDER' | 'MATCH';
}
export interface ProjectMonthPlansResponse {
data: ProjectMonthPlan[];
meta: {
year: number;
};
}
export interface BulkUpdateRequest {
year: number;
items: Array<{
@@ -39,14 +34,13 @@ export interface BulkUpdateResponse {
}
class ProjectMonthPlanService {
async getPlans(year: number): Promise<ProjectMonthPlansResponse> {
const response = await api.get<ProjectMonthPlansResponse>(`/project-month-plans?year=${year}`);
return response.data;
// Note: unwrapResponse strips the {data} wrapper, so this returns the array directly
async getPlans(year: number): Promise<ProjectMonthPlan[]> {
return api.get<ProjectMonthPlan[]>(`/project-month-plans?year=${year}`);
}
async bulkUpdate(request: BulkUpdateRequest): Promise<BulkUpdateResponse> {
const response = await api.put<BulkUpdateResponse>('/project-month-plans/bulk', request);
return response.data;
return api.put<BulkUpdateResponse>('/project-month-plans/bulk', request);
}
}

View File

@@ -35,6 +35,8 @@ export interface UpdateProjectRequest {
code?: string;
title?: string;
type_id?: number;
status_id?: number;
approved_estimate?: number | null;
}
export const projectService = {