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 && ( } onClick={() => navigate('/notification')} style={{ paddingLeft: 0 }} > Back to notification list )} 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)} } size="small" loading={user.loading} onClick={async (e) => { e.stopPropagation(); const userId = parseInt( user.id ); try { // Update user status to show loading const updatedUsers = notification.users.map( (u) => u.notification_error_user_id === userId ? { ...u, loading: true, } : u ); setNotification({ ...notification, users: updatedUsers, }); // Call the resend API const response = await resendNotificationToUser( notification.notification_error_id, userId ); if ( response && response.statusCode === 200 ) { message.success( `Notification resent to ${user.name}` ); // Update user status const updatedUsersAfterSuccess = notification.users.map( (u) => u.notification_error_user_id === userId ? { ...u, is_send: true, status: 'sent', loading: false, } : { ...u, loading: false, } ); setNotification({ ...notification, users: updatedUsersAfterSuccess, }); } else { throw new Error( response?.message || 'Failed to resend notification' ); } } catch (error) { console.error( 'Error resending notification:', error ); message.error( error.message || 'Failed to resend notification' ); // Reset loading state const resetUsers = notification.users.map( (u) => u.notification_error_user_id === userId ? { ...u, loading: false, } : u ); setNotification({ ...notification, users: resetUsers, }); } }} > Resend ))} 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 && ) } onClick={ isAddingLog ? handleSubmitLog : () => setIsAddingLog(true) } loading={submitLoading} disabled={submitLoading} > {isAddingLog ? 'Submit Log' : 'Add Log'} {isAddingLog && ( { setIsAddingLog(false); setNewLogDescription(''); }} disabled={submitLoading} > Cancel )} {logHistoryData.map((log) => ( {log.addedBy.name}:{' '} {log.description} {log.timestamp} ))} ); }; export default NotificationDetailTab;