import React, { useState, useEffect, useCallback } from 'react';
import { useParams, useNavigate, useSearchParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useMaintenanceLogDetails, useMaintenanceMutations } from '../hooks/useAssetMaintenance';
import { FaArrowLeft, FaSave, FaEdit, FaClock, FaList, FaPlus, FaTrash, FaCheck, FaTimes, FaExclamationTriangle } from 'react-icons/fa';
import apiService from '../services/apiService';
import WorkflowActions from '../components/WorkflowActions';
import useDefaultHospital from '../hooks/useDefaultHospital';
// Cast apiService to any to bypass strict typing
const api = apiService as any;
// PPM Table Row Interface
interface PPMTableRow {
name?: string;
idx?: number;
maintenance_name: string;
working: number | boolean;
defect_found: number | boolean;
not_working: number | boolean;
}
// Updated Interface with all fields from JSON
interface MaintenanceLogData {
name?: string;
owner?: string;
creation?: string;
modified?: string;
modified_by?: string;
docstatus?: number;
idx?: number;
workflow_state?: string;
asset_maintenance?: string;
naming_series?: string;
asset_name: string;
custom_asset_type?: string;
item_code?: string;
item_name?: string;
custom_asset_names?: string;
custom_hospital_name?: string;
task?: string;
task_name?: string;
maintenance_type: string;
periodicity?: string;
has_certificate?: number | boolean;
custom_early_completion?: string;
maintenance_status: string;
custom_accepted_by_moh?: number | boolean;
assign_to_name?: string;
due_date?: string;
completion_date?: string;
custom_early_completion_reason?: string;
custom_accepted_by_moh_?: number | boolean;
custom_template?: string;
custom_table?: PPMTableRow[];
description?: string;
}
// Toast notification component
const Toast: React.FC<{ message: string; type: 'warning' | 'success' | 'error'; onClose: () => void }> = ({ message, type, onClose }) => {
useEffect(() => {
const timer = setTimeout(onClose, 10000);
return () => clearTimeout(timer);
}, [onClose]);
const bgColor = type === 'warning' ? 'bg-yellow-500' : type === 'success' ? 'bg-green-500' : 'bg-red-500';
return (
);
};
const AssetMaintenanceDetail: React.FC = () => {
const { t } = useTranslation();
const { logName } = useParams<{ logName: string }>();
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const duplicateFromLog = searchParams.get('duplicate');
const isNewLog = logName === 'new';
const isDuplicating = isNewLog && !!duplicateFromLog;
const { log, loading, error } = useMaintenanceLogDetails(
isDuplicating ? duplicateFromLog : (isNewLog ? null : logName || null)
);
const { createLog, updateLog, loading: saving } = useMaintenanceMutations();
// Check if document is approved (not editable)
const isApproved = log?.workflow_state === 'Approved';
const [isEditing, setIsEditing] = useState(isNewLog);
const [ppmTableRows, setPpmTableRows] = useState([]);
const [toast, setToast] = useState<{ message: string; type: 'warning' | 'success' | 'error' } | null>(null);
const [templateLoading, setTemplateLoading] = useState(false);
const [formData, setFormData] = useState({
asset_name: '',
custom_asset_type: '',
item_code: '',
item_name: '',
custom_asset_names: '',
custom_hospital_name: '',
task: '',
task_name: '',
maintenance_type: 'Preventive Maintenance',
periodicity: '',
has_certificate: 0,
custom_early_completion: '',
maintenance_status: 'Planned',
custom_accepted_by_moh: 0,
assign_to_name: '',
due_date: '',
completion_date: '',
custom_early_completion_reason: '',
custom_accepted_by_moh_: 0,
custom_template: '',
custom_table: [],
description: '',
});
useDefaultHospital(setFormData, {
enabled: isNewLog && !isDuplicating,
fields: ['custom_hospital_name'],
});
// Check early completion logic
const checkEarlyCompletion = useCallback((dueDate: string, completionDate: string, maintenanceStatus: string) => {
if (maintenanceStatus !== 'Completed') {
setFormData(prev => ({ ...prev, custom_early_completion: '' }));
return;
}
if (!dueDate || !completionDate) {
return;
}
const dueDateObj = new Date(dueDate);
const completionDateObj = new Date(completionDate);
const dueMonthStart = new Date(dueDateObj.getFullYear(), dueDateObj.getMonth(), 1);
if (completionDateObj < dueMonthStart) {
setFormData(prev => ({ ...prev, custom_early_completion: 'Yes' }));
const formattedCompletionDate = completionDateObj.toLocaleDateString();
const formattedDueDate = dueDateObj.toLocaleDateString();
setToast({
message: `⚠️ Warning: Completion Date (${formattedCompletionDate}) is before the Due Date Month (${formattedDueDate}). Please verify.`,
type: 'warning'
});
}
}, []);
// Load PPM Template
const loadPPMTemplate = useCallback(async (templateName: string) => {
if (!templateName) return;
setTemplateLoading(true);
try {
const response = await api.apiCall(
`/api/resource/PPM Templates/${encodeURIComponent(templateName)}`,
'GET'
);
if (response?.data?.ppm_template_table && Array.isArray(response.data.ppm_template_table)) {
const templateRows: PPMTableRow[] = response.data.ppm_template_table.map((row: any, index: number) => ({
idx: index + 1,
maintenance_name: row.maintenance_name || '',
working: 0,
defect_found: 0,
not_working: 0,
}));
setPpmTableRows(templateRows);
}
} catch (err) {
console.error('Error loading PPM template:', err);
setToast({
message: 'Failed to load PPM template. Please add items manually.',
type: 'error'
});
} finally {
setTemplateLoading(false);
}
}, []);
// Load data when log is loaded
useEffect(() => {
if (log) {
// If document is approved, ensure editing is disabled
if (log.workflow_state === 'Approved') {
setIsEditing(false);
}
setFormData({
asset_name: log.asset_name || '',
custom_asset_type: log.custom_asset_type || '',
item_code: log.item_code || '',
item_name: log.item_name || '',
custom_asset_names: log.custom_asset_names || '',
custom_hospital_name: log.custom_hospital_name || '',
task: log.task || '',
task_name: log.task_name || '',
maintenance_type: log.maintenance_type || 'Preventive Maintenance',
periodicity: log.periodicity || '',
has_certificate: log.has_certificate || 0,
custom_early_completion: isDuplicating ? '' : (log.custom_early_completion || ''),
maintenance_status: isDuplicating ? 'Planned' : (log.maintenance_status || 'Planned'),
custom_accepted_by_moh: log.custom_accepted_by_moh || 0,
assign_to_name: log.assign_to_name || '',
due_date: log.due_date || '',
completion_date: isDuplicating ? '' : (log.completion_date || ''),
custom_early_completion_reason: isDuplicating ? '' : (log.custom_early_completion_reason || ''),
custom_accepted_by_moh_: log.custom_accepted_by_moh_ || 0,
custom_template: log.custom_template || '',
custom_table: log.custom_table || [],
description: log.description || '',
});
if (log.custom_table && Array.isArray(log.custom_table) && log.custom_table.length > 0) {
setPpmTableRows(log.custom_table.map((row: any, index: number) => ({
name: row.name,
idx: row.idx || index + 1,
maintenance_name: row.maintenance_name || '',
working: row.working || 0,
defect_found: row.defect_found || 0,
not_working: row.not_working || 0,
})));
} else if (log.custom_template && (!log.custom_table || log.custom_table.length === 0)) {
loadPPMTemplate(log.custom_template);
}
}
}, [log, isDuplicating, loadPPMTemplate]);
// Handle completion_date change - trigger early completion check
useEffect(() => {
if (formData.completion_date && formData.due_date) {
checkEarlyCompletion(formData.due_date, formData.completion_date, formData.maintenance_status);
}
}, [formData.completion_date, formData.due_date, formData.maintenance_status, checkEarlyCompletion]);
const handleChange = (e: React.ChangeEvent) => {
const { name, value, type } = e.target;
const checked = (e.target as HTMLInputElement).checked;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? (checked ? 1 : 0) : value
}));
};
const handleStatusChange = (e: React.ChangeEvent) => {
const newStatus = e.target.value;
setFormData(prev => ({
...prev,
maintenance_status: newStatus
}));
if (newStatus === 'Completed') {
checkEarlyCompletion(formData.due_date || '', formData.completion_date || '', newStatus);
} else {
setFormData(prev => ({ ...prev, custom_early_completion: '' }));
}
};
const handleCompletionDateChange = (e: React.ChangeEvent) => {
const newDate = e.target.value;
setFormData(prev => ({
...prev,
completion_date: newDate
}));
if (formData.maintenance_status === 'Completed') {
checkEarlyCompletion(formData.due_date || '', newDate, formData.maintenance_status);
}
};
const handleTemplateChange = async (e: React.ChangeEvent) => {
const newTemplate = e.target.value;
setFormData(prev => ({ ...prev, custom_template: newTemplate }));
if (newTemplate && ppmTableRows.length === 0) {
loadPPMTemplate(newTemplate);
} else if (newTemplate && ppmTableRows.length > 0) {
if (window.confirm('Loading a template will replace existing checklist items. Continue?')) {
loadPPMTemplate(newTemplate);
}
}
};
// PPM Table handlers
const addPpmRow = () => {
const newRow: PPMTableRow = {
idx: ppmTableRows.length + 1,
maintenance_name: '',
working: 0,
defect_found: 0,
not_working: 0,
};
setPpmTableRows([...ppmTableRows, newRow]);
};
const removePpmRow = (index: number) => {
const updated = ppmTableRows.filter((_, i) => i !== index).map((row, i) => ({
...row,
idx: i + 1
}));
setPpmTableRows(updated);
};
const updatePpmRow = (index: number, field: keyof PPMTableRow, value: any) => {
const updated = [...ppmTableRows];
updated[index] = { ...updated[index], [field]: value };
setPpmTableRows(updated);
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!formData.asset_name) {
alert('Please enter Asset Name');
return;
}
if (!formData.maintenance_type) {
alert('Please select Maintenance Type');
return;
}
// Clean PPM table rows - ensure name is either a valid string or removed
const cleanedPpmRows = ppmTableRows.map((row, index) => {
const cleanedRow: any = {
idx: row.idx || index + 1,
maintenance_name: row.maintenance_name || '',
working: row.working ? 1 : 0,
defect_found: row.defect_found ? 1 : 0,
not_working: row.not_working ? 1 : 0,
};
// Only include name if it's a valid non-empty string (existing row from DB)
if (row.name && typeof row.name === 'string' && row.name.trim() !== '') {
cleanedRow.name = row.name;
}
return cleanedRow;
});
const submitData = {
...formData,
custom_table: cleanedPpmRows,
};
console.log('Submitting maintenance log data:', submitData);
try {
if (isNewLog || isDuplicating) {
const newLog = await createLog(submitData as any);
const successMessage = isDuplicating
? 'Maintenance log duplicated successfully!'
: 'Maintenance log created successfully!';
alert(successMessage);
navigate(`/maintenance/${newLog.name}`);
} else if (logName) {
await updateLog(logName, submitData as any);
alert('Maintenance log updated successfully!');
setIsEditing(false);
}
} catch (err) {
console.error('Maintenance log save error:', err);
alert('Failed to save: ' + (err instanceof Error ? err.message : 'Unknown error'));
}
};
const getWorkflowStateBadge = (state: string) => {
const stateColors: Record = {
'Draft': 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300',
'Applied': 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200',
'Pending': 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200',
'Approved': 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
'Rejected': 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200',
'Completed': 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
};
return stateColors[state] || 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300';
};
const getStatusBadge = (status: string) => {
const statusColors: Record = {
'Planned': 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200',
'Completed': 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
'Overdue': 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200',
'Cancelled': 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300',
};
return statusColors[status] || 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300';
};
if (loading) {
return (
Loading maintenance log...
);
}
if (error && !isNewLog && !isDuplicating) {
return (
Error: {error}
);
}
return (
{/* Toast Notification */}
{toast && (
setToast(null)}
/>
)}
{/* Header */}
{!isNewLog && log?.workflow_state && (
{log.workflow_state}
)}
{/* {isApproved && (
🔒 Locked
)} */}
{!isNewLog && !isEditing && !isApproved && (
)}
{isEditing && (
<>
>
)}
{/* Standard View Form */}
);
};
export default AssetMaintenanceDetail;