import React, { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { Layout, Card, Row, Col, Typography, Space, Button, Spin, Result, Input, message, Avatar, Tag, } from 'antd'; import { ArrowLeftOutlined, CloseCircleFilled, WarningFilled, CheckCircleFilled, InfoCircleFilled, CloseOutlined, BookOutlined, ToolOutlined, HistoryOutlined, FilePdfOutlined, PlusOutlined, UserOutlined, LoadingOutlined, PhoneOutlined, CheckCircleOutlined, SyncOutlined, SendOutlined, } from '@ant-design/icons'; import { getNotificationDetail, createNotificationLog, getNotificationLogByNotificationId, updateIsRead, resendNotificationToUser, } from '../../api/notification'; const { Content } = Layout; const { Text, Paragraph, Link } = Typography; // Transform API response to component format const transformNotificationData = (apiData) => { // Extract nested data const errorCodeData = apiData.error_code; // Get active solution (is_active: true) const activeSolution = errorCodeData?.solution?.find((sol) => sol.is_active) || errorCodeData?.solution?.[0] || {}; return { id: `notification-${apiData.notification_error_id}-0`, type: apiData.is_read ? 'resolved' : apiData.is_delivered ? 'warning' : 'critical', title: errorCodeData?.error_code_name || 'Unknown Error', issue: errorCodeData?.error_code || 'Unknown Error', description: apiData.message_error_issue || 'No details available', timestamp: apiData.created_at ? new Date(apiData.created_at).toLocaleString('id-ID', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', }) + ' WIB' : 'N/A', location: apiData.plant_sub_section_name || 'Location not specified', details: apiData.message_error_issue || 'No details available', isRead: apiData.is_read || false, isDelivered: apiData.is_delivered || false, isSend: apiData.is_send || false, status: apiData.is_read ? 'Resolved' : apiData.is_delivered ? 'Delivered' : 'Pending', tag: errorCodeData?.error_code, plc: 'N/A', // PLC not available in API response notification_error_id: apiData.notification_error_id, error_code_id: apiData.error_code_id, error_chanel: apiData.error_chanel, spareparts: errorCodeData?.spareparts || [], solution: { ...activeSolution, path_document: activeSolution.path_document ? activeSolution.path_document.replace( '/detail-notification/pdf/', '/notification-detail/pdf/' ) : activeSolution.path_document, }, // Include the active solution data with fixed URL error_code: errorCodeData, device_info: { device_code: apiData.device_code, device_name: apiData.device_name, device_location: apiData.device_location, brand_name: apiData.brand_name, }, users: apiData.users || [], }; }; // Function to get actual users from notification data const getUsersFromNotification = (notification) => { if (!notification || !notification.users) return []; return notification.users.map((user) => ({ id: user.notification_error_user_id.toString(), name: user.contact_name, phone: user.contact_phone, status: user.is_send ? 'sent' : 'pending', loading: user.loading || false, })); }; const getStatusTag = (status) => { switch (status) { case 'delivered': return ( } color="success"> Delivered ); case 'sent': return ( } color="processing"> Sent ); case 'failed': return Failed; default: return {status}; } }; const getIconAndColor = (type) => { switch (type) { case 'critical': return { IconComponent: CloseCircleFilled, color: '#ff4d4f', bgColor: '#fff1f0' }; case 'warning': return { IconComponent: WarningFilled, color: '#faad14', bgColor: '#fffbe6' }; case 'resolved': return { IconComponent: CheckCircleFilled, color: '#52c41a', bgColor: '#f6ffed' }; default: return { IconComponent: InfoCircleFilled, color: '#1890ff', bgColor: '#e6f7ff' }; } }; const NotificationDetailTab = (props) => { const params = useParams(); // Mungkin perlu disesuaikan jika route berbeda const notificationId = props.id ?? params.notificationId; const navigate = useNavigate(); const [notification, setNotification] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [isAddingLog, setIsAddingLog] = useState(false); // Log history states const [logHistoryData, setLogHistoryData] = useState([]); const [logLoading, setLogLoading] = useState(false); const [newLogDescription, setNewLogDescription] = useState(''); const [submitLoading, setSubmitLoading] = useState(false); // Fetch log history from API const fetchLogHistory = async (notifId) => { try { setLogLoading(true); const response = await getNotificationLogByNotificationId(notifId); if (response && response.data) { // Transform API data to component format const transformedLogs = response.data.map((log) => ({ id: log.notification_error_log_id, timestamp: log.created_at ? new Date(log.created_at).toLocaleString('id-ID', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', }) + ' WIB' : 'N/A', addedBy: { name: log.contact_name || 'Unknown', phone: log.contact_phone || '', }, description: log.notification_error_log_description || '', })); setLogHistoryData(transformedLogs); } } catch (err) { console.error('Error fetching log history:', err); } finally { setLogLoading(false); } }; // Handle submit new log const handleSubmitLog = async () => { if (!newLogDescription.trim()) { message.warning('Mohon isi deskripsi log terlebih dahulu'); return; } try { setSubmitLoading(true); const payload = { notification_error_id: parseInt(notificationId), notification_error_log_description: newLogDescription.trim(), }; const response = await createNotificationLog(payload); if (response && response.statusCode === 200) { message.success('Log berhasil ditambahkan'); setNewLogDescription(''); setIsAddingLog(false); // Refresh log history fetchLogHistory(notificationId); } else { throw new Error(response?.message || 'Gagal menambahkan log'); } } catch (err) { console.error('Error submitting log:', err); message.error(err.message || 'Gagal menambahkan log'); } finally { setSubmitLoading(false); } }; useEffect(() => { const fetchDetail = async () => { try { setLoading(true); // Fetch using the actual API const response = await getNotificationDetail(notificationId); // Fetch using the actual API const resUpdate = await updateIsRead(notificationId); if (response && response.data) { const transformedData = transformNotificationData(response.data); setNotification(transformedData); // Fetch log history fetchLogHistory(notificationId); } else { throw new Error('Notification not found'); } } catch (err) { setError(err.message); console.error('Error fetching notification detail:', err); } finally { setLoading(false); } }; fetchDetail(); }, [notificationId]); if (loading) { return ( ); } if (error || !notification) { return ( navigate('/notification')}> Back to List } /> ); } const { color } = getIconAndColor(notification.type); return (
{!props.id && ( )}
Error Notification Detail
{/* Kolom Kiri: Data Kompresor */}
{notification.title}
{notification.issue}
Plant Subsection
{notification.location}
Date & Time
{notification.timestamp}
{/* Kolom Tengah: Informasi Teknis */}
Error Channel
{notification.error_chanel || 'N/A'}
Device Code
{notification.device_info?.device_code || 'N/A'}
Device Name
{notification.device_info?.device_name || 'N/A'}
Device Location
{notification.device_info?.device_location || 'N/A'}
Brand
{notification.device_info?.brand_name || 'N/A'}
{/* Kolom Kanan: User History */}
{getUsersFromNotification(notification).map((user) => ( } />
{user.name}
{user.phone}
{getStatusTag(user.status)}
))}
Handling Guideline Spare Part Log Activity {notification.error_code?.solution && notification.error_code.solution.length > 0 ? ( <> {notification.error_code.solution .filter((sol) => sol.is_active) // Hanya tampilkan solusi yang aktif .map((sol, index) => (
{sol.path_document ? ( PDF } >
{' '} {sol.file_upload_name || 'Solution Document.pdf'} lihat disini
) : null} {sol.type_solution === 'text' && sol.text_solution ? ( {sol.type_solution.toUpperCase()} } >
{sol.solution_name}:
{sol.text_solution}
) : null}
))} ) : (
Tidak ada dokumen solusi tersedia
)}
{notification.spareparts && notification.spareparts.length > 0 ? ( notification.spareparts.map((sparepart, index) => (
{sparepart.sparepart_stok} {sparepart.sparepart_name} {sparepart.sparepart_description || 'Deskripsi tidak tersedia'}
Kode: {sparepart.sparepart_code}{' '} | Qty: {sparepart.sparepart_qty}{' '} | Unit:{' '} {sparepart.sparepart_unit}
)) ) : (
Tidak ada spare parts terkait
)}
{isAddingLog && ( <> Add New Log / Update Progress setNewLogDescription(e.target.value) } disabled={submitLoading} /> )} {isAddingLog && ( )} {logHistoryData.map((log) => ( {log.addedBy.name}:{' '} {log.description} {log.timestamp} ))}
); }; export default NotificationDetailTab;