ADD new frontend
This commit is contained in:
655
frontend/src/hooks/business/useProductionSchedule.ts
Normal file
655
frontend/src/hooks/business/useProductionSchedule.ts
Normal file
@@ -0,0 +1,655 @@
|
||||
/**
|
||||
* Production schedule hook for managing bakery production scheduling and capacity planning
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { ProductionService } from '../../services/api/production.service';
|
||||
import { InventoryService } from '../../services/api/inventory.service';
|
||||
import { ForecastingService } from '../../services/api/forecasting.service';
|
||||
|
||||
export interface ScheduleItem {
|
||||
id: string;
|
||||
recipeId: string;
|
||||
recipeName: string;
|
||||
quantity: number;
|
||||
priority: 'low' | 'medium' | 'high' | 'urgent';
|
||||
estimatedStartTime: Date;
|
||||
estimatedEndTime: Date;
|
||||
actualStartTime?: Date;
|
||||
actualEndTime?: Date;
|
||||
status: 'scheduled' | 'in_progress' | 'completed' | 'cancelled' | 'delayed';
|
||||
assignedEquipment?: string[];
|
||||
assignedStaff?: string[];
|
||||
requiredIngredients: {
|
||||
ingredientId: string;
|
||||
ingredientName: string;
|
||||
requiredQuantity: number;
|
||||
availableQuantity: number;
|
||||
unit: string;
|
||||
}[];
|
||||
notes?: string;
|
||||
dependencies?: string[];
|
||||
}
|
||||
|
||||
export interface ProductionSlot {
|
||||
startTime: Date;
|
||||
endTime: Date;
|
||||
isAvailable: boolean;
|
||||
assignedItems: ScheduleItem[];
|
||||
capacity: number;
|
||||
utilizationRate: number;
|
||||
}
|
||||
|
||||
export interface DailySchedule {
|
||||
date: string;
|
||||
items: ScheduleItem[];
|
||||
slots: ProductionSlot[];
|
||||
totalCapacity: number;
|
||||
totalUtilization: number;
|
||||
efficiency: number;
|
||||
bottlenecks: string[];
|
||||
}
|
||||
|
||||
interface ProductionScheduleState {
|
||||
currentSchedule: DailySchedule | null;
|
||||
scheduleHistory: DailySchedule[];
|
||||
availableRecipes: any[];
|
||||
equipmentStatus: any[];
|
||||
staffAvailability: any[];
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
constraints: {
|
||||
maxDailyCapacity: number;
|
||||
workingHours: { start: string; end: string };
|
||||
equipmentLimitations: Record<string, number>;
|
||||
staffLimitations: Record<string, number>;
|
||||
};
|
||||
}
|
||||
|
||||
interface ProductionScheduleActions {
|
||||
// Schedule Management
|
||||
loadSchedule: (date: string) => Promise<void>;
|
||||
createSchedule: (date: string) => Promise<void>;
|
||||
updateSchedule: (schedule: Partial<DailySchedule>) => Promise<boolean>;
|
||||
|
||||
// Schedule Items
|
||||
addScheduleItem: (item: Omit<ScheduleItem, 'id'>) => Promise<boolean>;
|
||||
updateScheduleItem: (id: string, item: Partial<ScheduleItem>) => Promise<boolean>;
|
||||
removeScheduleItem: (id: string) => Promise<boolean>;
|
||||
moveScheduleItem: (id: string, newStartTime: Date) => Promise<boolean>;
|
||||
|
||||
// Automatic Scheduling
|
||||
autoSchedule: (date: string, items: Omit<ScheduleItem, 'id'>[]) => Promise<boolean>;
|
||||
optimizeSchedule: (date: string) => Promise<boolean>;
|
||||
generateFromForecast: (date: string) => Promise<boolean>;
|
||||
|
||||
// Capacity Management
|
||||
checkCapacity: (date: string, newItem: Omit<ScheduleItem, 'id'>) => Promise<{ canSchedule: boolean; suggestedTime?: Date; conflicts?: string[] }>;
|
||||
getAvailableSlots: (date: string, duration: number) => Promise<ProductionSlot[]>;
|
||||
calculateUtilization: (date: string) => Promise<number>;
|
||||
|
||||
// Resource Management
|
||||
checkIngredientAvailability: (items: ScheduleItem[]) => Promise<{ available: boolean; shortages: any[] }>;
|
||||
checkEquipmentAvailability: (date: string, equipment: string[], timeSlot: { start: Date; end: Date }) => Promise<boolean>;
|
||||
checkStaffAvailability: (date: string, staff: string[], timeSlot: { start: Date; end: Date }) => Promise<boolean>;
|
||||
|
||||
// Analytics and Optimization
|
||||
getScheduleAnalytics: (startDate: string, endDate: string) => Promise<any>;
|
||||
getBottleneckAnalysis: (date: string) => Promise<any>;
|
||||
getEfficiencyReport: (period: string) => Promise<any>;
|
||||
predictDelays: (date: string) => Promise<any>;
|
||||
|
||||
// Templates and Presets
|
||||
saveScheduleTemplate: (name: string, template: Omit<ScheduleItem, 'id'>[]) => Promise<boolean>;
|
||||
loadScheduleTemplate: (templateId: string, date: string) => Promise<boolean>;
|
||||
getScheduleTemplates: () => Promise<any[]>;
|
||||
|
||||
// Utilities
|
||||
clearError: () => void;
|
||||
refresh: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const useProductionSchedule = (): ProductionScheduleState & ProductionScheduleActions => {
|
||||
const [state, setState] = useState<ProductionScheduleState>({
|
||||
currentSchedule: null,
|
||||
scheduleHistory: [],
|
||||
availableRecipes: [],
|
||||
equipmentStatus: [],
|
||||
staffAvailability: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
constraints: {
|
||||
maxDailyCapacity: 8 * 60, // 8 hours in minutes
|
||||
workingHours: { start: '06:00', end: '20:00' },
|
||||
equipmentLimitations: {},
|
||||
staffLimitations: {},
|
||||
},
|
||||
});
|
||||
|
||||
const productionService = new ProductionService();
|
||||
const inventoryService = new InventoryService();
|
||||
const forecastingService = new ForecastingService();
|
||||
|
||||
// Load schedule for specific date
|
||||
const loadSchedule = useCallback(async (date: string) => {
|
||||
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
||||
|
||||
try {
|
||||
// Get schedule data from API
|
||||
const scheduleData = await getScheduleFromAPI(date);
|
||||
|
||||
if (scheduleData) {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
currentSchedule: scheduleData,
|
||||
isLoading: false,
|
||||
}));
|
||||
} else {
|
||||
// Create new schedule if none exists
|
||||
await createSchedule(date);
|
||||
}
|
||||
} catch (error) {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isLoading: false,
|
||||
error: 'Error al cargar programación de producción',
|
||||
}));
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Create new schedule
|
||||
const createSchedule = useCallback(async (date: string) => {
|
||||
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
||||
|
||||
try {
|
||||
const workingHours = generateWorkingHours(date, state.constraints.workingHours);
|
||||
const slots = generateTimeSlots(workingHours, 30); // 30-minute slots
|
||||
|
||||
const newSchedule: DailySchedule = {
|
||||
date,
|
||||
items: [],
|
||||
slots,
|
||||
totalCapacity: state.constraints.maxDailyCapacity,
|
||||
totalUtilization: 0,
|
||||
efficiency: 0,
|
||||
bottlenecks: [],
|
||||
};
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
currentSchedule: newSchedule,
|
||||
isLoading: false,
|
||||
}));
|
||||
} catch (error) {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isLoading: false,
|
||||
error: 'Error al crear nueva programación',
|
||||
}));
|
||||
}
|
||||
}, [state.constraints]);
|
||||
|
||||
// Generate working hours for a date
|
||||
const generateWorkingHours = (date: string, workingHours: { start: string; end: string }) => {
|
||||
const startTime = new Date(`${date}T${workingHours.start}`);
|
||||
const endTime = new Date(`${date}T${workingHours.end}`);
|
||||
return { startTime, endTime };
|
||||
};
|
||||
|
||||
// Generate time slots
|
||||
const generateTimeSlots = (workingHours: { startTime: Date; endTime: Date }, slotDuration: number): ProductionSlot[] => {
|
||||
const slots: ProductionSlot[] = [];
|
||||
const current = new Date(workingHours.startTime);
|
||||
|
||||
while (current < workingHours.endTime) {
|
||||
const slotEnd = new Date(current.getTime() + slotDuration * 60000);
|
||||
|
||||
slots.push({
|
||||
startTime: new Date(current),
|
||||
endTime: slotEnd,
|
||||
isAvailable: true,
|
||||
assignedItems: [],
|
||||
capacity: 1,
|
||||
utilizationRate: 0,
|
||||
});
|
||||
|
||||
current.setTime(slotEnd.getTime());
|
||||
}
|
||||
|
||||
return slots;
|
||||
};
|
||||
|
||||
// Add schedule item
|
||||
const addScheduleItem = useCallback(async (item: Omit<ScheduleItem, 'id'>): Promise<boolean> => {
|
||||
if (!state.currentSchedule) return false;
|
||||
|
||||
setState(prev => ({ ...prev, error: null }));
|
||||
|
||||
try {
|
||||
// Check capacity and resources
|
||||
const capacityCheck = await checkCapacity(state.currentSchedule.date, item);
|
||||
|
||||
if (!capacityCheck.canSchedule) {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
error: `No se puede programar: ${capacityCheck.conflicts?.join(', ')}`,
|
||||
}));
|
||||
return false;
|
||||
}
|
||||
|
||||
const newItem: ScheduleItem = {
|
||||
...item,
|
||||
id: generateScheduleItemId(),
|
||||
};
|
||||
|
||||
const updatedSchedule = {
|
||||
...state.currentSchedule,
|
||||
items: [...state.currentSchedule.items, newItem],
|
||||
};
|
||||
|
||||
// Recalculate utilization and efficiency
|
||||
recalculateScheduleMetrics(updatedSchedule);
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
currentSchedule: updatedSchedule,
|
||||
}));
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
setState(prev => ({ ...prev, error: 'Error al agregar item a la programación' }));
|
||||
return false;
|
||||
}
|
||||
}, [state.currentSchedule]);
|
||||
|
||||
// Update schedule item
|
||||
const updateScheduleItem = useCallback(async (id: string, item: Partial<ScheduleItem>): Promise<boolean> => {
|
||||
if (!state.currentSchedule) return false;
|
||||
|
||||
try {
|
||||
const updatedSchedule = {
|
||||
...state.currentSchedule,
|
||||
items: state.currentSchedule.items.map(scheduleItem =>
|
||||
scheduleItem.id === id ? { ...scheduleItem, ...item } : scheduleItem
|
||||
),
|
||||
};
|
||||
|
||||
recalculateScheduleMetrics(updatedSchedule);
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
currentSchedule: updatedSchedule,
|
||||
}));
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
setState(prev => ({ ...prev, error: 'Error al actualizar item de programación' }));
|
||||
return false;
|
||||
}
|
||||
}, [state.currentSchedule]);
|
||||
|
||||
// Remove schedule item
|
||||
const removeScheduleItem = useCallback(async (id: string): Promise<boolean> => {
|
||||
if (!state.currentSchedule) return false;
|
||||
|
||||
try {
|
||||
const updatedSchedule = {
|
||||
...state.currentSchedule,
|
||||
items: state.currentSchedule.items.filter(item => item.id !== id),
|
||||
};
|
||||
|
||||
recalculateScheduleMetrics(updatedSchedule);
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
currentSchedule: updatedSchedule,
|
||||
}));
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
setState(prev => ({ ...prev, error: 'Error al eliminar item de programación' }));
|
||||
return false;
|
||||
}
|
||||
}, [state.currentSchedule]);
|
||||
|
||||
// Move schedule item to new time
|
||||
const moveScheduleItem = useCallback(async (id: string, newStartTime: Date): Promise<boolean> => {
|
||||
if (!state.currentSchedule) return false;
|
||||
|
||||
const item = state.currentSchedule.items.find(item => item.id === id);
|
||||
if (!item) return false;
|
||||
|
||||
const duration = item.estimatedEndTime.getTime() - item.estimatedStartTime.getTime();
|
||||
const newEndTime = new Date(newStartTime.getTime() + duration);
|
||||
|
||||
return updateScheduleItem(id, {
|
||||
estimatedStartTime: newStartTime,
|
||||
estimatedEndTime: newEndTime,
|
||||
});
|
||||
}, [state.currentSchedule, updateScheduleItem]);
|
||||
|
||||
// Auto-schedule items
|
||||
const autoSchedule = useCallback(async (date: string, items: Omit<ScheduleItem, 'id'>[]): Promise<boolean> => {
|
||||
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
||||
|
||||
try {
|
||||
// Sort items by priority and estimated duration
|
||||
const sortedItems = [...items].sort((a, b) => {
|
||||
const priorityOrder = { urgent: 4, high: 3, medium: 2, low: 1 };
|
||||
return priorityOrder[b.priority] - priorityOrder[a.priority];
|
||||
});
|
||||
|
||||
const schedule = await createOptimalSchedule(date, sortedItems);
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
currentSchedule: schedule,
|
||||
isLoading: false,
|
||||
}));
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isLoading: false,
|
||||
error: 'Error al programar automáticamente',
|
||||
}));
|
||||
return false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Create optimal schedule
|
||||
const createOptimalSchedule = async (date: string, items: Omit<ScheduleItem, 'id'>[]): Promise<DailySchedule> => {
|
||||
const workingHours = generateWorkingHours(date, state.constraints.workingHours);
|
||||
const slots = generateTimeSlots(workingHours, 30);
|
||||
|
||||
const scheduledItems: ScheduleItem[] = [];
|
||||
let currentTime = new Date(workingHours.startTime);
|
||||
|
||||
for (const item of items) {
|
||||
const duration = item.estimatedEndTime.getTime() - item.estimatedStartTime.getTime();
|
||||
const endTime = new Date(currentTime.getTime() + duration);
|
||||
|
||||
// Check if item fits in remaining time
|
||||
if (endTime <= workingHours.endTime) {
|
||||
scheduledItems.push({
|
||||
...item,
|
||||
id: generateScheduleItemId(),
|
||||
estimatedStartTime: new Date(currentTime),
|
||||
estimatedEndTime: endTime,
|
||||
});
|
||||
|
||||
currentTime = endTime;
|
||||
}
|
||||
}
|
||||
|
||||
const schedule: DailySchedule = {
|
||||
date,
|
||||
items: scheduledItems,
|
||||
slots,
|
||||
totalCapacity: state.constraints.maxDailyCapacity,
|
||||
totalUtilization: 0,
|
||||
efficiency: 0,
|
||||
bottlenecks: [],
|
||||
};
|
||||
|
||||
recalculateScheduleMetrics(schedule);
|
||||
return schedule;
|
||||
};
|
||||
|
||||
// Check capacity for new item
|
||||
const checkCapacity = useCallback(async (date: string, newItem: Omit<ScheduleItem, 'id'>) => {
|
||||
const conflicts: string[] = [];
|
||||
let canSchedule = true;
|
||||
|
||||
// Check time conflicts
|
||||
if (state.currentSchedule) {
|
||||
const hasTimeConflict = state.currentSchedule.items.some(item => {
|
||||
return (newItem.estimatedStartTime < item.estimatedEndTime &&
|
||||
newItem.estimatedEndTime > item.estimatedStartTime);
|
||||
});
|
||||
|
||||
if (hasTimeConflict) {
|
||||
conflicts.push('Conflicto de horario');
|
||||
canSchedule = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check ingredient availability
|
||||
const ingredientCheck = await checkIngredientAvailability([newItem as ScheduleItem]);
|
||||
if (!ingredientCheck.available) {
|
||||
conflicts.push('Ingredientes insuficientes');
|
||||
canSchedule = false;
|
||||
}
|
||||
|
||||
return { canSchedule, conflicts };
|
||||
}, [state.currentSchedule]);
|
||||
|
||||
// Get available slots
|
||||
const getAvailableSlots = useCallback(async (date: string, duration: number): Promise<ProductionSlot[]> => {
|
||||
if (!state.currentSchedule) return [];
|
||||
|
||||
return state.currentSchedule.slots.filter(slot => {
|
||||
const slotDuration = slot.endTime.getTime() - slot.startTime.getTime();
|
||||
return slot.isAvailable && slotDuration >= duration * 60000;
|
||||
});
|
||||
}, [state.currentSchedule]);
|
||||
|
||||
// Check ingredient availability
|
||||
const checkIngredientAvailability = useCallback(async (items: ScheduleItem[]) => {
|
||||
try {
|
||||
const stockLevels = await inventoryService.getStockLevels();
|
||||
const shortages: any[] = [];
|
||||
|
||||
for (const item of items) {
|
||||
for (const ingredient of item.requiredIngredients) {
|
||||
const stock = stockLevels.data?.find((s: any) => s.ingredient_id === ingredient.ingredientId);
|
||||
if (!stock || stock.current_quantity < ingredient.requiredQuantity) {
|
||||
shortages.push({
|
||||
ingredientName: ingredient.ingredientName,
|
||||
required: ingredient.requiredQuantity,
|
||||
available: stock?.current_quantity || 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { available: shortages.length === 0, shortages };
|
||||
} catch (error) {
|
||||
return { available: false, shortages: [] };
|
||||
}
|
||||
}, [inventoryService]);
|
||||
|
||||
// Generate from forecast
|
||||
const generateFromForecast = useCallback(async (date: string): Promise<boolean> => {
|
||||
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
||||
|
||||
try {
|
||||
// Get forecast data
|
||||
const forecast = await forecastingService.generateDemandForecast('default', 1);
|
||||
|
||||
if (!forecast) {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isLoading: false,
|
||||
error: 'No se pudo obtener predicción de demanda',
|
||||
}));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert forecast to schedule items
|
||||
const items = convertForecastToScheduleItems(forecast);
|
||||
|
||||
// Auto-schedule the items
|
||||
return await autoSchedule(date, items);
|
||||
} catch (error) {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isLoading: false,
|
||||
error: 'Error al generar programación desde predicción',
|
||||
}));
|
||||
return false;
|
||||
}
|
||||
}, [forecastingService, autoSchedule]);
|
||||
|
||||
// Convert forecast to schedule items
|
||||
const convertForecastToScheduleItems = (forecast: any): Omit<ScheduleItem, 'id'>[] => {
|
||||
if (!forecast.products) return [];
|
||||
|
||||
return forecast.products.map((product: any) => ({
|
||||
recipeId: product.recipe_id || `recipe_${product.id}`,
|
||||
recipeName: product.name,
|
||||
quantity: product.estimated_quantity || 1,
|
||||
priority: 'medium' as const,
|
||||
estimatedStartTime: new Date(),
|
||||
estimatedEndTime: new Date(Date.now() + (product.production_time || 60) * 60000),
|
||||
status: 'scheduled' as const,
|
||||
requiredIngredients: product.ingredients || [],
|
||||
}));
|
||||
};
|
||||
|
||||
// Recalculate schedule metrics
|
||||
const recalculateScheduleMetrics = (schedule: DailySchedule) => {
|
||||
const totalScheduledTime = schedule.items.reduce((total, item) => {
|
||||
return total + (item.estimatedEndTime.getTime() - item.estimatedStartTime.getTime());
|
||||
}, 0);
|
||||
|
||||
schedule.totalUtilization = (totalScheduledTime / (schedule.totalCapacity * 60000)) * 100;
|
||||
schedule.efficiency = calculateEfficiency(schedule.items);
|
||||
schedule.bottlenecks = identifyBottlenecks(schedule.items);
|
||||
};
|
||||
|
||||
// Calculate efficiency
|
||||
const calculateEfficiency = (items: ScheduleItem[]): number => {
|
||||
if (items.length === 0) return 0;
|
||||
|
||||
const completedItems = items.filter(item => item.status === 'completed');
|
||||
return (completedItems.length / items.length) * 100;
|
||||
};
|
||||
|
||||
// Identify bottlenecks
|
||||
const identifyBottlenecks = (items: ScheduleItem[]): string[] => {
|
||||
const bottlenecks: string[] = [];
|
||||
|
||||
// Check for equipment conflicts
|
||||
const equipmentUsage: Record<string, number> = {};
|
||||
items.forEach(item => {
|
||||
item.assignedEquipment?.forEach(equipment => {
|
||||
equipmentUsage[equipment] = (equipmentUsage[equipment] || 0) + 1;
|
||||
});
|
||||
});
|
||||
|
||||
Object.entries(equipmentUsage).forEach(([equipment, usage]) => {
|
||||
if (usage > 1) {
|
||||
bottlenecks.push(`Conflicto de equipamiento: ${equipment}`);
|
||||
}
|
||||
});
|
||||
|
||||
return bottlenecks;
|
||||
};
|
||||
|
||||
// Generate unique ID
|
||||
const generateScheduleItemId = (): string => {
|
||||
return `schedule_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
};
|
||||
|
||||
// Get schedule from API (placeholder)
|
||||
const getScheduleFromAPI = async (date: string): Promise<DailySchedule | null> => {
|
||||
// This would fetch from actual API
|
||||
return null;
|
||||
};
|
||||
|
||||
// Placeholder implementations for remaining functions
|
||||
const updateSchedule = useCallback(async (schedule: Partial<DailySchedule>): Promise<boolean> => {
|
||||
return true;
|
||||
}, []);
|
||||
|
||||
const optimizeSchedule = useCallback(async (date: string): Promise<boolean> => {
|
||||
return true;
|
||||
}, []);
|
||||
|
||||
const calculateUtilization = useCallback(async (date: string): Promise<number> => {
|
||||
return state.currentSchedule?.totalUtilization || 0;
|
||||
}, [state.currentSchedule]);
|
||||
|
||||
const checkEquipmentAvailability = useCallback(async (date: string, equipment: string[], timeSlot: { start: Date; end: Date }): Promise<boolean> => {
|
||||
return true;
|
||||
}, []);
|
||||
|
||||
const checkStaffAvailability = useCallback(async (date: string, staff: string[], timeSlot: { start: Date; end: Date }): Promise<boolean> => {
|
||||
return true;
|
||||
}, []);
|
||||
|
||||
const getScheduleAnalytics = useCallback(async (startDate: string, endDate: string): Promise<any> => {
|
||||
return {};
|
||||
}, []);
|
||||
|
||||
const getBottleneckAnalysis = useCallback(async (date: string): Promise<any> => {
|
||||
return {};
|
||||
}, []);
|
||||
|
||||
const getEfficiencyReport = useCallback(async (period: string): Promise<any> => {
|
||||
return {};
|
||||
}, []);
|
||||
|
||||
const predictDelays = useCallback(async (date: string): Promise<any> => {
|
||||
return {};
|
||||
}, []);
|
||||
|
||||
const saveScheduleTemplate = useCallback(async (name: string, template: Omit<ScheduleItem, 'id'>[]): Promise<boolean> => {
|
||||
return true;
|
||||
}, []);
|
||||
|
||||
const loadScheduleTemplate = useCallback(async (templateId: string, date: string): Promise<boolean> => {
|
||||
return true;
|
||||
}, []);
|
||||
|
||||
const getScheduleTemplates = useCallback(async (): Promise<any[]> => {
|
||||
return [];
|
||||
}, []);
|
||||
|
||||
const clearError = useCallback(() => {
|
||||
setState(prev => ({ ...prev, error: null }));
|
||||
}, []);
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
if (state.currentSchedule) {
|
||||
await loadSchedule(state.currentSchedule.date);
|
||||
}
|
||||
}, [state.currentSchedule, loadSchedule]);
|
||||
|
||||
// Load today's schedule on mount
|
||||
useEffect(() => {
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
loadSchedule(today);
|
||||
}, [loadSchedule]);
|
||||
|
||||
return {
|
||||
...state,
|
||||
loadSchedule,
|
||||
createSchedule,
|
||||
updateSchedule,
|
||||
addScheduleItem,
|
||||
updateScheduleItem,
|
||||
removeScheduleItem,
|
||||
moveScheduleItem,
|
||||
autoSchedule,
|
||||
optimizeSchedule,
|
||||
generateFromForecast,
|
||||
checkCapacity,
|
||||
getAvailableSlots,
|
||||
calculateUtilization,
|
||||
checkIngredientAvailability,
|
||||
checkEquipmentAvailability,
|
||||
checkStaffAvailability,
|
||||
getScheduleAnalytics,
|
||||
getBottleneckAnalysis,
|
||||
getEfficiencyReport,
|
||||
predictDelays,
|
||||
saveScheduleTemplate,
|
||||
loadScheduleTemplate,
|
||||
getScheduleTemplates,
|
||||
clearError,
|
||||
refresh,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user