2025-09-23 19:24:22 +02:00
|
|
|
import React, { useState, useMemo } from 'react';
|
|
|
|
|
import { useTranslation } from 'react-i18next';
|
2025-09-23 22:11:34 +02:00
|
|
|
import { Plus, AlertTriangle, Settings, CheckCircle, Eye, Wrench, Thermometer, Activity, Search, Filter, Bell, History, Calendar, Edit, Trash2 } from 'lucide-react';
|
2025-10-27 16:33:26 +01:00
|
|
|
import { Button, StatsGrid, StatusCard, getStatusColor, SearchAndFilter, type FilterConfig, EmptyState } from '../../../../components/ui';
|
2025-09-23 22:11:34 +02:00
|
|
|
import { Badge } from '../../../../components/ui/Badge';
|
2025-09-26 07:46:25 +02:00
|
|
|
import { LoadingSpinner } from '../../../../components/ui';
|
2025-09-23 19:24:22 +02:00
|
|
|
import { PageHeader } from '../../../../components/layout';
|
|
|
|
|
import { useCurrentTenant } from '../../../../stores/tenant.store';
|
2025-09-26 07:46:25 +02:00
|
|
|
import { Equipment } from '../../../../api/types/equipment';
|
2025-09-24 15:58:18 +02:00
|
|
|
import { EquipmentModal } from '../../../../components/domain/equipment/EquipmentModal';
|
2025-10-29 06:58:05 +01:00
|
|
|
import { DeleteEquipmentModal } from '../../../../components/domain/equipment/DeleteEquipmentModal';
|
|
|
|
|
import { MaintenanceHistoryModal } from '../../../../components/domain/equipment/MaintenanceHistoryModal';
|
|
|
|
|
import { ScheduleMaintenanceModal, type MaintenanceScheduleData } from '../../../../components/domain/equipment/ScheduleMaintenanceModal';
|
|
|
|
|
import { useEquipment, useCreateEquipment, useUpdateEquipment, useDeleteEquipment, useHardDeleteEquipment } from '../../../../api/hooks/equipment';
|
2025-09-23 19:24:22 +02:00
|
|
|
|
|
|
|
|
const MaquinariaPage: React.FC = () => {
|
|
|
|
|
const { t } = useTranslation(['equipment', 'common']);
|
|
|
|
|
const [searchTerm, setSearchTerm] = useState('');
|
2025-09-26 12:12:17 +02:00
|
|
|
const [statusFilter, setStatusFilter] = useState('');
|
|
|
|
|
const [typeFilter, setTypeFilter] = useState('');
|
2025-09-24 15:58:18 +02:00
|
|
|
const [showEquipmentModal, setShowEquipmentModal] = useState(false);
|
|
|
|
|
const [equipmentModalMode, setEquipmentModalMode] = useState<'view' | 'edit' | 'create'>('create');
|
|
|
|
|
const [selectedEquipment, setSelectedEquipment] = useState<Equipment | null>(null);
|
2025-10-19 19:22:37 +02:00
|
|
|
|
2025-10-29 06:58:05 +01:00
|
|
|
// New modal states
|
|
|
|
|
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
|
|
|
|
const [showHistoryModal, setShowHistoryModal] = useState(false);
|
|
|
|
|
const [showScheduleModal, setShowScheduleModal] = useState(false);
|
|
|
|
|
const [equipmentForAction, setEquipmentForAction] = useState<Equipment | null>(null);
|
|
|
|
|
|
2025-09-23 19:24:22 +02:00
|
|
|
const currentTenant = useCurrentTenant();
|
|
|
|
|
const tenantId = currentTenant?.id || '';
|
|
|
|
|
|
2025-10-19 19:22:37 +02:00
|
|
|
// Fetch equipment data from API
|
|
|
|
|
const { data: equipment = [], isLoading, error } = useEquipment(tenantId, {
|
|
|
|
|
is_active: true
|
|
|
|
|
});
|
|
|
|
|
|
2025-10-29 06:58:05 +01:00
|
|
|
// Mutations for create, update, and delete
|
2025-10-19 19:22:37 +02:00
|
|
|
const createEquipmentMutation = useCreateEquipment(tenantId);
|
|
|
|
|
const updateEquipmentMutation = useUpdateEquipment(tenantId);
|
2025-10-29 06:58:05 +01:00
|
|
|
const deleteEquipmentMutation = useDeleteEquipment(tenantId);
|
|
|
|
|
const hardDeleteEquipmentMutation = useHardDeleteEquipment(tenantId);
|
2025-10-19 19:22:37 +02:00
|
|
|
|
2025-09-23 19:24:22 +02:00
|
|
|
const handleCreateEquipment = () => {
|
2025-09-24 15:58:18 +02:00
|
|
|
setSelectedEquipment({
|
|
|
|
|
id: '',
|
|
|
|
|
name: '',
|
|
|
|
|
type: 'other',
|
|
|
|
|
model: '',
|
|
|
|
|
serialNumber: '',
|
|
|
|
|
location: '',
|
|
|
|
|
status: 'operational',
|
|
|
|
|
installDate: new Date().toISOString().split('T')[0],
|
|
|
|
|
lastMaintenance: new Date().toISOString().split('T')[0],
|
|
|
|
|
nextMaintenance: new Date().toISOString().split('T')[0],
|
|
|
|
|
maintenanceInterval: 30,
|
|
|
|
|
efficiency: 100,
|
|
|
|
|
uptime: 100,
|
|
|
|
|
energyUsage: 0,
|
|
|
|
|
utilizationToday: 0,
|
|
|
|
|
alerts: [],
|
|
|
|
|
maintenanceHistory: [],
|
|
|
|
|
specifications: {
|
|
|
|
|
power: 0,
|
|
|
|
|
capacity: 0,
|
|
|
|
|
dimensions: { width: 0, height: 0, depth: 0 },
|
|
|
|
|
weight: 0
|
|
|
|
|
}
|
|
|
|
|
} as Equipment);
|
|
|
|
|
setEquipmentModalMode('create');
|
|
|
|
|
setShowEquipmentModal(true);
|
2025-09-23 19:24:22 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleEditEquipment = (equipmentId: string) => {
|
2025-10-19 19:22:37 +02:00
|
|
|
// Find the equipment to edit from real data
|
|
|
|
|
const equipmentToEdit = equipment.find(eq => eq.id === equipmentId);
|
2025-09-24 15:58:18 +02:00
|
|
|
if (equipmentToEdit) {
|
|
|
|
|
setSelectedEquipment(equipmentToEdit);
|
|
|
|
|
setEquipmentModalMode('edit');
|
|
|
|
|
setShowEquipmentModal(true);
|
|
|
|
|
}
|
2025-09-23 19:24:22 +02:00
|
|
|
};
|
|
|
|
|
|
2025-10-29 06:58:05 +01:00
|
|
|
const handleScheduleMaintenance = (equipment: Equipment) => {
|
|
|
|
|
setEquipmentForAction(equipment);
|
|
|
|
|
setShowScheduleModal(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleScheduleMaintenanceSubmit = async (equipmentId: string, maintenanceData: MaintenanceScheduleData) => {
|
|
|
|
|
try {
|
|
|
|
|
// Update next maintenance date based on scheduled date
|
|
|
|
|
await updateEquipmentMutation.mutateAsync({
|
|
|
|
|
equipmentId: equipmentId,
|
|
|
|
|
equipmentData: {
|
|
|
|
|
nextMaintenance: maintenanceData.scheduledDate
|
|
|
|
|
} as Partial<Equipment>
|
|
|
|
|
});
|
|
|
|
|
setShowScheduleModal(false);
|
|
|
|
|
setEquipmentForAction(null);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error scheduling maintenance:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
2025-09-23 19:24:22 +02:00
|
|
|
};
|
|
|
|
|
|
2025-10-29 06:58:05 +01:00
|
|
|
const handleViewMaintenanceHistory = (equipment: Equipment) => {
|
|
|
|
|
setEquipmentForAction(equipment);
|
|
|
|
|
setShowHistoryModal(true);
|
2025-09-23 19:24:22 +02:00
|
|
|
};
|
|
|
|
|
|
2025-10-29 06:58:05 +01:00
|
|
|
const handleDeleteEquipment = (equipment: Equipment) => {
|
|
|
|
|
setEquipmentForAction(equipment);
|
|
|
|
|
setShowDeleteModal(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleSoftDelete = async (equipmentId: string) => {
|
|
|
|
|
try {
|
|
|
|
|
await deleteEquipmentMutation.mutateAsync(equipmentId);
|
|
|
|
|
setShowDeleteModal(false);
|
|
|
|
|
setEquipmentForAction(null);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error deleting equipment:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleHardDelete = async (equipmentId: string) => {
|
|
|
|
|
try {
|
|
|
|
|
await hardDeleteEquipmentMutation.mutateAsync(equipmentId);
|
|
|
|
|
setShowDeleteModal(false);
|
|
|
|
|
setEquipmentForAction(null);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error hard deleting equipment:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
2025-09-23 19:24:22 +02:00
|
|
|
};
|
|
|
|
|
|
2025-10-19 19:22:37 +02:00
|
|
|
const handleSaveEquipment = async (equipmentData: Equipment) => {
|
|
|
|
|
try {
|
|
|
|
|
if (equipmentModalMode === 'create') {
|
|
|
|
|
await createEquipmentMutation.mutateAsync(equipmentData);
|
|
|
|
|
} else if (equipmentModalMode === 'edit' && equipmentData.id) {
|
|
|
|
|
await updateEquipmentMutation.mutateAsync({
|
|
|
|
|
equipmentId: equipmentData.id,
|
|
|
|
|
equipmentData: equipmentData
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
setShowEquipmentModal(false);
|
|
|
|
|
setSelectedEquipment(null);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error saving equipment:', error);
|
|
|
|
|
// Error is already handled by mutation with toast
|
|
|
|
|
}
|
2025-09-24 15:58:18 +02:00
|
|
|
};
|
|
|
|
|
|
2025-09-23 19:24:22 +02:00
|
|
|
const filteredEquipment = useMemo(() => {
|
2025-10-19 19:22:37 +02:00
|
|
|
return equipment.filter(eq => {
|
2025-09-23 19:24:22 +02:00
|
|
|
const matchesSearch = !searchTerm ||
|
|
|
|
|
eq.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
|
|
|
eq.location.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
|
|
|
eq.type.toLowerCase().includes(searchTerm.toLowerCase());
|
|
|
|
|
|
2025-09-26 12:12:17 +02:00
|
|
|
const matchesStatus = !statusFilter || eq.status === statusFilter;
|
|
|
|
|
const matchesType = !typeFilter || eq.type === typeFilter;
|
2025-09-23 19:24:22 +02:00
|
|
|
|
2025-09-26 12:12:17 +02:00
|
|
|
return matchesSearch && matchesStatus && matchesType;
|
2025-09-23 19:24:22 +02:00
|
|
|
});
|
2025-10-19 19:22:37 +02:00
|
|
|
}, [equipment, searchTerm, statusFilter, typeFilter]);
|
2025-09-23 19:24:22 +02:00
|
|
|
|
|
|
|
|
const equipmentStats = useMemo(() => {
|
2025-10-19 19:22:37 +02:00
|
|
|
const total = equipment.length;
|
|
|
|
|
const operational = equipment.filter(e => e.status === 'operational').length;
|
|
|
|
|
const warning = equipment.filter(e => e.status === 'warning').length;
|
|
|
|
|
const maintenance = equipment.filter(e => e.status === 'maintenance').length;
|
|
|
|
|
const down = equipment.filter(e => e.status === 'down').length;
|
|
|
|
|
const totalAlerts = equipment.reduce((sum, e) => sum + e.alerts.filter(a => !a.acknowledged).length, 0);
|
2025-09-23 19:24:22 +02:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
total,
|
|
|
|
|
operational,
|
|
|
|
|
warning,
|
|
|
|
|
maintenance,
|
|
|
|
|
down,
|
|
|
|
|
totalAlerts
|
|
|
|
|
};
|
2025-10-19 19:22:37 +02:00
|
|
|
}, [equipment]);
|
2025-09-23 19:24:22 +02:00
|
|
|
|
|
|
|
|
const getStatusConfig = (status: Equipment['status']) => {
|
|
|
|
|
const configs = {
|
|
|
|
|
operational: { color: getStatusColor('completed'), text: t('equipment_status.operational'), icon: CheckCircle },
|
|
|
|
|
warning: { color: getStatusColor('warning'), text: t('equipment_status.warning'), icon: AlertTriangle },
|
|
|
|
|
maintenance: { color: getStatusColor('info'), text: t('equipment_status.maintenance'), icon: Wrench },
|
|
|
|
|
down: { color: getStatusColor('error'), text: t('equipment_status.down'), icon: AlertTriangle }
|
|
|
|
|
};
|
2025-12-13 23:57:54 +01:00
|
|
|
return configs[status] || { color: getStatusColor('other'), text: status, icon: Settings };
|
2025-09-23 19:24:22 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getTypeIcon = (type: Equipment['type']) => {
|
|
|
|
|
const icons = {
|
|
|
|
|
oven: Thermometer,
|
|
|
|
|
mixer: Activity,
|
|
|
|
|
proofer: Settings,
|
2025-09-23 22:11:34 +02:00
|
|
|
freezer: Settings,
|
2025-09-23 19:24:22 +02:00
|
|
|
packaging: Settings,
|
|
|
|
|
other: Settings
|
|
|
|
|
};
|
|
|
|
|
return icons[type];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const stats = [
|
|
|
|
|
{
|
|
|
|
|
title: t('labels.total_equipment'),
|
|
|
|
|
value: equipmentStats.total,
|
2025-10-27 16:33:26 +01:00
|
|
|
variant: 'default' as const,
|
2025-09-23 19:24:22 +02:00
|
|
|
icon: Settings,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: t('labels.operational'),
|
|
|
|
|
value: equipmentStats.operational,
|
|
|
|
|
variant: 'success' as const,
|
2025-10-27 16:33:26 +01:00
|
|
|
icon: CheckCircle,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: t('labels.warning'),
|
|
|
|
|
value: equipmentStats.warning,
|
|
|
|
|
variant: 'warning' as const,
|
|
|
|
|
icon: AlertTriangle,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: t('labels.maintenance_required'),
|
|
|
|
|
value: equipmentStats.maintenance,
|
|
|
|
|
variant: 'info' as const,
|
|
|
|
|
icon: Wrench,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: t('labels.down'),
|
|
|
|
|
value: equipmentStats.down,
|
|
|
|
|
variant: 'error' as const,
|
|
|
|
|
icon: AlertTriangle,
|
2025-09-23 19:24:22 +02:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: t('labels.active_alerts'),
|
|
|
|
|
value: equipmentStats.totalAlerts,
|
2025-10-27 16:33:26 +01:00
|
|
|
variant: equipmentStats.totalAlerts === 0 ? 'success' as const : 'error' as const,
|
2025-09-23 19:24:22 +02:00
|
|
|
icon: Bell,
|
2025-10-27 16:33:26 +01:00
|
|
|
},
|
2025-09-23 19:24:22 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const handleShowMaintenanceDetails = (equipment: Equipment) => {
|
2025-10-29 06:58:05 +01:00
|
|
|
setSelectedEquipment(equipment);
|
|
|
|
|
setEquipmentModalMode('view');
|
|
|
|
|
setShowEquipmentModal(true);
|
2025-09-23 19:24:22 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Loading state
|
|
|
|
|
if (!tenantId) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex items-center justify-center min-h-64">
|
|
|
|
|
<LoadingSpinner text="Cargando datos..." />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-19 19:22:37 +02:00
|
|
|
if (isLoading) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex items-center justify-center min-h-64">
|
|
|
|
|
<LoadingSpinner text={t('common:loading')} />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex flex-col items-center justify-center min-h-64">
|
|
|
|
|
<AlertTriangle className="w-12 h-12 text-red-500 mb-4" />
|
|
|
|
|
<h3 className="text-lg font-medium text-[var(--text-primary)] mb-2">
|
|
|
|
|
{t('common:errors.load_error')}
|
|
|
|
|
</h3>
|
|
|
|
|
<p className="text-[var(--text-secondary)]">
|
|
|
|
|
{t('common:errors.try_again')}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-23 19:24:22 +02:00
|
|
|
return (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
<PageHeader
|
|
|
|
|
title={t('title')}
|
|
|
|
|
description={t('subtitle')}
|
|
|
|
|
actions={[
|
|
|
|
|
{
|
|
|
|
|
id: "add-new-equipment",
|
|
|
|
|
label: t('actions.add_equipment'),
|
|
|
|
|
variant: "primary" as const,
|
|
|
|
|
icon: Plus,
|
|
|
|
|
onClick: handleCreateEquipment,
|
|
|
|
|
tooltip: t('actions.add_equipment'),
|
|
|
|
|
size: "md"
|
|
|
|
|
}
|
|
|
|
|
]}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
{/* Stats Grid */}
|
|
|
|
|
<StatsGrid
|
|
|
|
|
stats={stats}
|
|
|
|
|
columns={3}
|
|
|
|
|
/>
|
|
|
|
|
|
2025-09-26 12:12:17 +02:00
|
|
|
{/* Search and Filter Controls */}
|
|
|
|
|
<SearchAndFilter
|
|
|
|
|
searchValue={searchTerm}
|
|
|
|
|
onSearchChange={setSearchTerm}
|
|
|
|
|
searchPlaceholder={t('common:forms.search_placeholder')}
|
|
|
|
|
filters={[
|
|
|
|
|
{
|
|
|
|
|
key: 'status',
|
|
|
|
|
label: t('fields.status'),
|
|
|
|
|
type: 'dropdown',
|
|
|
|
|
value: statusFilter,
|
|
|
|
|
onChange: (value) => setStatusFilter(value as string),
|
|
|
|
|
placeholder: t('common:forms.select_option'),
|
|
|
|
|
options: [
|
|
|
|
|
{ value: 'operational', label: t('equipment_status.operational') },
|
|
|
|
|
{ value: 'warning', label: t('equipment_status.warning') },
|
|
|
|
|
{ value: 'maintenance', label: t('equipment_status.maintenance') },
|
|
|
|
|
{ value: 'down', label: t('equipment_status.down') }
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'type',
|
|
|
|
|
label: 'Tipo',
|
|
|
|
|
type: 'dropdown',
|
|
|
|
|
value: typeFilter,
|
|
|
|
|
onChange: (value) => setTypeFilter(value as string),
|
|
|
|
|
placeholder: 'Todos los tipos',
|
|
|
|
|
options: [
|
|
|
|
|
{ value: 'oven', label: 'Horno' },
|
|
|
|
|
{ value: 'mixer', label: 'Batidora' },
|
|
|
|
|
{ value: 'proofer', label: 'Fermentadora' },
|
|
|
|
|
{ value: 'freezer', label: 'Congelador' },
|
|
|
|
|
{ value: 'packaging', label: 'Empaquetado' },
|
|
|
|
|
{ value: 'other', label: 'Otro' }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
] as FilterConfig[]}
|
|
|
|
|
/>
|
2025-09-23 19:24:22 +02:00
|
|
|
|
|
|
|
|
{/* Equipment Grid */}
|
|
|
|
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
|
|
|
{filteredEquipment.map((equipment) => {
|
|
|
|
|
const statusConfig = getStatusConfig(equipment.status);
|
|
|
|
|
const TypeIcon = getTypeIcon(equipment.type);
|
|
|
|
|
|
|
|
|
|
// Calculate maintenance status
|
|
|
|
|
const nextMaintenanceDate = new Date(equipment.nextMaintenance);
|
|
|
|
|
const daysUntilMaintenance = Math.ceil((nextMaintenanceDate.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24));
|
|
|
|
|
const isOverdue = daysUntilMaintenance < 0;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<StatusCard
|
|
|
|
|
key={equipment.id}
|
|
|
|
|
id={equipment.id}
|
|
|
|
|
statusIndicator={statusConfig}
|
|
|
|
|
title={equipment.name}
|
|
|
|
|
subtitle={equipment.location}
|
|
|
|
|
primaryValue={`${equipment.efficiency}%`}
|
|
|
|
|
primaryValueLabel={t('fields.efficiency')}
|
|
|
|
|
secondaryInfo={{
|
|
|
|
|
label: t('fields.uptime'),
|
|
|
|
|
value: `${equipment.uptime.toFixed(1)}%`
|
|
|
|
|
}}
|
2025-09-26 12:12:17 +02:00
|
|
|
onClick={() => handleShowMaintenanceDetails(equipment)}
|
2025-09-23 19:24:22 +02:00
|
|
|
actions={[
|
|
|
|
|
{
|
|
|
|
|
label: t('actions.view_details'),
|
|
|
|
|
icon: Eye,
|
|
|
|
|
variant: 'primary',
|
|
|
|
|
priority: 'primary',
|
|
|
|
|
onClick: () => handleShowMaintenanceDetails(equipment)
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: t('actions.view_history'),
|
|
|
|
|
icon: History,
|
|
|
|
|
priority: 'secondary',
|
2025-10-29 06:58:05 +01:00
|
|
|
onClick: () => handleViewMaintenanceHistory(equipment)
|
2025-09-23 19:24:22 +02:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: t('actions.schedule_maintenance'),
|
|
|
|
|
icon: Wrench,
|
|
|
|
|
priority: 'secondary',
|
2025-10-29 06:58:05 +01:00
|
|
|
highlighted: true,
|
|
|
|
|
onClick: () => handleScheduleMaintenance(equipment)
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: t('actions.delete'),
|
|
|
|
|
icon: Trash2,
|
|
|
|
|
priority: 'secondary',
|
|
|
|
|
destructive: true,
|
|
|
|
|
onClick: () => handleDeleteEquipment(equipment)
|
2025-09-23 19:24:22 +02:00
|
|
|
}
|
|
|
|
|
]}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Empty State */}
|
|
|
|
|
{filteredEquipment.length === 0 && (
|
2025-10-27 16:33:26 +01:00
|
|
|
<EmptyState
|
|
|
|
|
icon={Settings}
|
|
|
|
|
title={t('common:forms.no_results')}
|
|
|
|
|
description={t('common:forms.empty_state')}
|
|
|
|
|
actionLabel={t('actions.add_equipment')}
|
|
|
|
|
actionIcon={Plus}
|
|
|
|
|
onAction={handleCreateEquipment}
|
|
|
|
|
/>
|
2025-09-23 19:24:22 +02:00
|
|
|
)}
|
|
|
|
|
|
2025-10-29 06:58:05 +01:00
|
|
|
{/* Equipment Modal - Used for View Details, Edit, and Create */}
|
2025-09-24 15:58:18 +02:00
|
|
|
{showEquipmentModal && (
|
|
|
|
|
<EquipmentModal
|
|
|
|
|
isOpen={showEquipmentModal}
|
|
|
|
|
onClose={() => {
|
|
|
|
|
setShowEquipmentModal(false);
|
|
|
|
|
setSelectedEquipment(null);
|
|
|
|
|
}}
|
|
|
|
|
equipment={selectedEquipment}
|
|
|
|
|
onSave={handleSaveEquipment}
|
|
|
|
|
mode={equipmentModalMode}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2025-10-29 06:58:05 +01:00
|
|
|
|
|
|
|
|
{/* Delete Equipment Modal */}
|
|
|
|
|
{showDeleteModal && equipmentForAction && (
|
|
|
|
|
<DeleteEquipmentModal
|
|
|
|
|
isOpen={showDeleteModal}
|
|
|
|
|
onClose={() => {
|
|
|
|
|
setShowDeleteModal(false);
|
|
|
|
|
setEquipmentForAction(null);
|
|
|
|
|
}}
|
|
|
|
|
equipment={equipmentForAction}
|
|
|
|
|
onSoftDelete={handleSoftDelete}
|
|
|
|
|
onHardDelete={handleHardDelete}
|
|
|
|
|
isLoading={deleteEquipmentMutation.isPending || hardDeleteEquipmentMutation.isPending}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Maintenance History Modal */}
|
|
|
|
|
{showHistoryModal && equipmentForAction && (
|
|
|
|
|
<MaintenanceHistoryModal
|
|
|
|
|
isOpen={showHistoryModal}
|
|
|
|
|
onClose={() => {
|
|
|
|
|
setShowHistoryModal(false);
|
|
|
|
|
setEquipmentForAction(null);
|
|
|
|
|
}}
|
|
|
|
|
equipment={equipmentForAction}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Schedule Maintenance Modal */}
|
|
|
|
|
{showScheduleModal && equipmentForAction && (
|
|
|
|
|
<ScheduleMaintenanceModal
|
|
|
|
|
isOpen={showScheduleModal}
|
|
|
|
|
onClose={() => {
|
|
|
|
|
setShowScheduleModal(false);
|
|
|
|
|
setEquipmentForAction(null);
|
|
|
|
|
}}
|
|
|
|
|
equipment={equipmentForAction}
|
|
|
|
|
onSchedule={handleScheduleMaintenanceSubmit}
|
|
|
|
|
isLoading={updateEquipmentMutation.isPending}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2025-09-23 19:24:22 +02:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-27 16:33:26 +01:00
|
|
|
export default MaquinariaPage;
|