feat(allocation): implement resource allocation feature
- Add AllocationController with CRUD + bulk endpoints - Add AllocationValidationService for capacity/estimate validation - Add AllocationMatrixService for optimized matrix queries - Add AllocationPolicy for authorization - Add AllocationResource for API responses - Add frontend allocationService and matrix UI - Add E2E tests for allocation matrix (20 tests) - Add unit tests for validation service and policies - Fix month format conversion (YYYY-MM to YYYY-MM-01)
This commit is contained in:
92
frontend/src/lib/services/allocationService.ts
Normal file
92
frontend/src/lib/services/allocationService.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Allocation Service
|
||||
*
|
||||
* API operations for resource allocation management.
|
||||
*/
|
||||
|
||||
import { api } from './api';
|
||||
|
||||
export interface Allocation {
|
||||
id: string;
|
||||
project_id: string;
|
||||
team_member_id: string;
|
||||
month: string;
|
||||
allocated_hours: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
project?: {
|
||||
id: string;
|
||||
code: string;
|
||||
title: string;
|
||||
};
|
||||
team_member?: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CreateAllocationRequest {
|
||||
project_id: string;
|
||||
team_member_id: string;
|
||||
month: string;
|
||||
allocated_hours: number;
|
||||
}
|
||||
|
||||
export interface UpdateAllocationRequest {
|
||||
allocated_hours: number;
|
||||
}
|
||||
|
||||
export interface BulkAllocationRequest {
|
||||
allocations: CreateAllocationRequest[];
|
||||
}
|
||||
|
||||
// Allocation API methods
|
||||
export const allocationService = {
|
||||
/**
|
||||
* Get all allocations, optionally filtered by month
|
||||
*/
|
||||
getAll: (month?: string) => {
|
||||
const query = month ? `?month=${month}` : '';
|
||||
return api.get<Allocation[]>(`/allocations${query}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a single allocation by ID
|
||||
*/
|
||||
getById: (id: string) =>
|
||||
api.get<Allocation>(`/allocations/${id}`),
|
||||
|
||||
/**
|
||||
* Create a new allocation
|
||||
*/
|
||||
create: (data: CreateAllocationRequest) =>
|
||||
api.post<Allocation>('/allocations', data),
|
||||
|
||||
/**
|
||||
* Update an existing allocation
|
||||
*/
|
||||
update: (id: string, data: UpdateAllocationRequest) =>
|
||||
api.put<Allocation>(`/allocations/${id}`, data),
|
||||
|
||||
/**
|
||||
* Delete an allocation
|
||||
*/
|
||||
delete: (id: string) =>
|
||||
api.delete<{ message: string }>(`/allocations/${id}`),
|
||||
|
||||
/**
|
||||
* Bulk create allocations
|
||||
*/
|
||||
bulkCreate: (data: BulkAllocationRequest) =>
|
||||
api.post<Allocation[]>('/allocations/bulk', data),
|
||||
};
|
||||
|
||||
/**
|
||||
* Format allocated hours
|
||||
*/
|
||||
export function formatAllocatedHours(hours: string | number): string {
|
||||
const numHours = typeof hours === 'string' ? parseFloat(hours) : hours;
|
||||
return `${numHours}h`;
|
||||
}
|
||||
|
||||
export default allocationService;
|
||||
@@ -144,7 +144,15 @@ interface ApiRequestOptions {
|
||||
|
||||
// Main API request function
|
||||
export async function apiRequest<T>(endpoint: string, options: ApiRequestOptions = {}): Promise<T> {
|
||||
const url = `${API_BASE_URL}${endpoint}`;
|
||||
// Ensure we have an absolute URL for server-side rendering
|
||||
let url = endpoint;
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
// Get the base URL - works in both browser and server contexts
|
||||
const baseUrl = typeof window !== 'undefined'
|
||||
? ''
|
||||
: process.env['ORIGIN'] || '';
|
||||
url = `${baseUrl}${API_BASE_URL}${endpoint}`;
|
||||
}
|
||||
|
||||
// Prepare headers
|
||||
const headers: Record<string, string> = {
|
||||
|
||||
Reference in New Issue
Block a user