Add production APIs to frontend

This commit is contained in:
Urtzi Alfaro
2025-09-10 08:00:50 +02:00
parent aff644d793
commit 44b789f523
4 changed files with 657 additions and 0 deletions

View File

@@ -0,0 +1,243 @@
/**
* Production React Query hooks
*/
import { useMutation, useQuery, useQueryClient, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
import { productionService } from '../services/production';
import type {
ProductionBatchCreate,
ProductionBatchStatusUpdate,
ProductionBatchResponse,
ProductionBatchListResponse,
ProductionDashboardSummary,
DailyProductionRequirements,
ProductionScheduleData,
ProductionCapacityStatus,
ProductionRequirements,
ProductionYieldMetrics,
} from '../types/production';
import { ApiError } from '../client';
// Query Keys
export const productionKeys = {
all: ['production'] as const,
tenant: (tenantId: string) => [...productionKeys.all, tenantId] as const,
dashboard: (tenantId: string) => [...productionKeys.tenant(tenantId), 'dashboard'] as const,
dailyRequirements: (tenantId: string, date?: string) =>
[...productionKeys.tenant(tenantId), 'daily-requirements', date] as const,
requirements: (tenantId: string, date?: string) =>
[...productionKeys.tenant(tenantId), 'requirements', date] as const,
batches: (tenantId: string) => [...productionKeys.tenant(tenantId), 'batches'] as const,
activeBatches: (tenantId: string) => [...productionKeys.batches(tenantId), 'active'] as const,
batch: (tenantId: string, batchId: string) =>
[...productionKeys.batches(tenantId), batchId] as const,
schedule: (tenantId: string, startDate?: string, endDate?: string) =>
[...productionKeys.tenant(tenantId), 'schedule', startDate, endDate] as const,
capacity: (tenantId: string, date?: string) =>
[...productionKeys.tenant(tenantId), 'capacity', date] as const,
yieldMetrics: (tenantId: string, startDate: string, endDate: string) =>
[...productionKeys.tenant(tenantId), 'yield-metrics', startDate, endDate] as const,
} as const;
// Dashboard Queries
export const useProductionDashboard = (
tenantId: string,
options?: Omit<UseQueryOptions<ProductionDashboardSummary, ApiError>, 'queryKey' | 'queryFn'>
) => {
return useQuery<ProductionDashboardSummary, ApiError>({
queryKey: productionKeys.dashboard(tenantId),
queryFn: () => productionService.getDashboardSummary(tenantId),
enabled: !!tenantId,
staleTime: 30 * 1000, // 30 seconds
refetchInterval: 60 * 1000, // 1 minute
...options,
});
};
export const useDailyProductionRequirements = (
tenantId: string,
date?: string,
options?: Omit<UseQueryOptions<DailyProductionRequirements, ApiError>, 'queryKey' | 'queryFn'>
) => {
return useQuery<DailyProductionRequirements, ApiError>({
queryKey: productionKeys.dailyRequirements(tenantId, date),
queryFn: () => productionService.getDailyRequirements(tenantId, date),
enabled: !!tenantId,
staleTime: 5 * 60 * 1000, // 5 minutes
...options,
});
};
export const useProductionRequirements = (
tenantId: string,
date?: string,
options?: Omit<UseQueryOptions<ProductionRequirements, ApiError>, 'queryKey' | 'queryFn'>
) => {
return useQuery<ProductionRequirements, ApiError>({
queryKey: productionKeys.requirements(tenantId, date),
queryFn: () => productionService.getProductionRequirements(tenantId, date),
enabled: !!tenantId,
staleTime: 5 * 60 * 1000, // 5 minutes
...options,
});
};
// Batch Queries
export const useActiveBatches = (
tenantId: string,
options?: Omit<UseQueryOptions<ProductionBatchListResponse, ApiError>, 'queryKey' | 'queryFn'>
) => {
return useQuery<ProductionBatchListResponse, ApiError>({
queryKey: productionKeys.activeBatches(tenantId),
queryFn: () => productionService.getActiveBatches(tenantId),
enabled: !!tenantId,
staleTime: 30 * 1000, // 30 seconds
refetchInterval: 60 * 1000, // 1 minute
...options,
});
};
export const useBatchDetails = (
tenantId: string,
batchId: string,
options?: Omit<UseQueryOptions<ProductionBatchResponse, ApiError>, 'queryKey' | 'queryFn'>
) => {
return useQuery<ProductionBatchResponse, ApiError>({
queryKey: productionKeys.batch(tenantId, batchId),
queryFn: () => productionService.getBatchDetails(tenantId, batchId),
enabled: !!tenantId && !!batchId,
staleTime: 30 * 1000, // 30 seconds
...options,
});
};
// Schedule and Capacity Queries
export const useProductionSchedule = (
tenantId: string,
startDate?: string,
endDate?: string,
options?: Omit<UseQueryOptions<ProductionScheduleData, ApiError>, 'queryKey' | 'queryFn'>
) => {
return useQuery<ProductionScheduleData, ApiError>({
queryKey: productionKeys.schedule(tenantId, startDate, endDate),
queryFn: () => productionService.getProductionSchedule(tenantId, startDate, endDate),
enabled: !!tenantId,
staleTime: 5 * 60 * 1000, // 5 minutes
...options,
});
};
export const useCapacityStatus = (
tenantId: string,
date?: string,
options?: Omit<UseQueryOptions<ProductionCapacityStatus, ApiError>, 'queryKey' | 'queryFn'>
) => {
return useQuery<ProductionCapacityStatus, ApiError>({
queryKey: productionKeys.capacity(tenantId, date),
queryFn: () => productionService.getCapacityStatus(tenantId, date),
enabled: !!tenantId,
staleTime: 5 * 60 * 1000, // 5 minutes
...options,
});
};
export const useYieldMetrics = (
tenantId: string,
startDate: string,
endDate: string,
options?: Omit<UseQueryOptions<ProductionYieldMetrics, ApiError>, 'queryKey' | 'queryFn'>
) => {
return useQuery<ProductionYieldMetrics, ApiError>({
queryKey: productionKeys.yieldMetrics(tenantId, startDate, endDate),
queryFn: () => productionService.getYieldMetrics(tenantId, startDate, endDate),
enabled: !!tenantId && !!startDate && !!endDate,
staleTime: 15 * 60 * 1000, // 15 minutes (metrics are less frequently changing)
...options,
});
};
// Mutations
export const useCreateProductionBatch = (
options?: UseMutationOptions<ProductionBatchResponse, ApiError, { tenantId: string; batchData: ProductionBatchCreate }>
) => {
const queryClient = useQueryClient();
return useMutation<ProductionBatchResponse, ApiError, { tenantId: string; batchData: ProductionBatchCreate }>({
mutationFn: ({ tenantId, batchData }) => productionService.createProductionBatch(tenantId, batchData),
onSuccess: (data, { tenantId }) => {
// Invalidate active batches to refresh the list
queryClient.invalidateQueries({ queryKey: productionKeys.activeBatches(tenantId) });
// Invalidate dashboard to update summary
queryClient.invalidateQueries({ queryKey: productionKeys.dashboard(tenantId) });
// Cache the new batch details
queryClient.setQueryData(productionKeys.batch(tenantId, data.id), data);
},
...options,
});
};
export const useUpdateBatchStatus = (
options?: UseMutationOptions<
ProductionBatchResponse,
ApiError,
{ tenantId: string; batchId: string; statusUpdate: ProductionBatchStatusUpdate }
>
) => {
const queryClient = useQueryClient();
return useMutation<
ProductionBatchResponse,
ApiError,
{ tenantId: string; batchId: string; statusUpdate: ProductionBatchStatusUpdate }
>({
mutationFn: ({ tenantId, batchId, statusUpdate }) =>
productionService.updateBatchStatus(tenantId, batchId, statusUpdate),
onSuccess: (data, { tenantId, batchId }) => {
// Update the specific batch data
queryClient.setQueryData(productionKeys.batch(tenantId, batchId), data);
// Invalidate active batches to refresh the list
queryClient.invalidateQueries({ queryKey: productionKeys.activeBatches(tenantId) });
// Invalidate dashboard to update summary
queryClient.invalidateQueries({ queryKey: productionKeys.dashboard(tenantId) });
},
...options,
});
};
// Helper hooks for common use cases
export const useProductionDashboardData = (tenantId: string) => {
const dashboard = useProductionDashboard(tenantId);
const activeBatches = useActiveBatches(tenantId);
const dailyRequirements = useDailyProductionRequirements(tenantId);
return {
dashboard: dashboard.data,
activeBatches: activeBatches.data,
dailyRequirements: dailyRequirements.data,
isLoading: dashboard.isLoading || activeBatches.isLoading || dailyRequirements.isLoading,
error: dashboard.error || activeBatches.error || dailyRequirements.error,
refetch: () => {
dashboard.refetch();
activeBatches.refetch();
dailyRequirements.refetch();
},
};
};
export const useProductionPlanningData = (tenantId: string, date?: string) => {
const schedule = useProductionSchedule(tenantId);
const capacity = useCapacityStatus(tenantId, date);
const requirements = useProductionRequirements(tenantId, date);
return {
schedule: schedule.data,
capacity: capacity.data,
requirements: requirements.data,
isLoading: schedule.isLoading || capacity.isLoading || requirements.isLoading,
error: schedule.error || capacity.error || requirements.error,
refetch: () => {
schedule.refetch();
capacity.refetch();
requirements.refetch();
},
};
};

View File

@@ -26,6 +26,7 @@ export { alertProcessorService } from './services/alert_processor';
export { suppliersService } from './services/suppliers'; export { suppliersService } from './services/suppliers';
export { OrdersService } from './services/orders'; export { OrdersService } from './services/orders';
export { forecastingService } from './services/forecasting'; export { forecastingService } from './services/forecasting';
export { productionService } from './services/production';
// Types - Auth // Types - Auth
export type { export type {
@@ -312,6 +313,37 @@ export type {
export { BusinessType } from './types/forecasting'; export { BusinessType } from './types/forecasting';
// Types - Production
export type {
ProductionBatchBase,
ProductionBatchCreate,
ProductionBatchUpdate,
ProductionBatchStatusUpdate,
ProductionBatchResponse,
ProductionScheduleBase,
ProductionScheduleCreate,
ProductionScheduleUpdate,
ProductionScheduleResponse,
QualityCheckBase,
QualityCheckCreate,
QualityCheckResponse,
ProductionDashboardSummary,
DailyProductionRequirements,
ProductionMetrics,
ProductionBatchListResponse,
ProductionScheduleListResponse,
QualityCheckListResponse,
ProductionScheduleData,
ProductionCapacityStatus,
ProductionRequirements,
ProductionYieldMetrics,
} from './types/production';
export {
ProductionStatusEnum,
ProductionPriorityEnum,
} from './types/production';
// Hooks - Auth // Hooks - Auth
export { export {
useAuthProfile, useAuthProfile,
@@ -583,6 +615,23 @@ export {
forecastingKeys, forecastingKeys,
} from './hooks/forecasting'; } from './hooks/forecasting';
// Hooks - Production
export {
useProductionDashboard,
useDailyProductionRequirements,
useProductionRequirements,
useActiveBatches,
useBatchDetails,
useProductionSchedule,
useCapacityStatus,
useYieldMetrics,
useCreateProductionBatch,
useUpdateBatchStatus,
useProductionDashboardData,
useProductionPlanningData,
productionKeys,
} from './hooks/production';
// Query Key Factories (for advanced usage) // Query Key Factories (for advanced usage)
export { export {
authKeys, authKeys,
@@ -600,4 +649,5 @@ export {
ordersKeys, ordersKeys,
dataImportKeys, dataImportKeys,
forecastingKeys, forecastingKeys,
productionKeys,
}; };

View File

@@ -0,0 +1,79 @@
import { apiClient } from '../client';
import type {
ProductionBatchCreate,
ProductionBatchStatusUpdate,
ProductionBatchResponse,
ProductionBatchListResponse,
ProductionDashboardSummary,
DailyProductionRequirements,
ProductionScheduleData,
ProductionCapacityStatus,
ProductionRequirements,
ProductionYieldMetrics,
} from '../types/production';
export class ProductionService {
private readonly baseUrl = '/production';
getDashboardSummary(tenantId: string): Promise<ProductionDashboardSummary> {
return apiClient.get(`/tenants/${tenantId}${this.baseUrl}/dashboard-summary`);
}
getDailyRequirements(tenantId: string, date?: string): Promise<DailyProductionRequirements> {
const params = date ? { date } : {};
return apiClient.get(`/tenants/${tenantId}${this.baseUrl}/daily-requirements`, { params });
}
getProductionRequirements(tenantId: string, date?: string): Promise<ProductionRequirements> {
const params = date ? { date } : {};
return apiClient.get(`/tenants/${tenantId}${this.baseUrl}/requirements`, { params });
}
createProductionBatch(tenantId: string, batchData: ProductionBatchCreate): Promise<ProductionBatchResponse> {
return apiClient.post(`/tenants/${tenantId}${this.baseUrl}/batches`, batchData);
}
getActiveBatches(tenantId: string): Promise<ProductionBatchListResponse> {
return apiClient.get(`/tenants/${tenantId}${this.baseUrl}/batches/active`);
}
getBatchDetails(tenantId: string, batchId: string): Promise<ProductionBatchResponse> {
return apiClient.get(`/tenants/${tenantId}${this.baseUrl}/batches/${batchId}`);
}
updateBatchStatus(
tenantId: string,
batchId: string,
statusUpdate: ProductionBatchStatusUpdate
): Promise<ProductionBatchResponse> {
return apiClient.put(`/tenants/${tenantId}${this.baseUrl}/batches/${batchId}/status`, statusUpdate);
}
getProductionSchedule(
tenantId: string,
startDate?: string,
endDate?: string
): Promise<ProductionScheduleData> {
const params: Record<string, string> = {};
if (startDate) params.start_date = startDate;
if (endDate) params.end_date = endDate;
return apiClient.get(`/tenants/${tenantId}${this.baseUrl}/schedule`, { params });
}
getCapacityStatus(tenantId: string, date?: string): Promise<ProductionCapacityStatus> {
const params = date ? { date } : {};
return apiClient.get(`/tenants/${tenantId}${this.baseUrl}/capacity/status`, { params });
}
getYieldMetrics(
tenantId: string,
startDate: string,
endDate: string
): Promise<ProductionYieldMetrics> {
const params = { start_date: startDate, end_date: endDate };
return apiClient.get(`/tenants/${tenantId}${this.baseUrl}/metrics/yield`, { params });
}
}
export const productionService = new ProductionService();

View File

@@ -0,0 +1,285 @@
export enum ProductionStatusEnum {
PENDING = "pending",
IN_PROGRESS = "in_progress",
COMPLETED = "completed",
CANCELLED = "cancelled",
ON_HOLD = "on_hold",
QUALITY_CHECK = "quality_check",
FAILED = "failed"
}
export enum ProductionPriorityEnum {
LOW = "low",
MEDIUM = "medium",
HIGH = "high",
URGENT = "urgent"
}
export interface ProductionBatchBase {
product_id: string;
product_name: string;
recipe_id?: string | null;
planned_start_time: string;
planned_end_time: string;
planned_quantity: number;
planned_duration_minutes: number;
priority: ProductionPriorityEnum;
is_rush_order: boolean;
is_special_recipe: boolean;
production_notes?: string | null;
}
export interface ProductionBatchCreate extends ProductionBatchBase {
batch_number?: string | null;
order_id?: string | null;
forecast_id?: string | null;
equipment_used?: string[] | null;
staff_assigned?: string[] | null;
station_id?: string | null;
}
export interface ProductionBatchUpdate {
product_name?: string | null;
planned_start_time?: string | null;
planned_end_time?: string | null;
planned_quantity?: number | null;
planned_duration_minutes?: number | null;
actual_quantity?: number | null;
priority?: ProductionPriorityEnum | null;
equipment_used?: string[] | null;
staff_assigned?: string[] | null;
station_id?: string | null;
production_notes?: string | null;
}
export interface ProductionBatchStatusUpdate {
status: ProductionStatusEnum;
actual_quantity?: number | null;
notes?: string | null;
}
export interface ProductionBatchResponse {
id: string;
tenant_id: string;
batch_number: string;
product_id: string;
product_name: string;
recipe_id?: string | null;
planned_start_time: string;
planned_end_time: string;
planned_quantity: number;
planned_duration_minutes: number;
actual_start_time?: string | null;
actual_end_time?: string | null;
actual_quantity?: number | null;
actual_duration_minutes?: number | null;
status: ProductionStatusEnum;
priority: ProductionPriorityEnum;
estimated_cost?: number | null;
actual_cost?: number | null;
yield_percentage?: number | null;
quality_score?: number | null;
equipment_used?: string[] | null;
staff_assigned?: string[] | null;
station_id?: string | null;
order_id?: string | null;
forecast_id?: string | null;
is_rush_order: boolean;
is_special_recipe: boolean;
production_notes?: string | null;
quality_notes?: string | null;
delay_reason?: string | null;
cancellation_reason?: string | null;
created_at: string;
updated_at: string;
completed_at?: string | null;
}
export interface ProductionScheduleBase {
schedule_date: string;
shift_start: string;
shift_end: string;
total_capacity_hours: number;
planned_capacity_hours: number;
staff_count: number;
equipment_capacity?: Record<string, any> | null;
station_assignments?: Record<string, any> | null;
schedule_notes?: string | null;
}
export interface ProductionScheduleCreate extends ProductionScheduleBase {}
export interface ProductionScheduleUpdate {
shift_start?: string | null;
shift_end?: string | null;
total_capacity_hours?: number | null;
planned_capacity_hours?: number | null;
staff_count?: number | null;
overtime_hours?: number | null;
equipment_capacity?: Record<string, any> | null;
station_assignments?: Record<string, any> | null;
schedule_notes?: string | null;
}
export interface ProductionScheduleResponse {
id: string;
tenant_id: string;
schedule_date: string;
shift_start: string;
shift_end: string;
total_capacity_hours: number;
planned_capacity_hours: number;
actual_capacity_hours?: number | null;
overtime_hours?: number | null;
staff_count: number;
equipment_capacity?: Record<string, any> | null;
station_assignments?: Record<string, any> | null;
total_batches_planned: number;
total_batches_completed?: number | null;
total_quantity_planned: number;
total_quantity_produced?: number | null;
is_finalized: boolean;
is_active: boolean;
efficiency_percentage?: number | null;
utilization_percentage?: number | null;
on_time_completion_rate?: number | null;
schedule_notes?: string | null;
schedule_adjustments?: Record<string, any> | null;
created_at: string;
updated_at: string;
finalized_at?: string | null;
}
export interface QualityCheckBase {
batch_id: string;
check_type: string;
check_time: string;
quality_score: number;
pass_fail: boolean;
defect_count: number;
defect_types?: string[] | null;
check_notes?: string | null;
}
export interface QualityCheckCreate extends QualityCheckBase {
checker_id?: string | null;
measured_weight?: number | null;
measured_temperature?: number | null;
measured_moisture?: number | null;
measured_dimensions?: Record<string, number> | null;
target_weight?: number | null;
target_temperature?: number | null;
target_moisture?: number | null;
tolerance_percentage?: number | null;
corrective_actions?: string[] | null;
}
export interface QualityCheckResponse {
id: string;
tenant_id: string;
batch_id: string;
check_type: string;
check_time: string;
checker_id?: string | null;
quality_score: number;
pass_fail: boolean;
defect_count: number;
defect_types?: string[] | null;
measured_weight?: number | null;
measured_temperature?: number | null;
measured_moisture?: number | null;
measured_dimensions?: Record<string, number> | null;
target_weight?: number | null;
target_temperature?: number | null;
target_moisture?: number | null;
tolerance_percentage?: number | null;
within_tolerance?: boolean | null;
corrective_action_needed: boolean;
corrective_actions?: string[] | null;
check_notes?: string | null;
photos_urls?: string[] | null;
certificate_url?: string | null;
created_at: string;
updated_at: string;
}
export interface ProductionDashboardSummary {
active_batches: number;
todays_production_plan: Record<string, any>[];
capacity_utilization: number;
on_time_completion_rate: number;
average_quality_score: number;
total_output_today: number;
efficiency_percentage: number;
}
export interface DailyProductionRequirements {
date: string;
production_plan: Record<string, any>[];
total_capacity_needed: number;
available_capacity: number;
capacity_gap: number;
urgent_items: number;
recommended_schedule?: Record<string, any> | null;
}
export interface ProductionMetrics {
period_start: string;
period_end: string;
total_batches: number;
completed_batches: number;
completion_rate: number;
average_yield_percentage: number;
on_time_completion_rate: number;
total_production_cost: number;
average_quality_score: number;
efficiency_trends: Record<string, any>[];
}
export interface ProductionBatchListResponse {
batches: ProductionBatchResponse[];
total_count: number;
page: number;
page_size: number;
}
export interface ProductionScheduleListResponse {
schedules: ProductionScheduleResponse[];
total_count: number;
page: number;
page_size: number;
}
export interface QualityCheckListResponse {
quality_checks: QualityCheckResponse[];
total_count: number;
page: number;
page_size: number;
}
export interface ProductionScheduleData {
start_date: string;
end_date: string;
schedules: {
id: string;
date: string;
shift_start: string;
shift_end: string;
capacity_utilization: number;
batches_planned: number;
is_finalized: boolean;
}[];
total_schedules: number;
}
export interface ProductionCapacityStatus {
[key: string]: any;
}
export interface ProductionRequirements {
[key: string]: any;
}
export interface ProductionYieldMetrics {
[key: string]: any;
}