Add procurement management logic

This commit is contained in:
Urtzi Alfaro
2025-08-23 19:47:08 +02:00
parent 62ff755f25
commit 5077a45a25
22 changed files with 4011 additions and 79 deletions

View File

@@ -13,6 +13,21 @@ export { useNotification } from './useNotification';
export { useOnboarding, useOnboardingStep } from './useOnboarding';
export { useInventory, useInventoryDashboard, useInventoryItem, useInventoryProducts } from './useInventory';
export { useRecipes, useProduction } from './useRecipes';
export {
useCurrentProcurementPlan,
useProcurementPlanByDate,
useProcurementPlan,
useProcurementPlans,
usePlanRequirements,
useCriticalRequirements,
useProcurementDashboard,
useGenerateProcurementPlan,
useUpdatePlanStatus,
useTriggerDailyScheduler,
useProcurementHealth,
useProcurementPlanDashboard,
useProcurementPlanActions
} from './useProcurement';
// Import hooks for combined usage
import { useAuth } from './useAuth';

View File

@@ -0,0 +1,294 @@
// ================================================================
// frontend/src/api/hooks/useProcurement.ts
// ================================================================
/**
* React hooks for procurement planning functionality
*/
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { procurementService } from '../services/procurement.service';
import type {
ProcurementPlan,
GeneratePlanRequest,
GeneratePlanResponse,
DashboardData,
ProcurementRequirement,
PaginatedProcurementPlans
} from '../types/procurement';
// ================================================================
// QUERY KEYS
// ================================================================
export const procurementKeys = {
all: ['procurement'] as const,
plans: () => [...procurementKeys.all, 'plans'] as const,
plan: (id: string) => [...procurementKeys.plans(), id] as const,
currentPlan: () => [...procurementKeys.plans(), 'current'] as const,
planByDate: (date: string) => [...procurementKeys.plans(), 'date', date] as const,
plansList: (filters?: any) => [...procurementKeys.plans(), 'list', filters] as const,
requirements: () => [...procurementKeys.all, 'requirements'] as const,
planRequirements: (planId: string) => [...procurementKeys.requirements(), 'plan', planId] as const,
criticalRequirements: () => [...procurementKeys.requirements(), 'critical'] as const,
dashboard: () => [...procurementKeys.all, 'dashboard'] as const,
};
// ================================================================
// PROCUREMENT PLAN HOOKS
// ================================================================
/**
* Hook to fetch the current day's procurement plan
*/
export function useCurrentProcurementPlan() {
return useQuery({
queryKey: procurementKeys.currentPlan(),
queryFn: () => procurementService.getCurrentPlan(),
staleTime: 5 * 60 * 1000, // 5 minutes
refetchInterval: 10 * 60 * 1000, // Refetch every 10 minutes
});
}
/**
* Hook to fetch procurement plan by date
*/
export function useProcurementPlanByDate(date: string, enabled = true) {
return useQuery({
queryKey: procurementKeys.planByDate(date),
queryFn: () => procurementService.getPlanByDate(date),
enabled: enabled && !!date,
staleTime: 30 * 60 * 1000, // 30 minutes for historical data
});
}
/**
* Hook to fetch procurement plan by ID
*/
export function useProcurementPlan(planId: string, enabled = true) {
return useQuery({
queryKey: procurementKeys.plan(planId),
queryFn: () => procurementService.getPlanById(planId),
enabled: enabled && !!planId,
staleTime: 10 * 60 * 1000, // 10 minutes
});
}
/**
* Hook to fetch paginated list of procurement plans
*/
export function useProcurementPlans(params?: {
status?: string;
startDate?: string;
endDate?: string;
limit?: number;
offset?: number;
}) {
return useQuery({
queryKey: procurementKeys.plansList(params),
queryFn: () => procurementService.listPlans(params),
staleTime: 5 * 60 * 1000, // 5 minutes
});
}
// ================================================================
// REQUIREMENTS HOOKS
// ================================================================
/**
* Hook to fetch requirements for a specific plan
*/
export function usePlanRequirements(
planId: string,
filters?: {
status?: string;
priority?: string;
},
enabled = true
) {
return useQuery({
queryKey: procurementKeys.planRequirements(planId),
queryFn: () => procurementService.getPlanRequirements(planId, filters),
enabled: enabled && !!planId,
staleTime: 5 * 60 * 1000, // 5 minutes
});
}
/**
* Hook to fetch critical requirements across all plans
*/
export function useCriticalRequirements() {
return useQuery({
queryKey: procurementKeys.criticalRequirements(),
queryFn: () => procurementService.getCriticalRequirements(),
staleTime: 2 * 60 * 1000, // 2 minutes for critical data
refetchInterval: 5 * 60 * 1000, // Refetch every 5 minutes
});
}
// ================================================================
// DASHBOARD HOOKS
// ================================================================
/**
* Hook to fetch procurement dashboard data
*/
export function useProcurementDashboard() {
return useQuery({
queryKey: procurementKeys.dashboard(),
queryFn: () => procurementService.getDashboardData(),
staleTime: 2 * 60 * 1000, // 2 minutes
refetchInterval: 5 * 60 * 1000, // Refetch every 5 minutes
});
}
// ================================================================
// MUTATION HOOKS
// ================================================================
/**
* Hook to generate a new procurement plan
*/
export function useGenerateProcurementPlan() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (request: GeneratePlanRequest) =>
procurementService.generatePlan(request),
onSuccess: (data: GeneratePlanResponse) => {
// Invalidate relevant queries
queryClient.invalidateQueries({ queryKey: procurementKeys.plans() });
queryClient.invalidateQueries({ queryKey: procurementKeys.dashboard() });
// If plan was generated successfully, update the cache
if (data.success && data.plan) {
queryClient.setQueryData(
procurementKeys.plan(data.plan.id),
data.plan
);
// Update current plan cache if this is today's plan
const today = new Date().toISOString().split('T')[0];
if (data.plan.plan_date === today) {
queryClient.setQueryData(
procurementKeys.currentPlan(),
data.plan
);
}
}
},
});
}
/**
* Hook to update procurement plan status
*/
export function useUpdatePlanStatus() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ planId, status }: { planId: string; status: string }) =>
procurementService.updatePlanStatus(planId, status),
onSuccess: (updatedPlan: ProcurementPlan) => {
// Update the specific plan in cache
queryClient.setQueryData(
procurementKeys.plan(updatedPlan.id),
updatedPlan
);
// Update current plan if this is the current plan
const today = new Date().toISOString().split('T')[0];
if (updatedPlan.plan_date === today) {
queryClient.setQueryData(
procurementKeys.currentPlan(),
updatedPlan
);
}
// Invalidate lists to ensure they're refreshed
queryClient.invalidateQueries({ queryKey: procurementKeys.plansList() });
queryClient.invalidateQueries({ queryKey: procurementKeys.dashboard() });
},
});
}
/**
* Hook to trigger the daily scheduler manually
*/
export function useTriggerDailyScheduler() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => procurementService.triggerDailyScheduler(),
onSuccess: () => {
// Invalidate all procurement data
queryClient.invalidateQueries({ queryKey: procurementKeys.all });
},
});
}
// ================================================================
// UTILITY HOOKS
// ================================================================
/**
* Hook to check procurement service health
*/
export function useProcurementHealth() {
return useQuery({
queryKey: [...procurementKeys.all, 'health'],
queryFn: () => procurementService.healthCheck(),
staleTime: 60 * 1000, // 1 minute
refetchInterval: 5 * 60 * 1000, // Check every 5 minutes
});
}
// ================================================================
// COMBINED HOOKS
// ================================================================
/**
* Combined hook for procurement plan dashboard
* Fetches current plan, dashboard data, and critical requirements
*/
export function useProcurementPlanDashboard() {
const currentPlan = useCurrentProcurementPlan();
const dashboard = useProcurementDashboard();
const criticalRequirements = useCriticalRequirements();
const health = useProcurementHealth();
return {
currentPlan,
dashboard,
criticalRequirements,
health,
isLoading: currentPlan.isLoading || dashboard.isLoading,
error: currentPlan.error || dashboard.error || criticalRequirements.error,
refetchAll: () => {
currentPlan.refetch();
dashboard.refetch();
criticalRequirements.refetch();
health.refetch();
},
};
}
/**
* Hook for managing procurement plan lifecycle
*/
export function useProcurementPlanActions() {
const generatePlan = useGenerateProcurementPlan();
const updateStatus = useUpdatePlanStatus();
const triggerScheduler = useTriggerDailyScheduler();
return {
generatePlan: generatePlan.mutate,
updateStatus: updateStatus.mutate,
triggerScheduler: triggerScheduler.mutate,
isGenerating: generatePlan.isPending,
isUpdating: updateStatus.isPending,
isTriggering: triggerScheduler.isPending,
generateError: generatePlan.error,
updateError: updateStatus.error,
triggerError: triggerScheduler.error,
};
}