minor fix notif

This commit is contained in:
2025-12-18 12:36:46 +07:00
parent 3a4b0f0748
commit 1ce922ff4c
3 changed files with 5 additions and 5 deletions

View File

@@ -0,0 +1,411 @@
import React, { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import {
Layout,
Card,
Row,
Col,
Typography,
Space,
Button,
Spin,
Result,
Input,
} from 'antd';
import {
ArrowLeftOutlined,
CloseCircleFilled,
WarningFilled,
CheckCircleFilled,
InfoCircleFilled,
CloseOutlined,
BookOutlined,
ToolOutlined,
HistoryOutlined,
FilePdfOutlined,
PlusOutlined,
UserOutlined,
} from '@ant-design/icons';
import { getNotificationDetail } from '../../api/notification';
import UserHistoryModal from '../notification/component/UserHistoryModal';
import LogHistoryCard from '../notification/component/LogHistoryCard';
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;
const solutionData = 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 Device',
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: solutionData?.solution_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,
spareparts: errorCodeData?.spareparts || [],
solution: solutionData, // Include the solution data
error_code: errorCodeData,
};
};
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 = () => {
const { notificationId } = useParams(); // Mungkin perlu disesuaikan jika route berbeda
const navigate = useNavigate();
const [notification, setNotification] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [modalContent, setModalContent] = useState(null); // 'user', atau null
const [isAddingLog, setIsAddingLog] = useState(false);
const logHistoryData = [
{
id: 1,
timestamp: '04-11-2025 11:55 WIB',
addedBy: {
name: 'Budi Santoso',
phone: '081122334455',
},
description: 'Suhu sudah coba diturunkan, namun masih belum mencapai treshold aman.',
},
{
id: 2,
timestamp: '04-11-2025 11:45 WIB',
addedBy: {
name: 'John Doe',
phone: '081234567890',
},
description: 'Suhu sudah coba diturunkan, namun masih belum mencapai treshold aman.',
},
{
id: 3,
timestamp: '04-11-2025 11:40 WIB',
addedBy: {
name: 'Jane Smith',
phone: '087654321098',
},
description: 'Suhu sudah coba diturunkan, namun masih belum mencapai treshold aman.',
},
];
useEffect(() => {
const fetchDetail = async () => {
try {
setLoading(true);
// Fetch using the actual API
const response = await getNotificationDetail(notificationId);
if (response && response.data) {
const transformedData = transformNotificationData(response.data);
setNotification(transformedData);
} 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 (
<Layout style={{ minHeight: '100vh', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<Spin size="large" />
</Layout>
);
}
if (error || !notification) {
return (
<Layout style={{ minHeight: '100vh', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<Result
status="404"
title="404"
subTitle="Sorry, the notification you visited does not exist."
extra={<Button type="primary" onClick={() => navigate('/notification')}>Back to List</Button>}
/>
</Layout>
);
}
const { color } = getIconAndColor(notification.type);
return (
<Layout style={{ padding: '24px', backgroundColor: '#f0f2f5' }}>
<Content>
<Card>
<div style={{ borderBottom: '1px solid #f0f0f0', paddingBottom: '16px', marginBottom: '24px' }}>
<Row justify="space-between" align="middle">
<Col>
<Button
type="text"
icon={<ArrowLeftOutlined />}
onClick={() => navigate('/notification')}
style={{ paddingLeft: 0 }}
>
Back to notification list
</Button>
</Col>
<Col>
<Button
icon={<UserOutlined />}
onClick={() => setModalContent('user')}
>
User History
</Button>
</Col>
</Row>
<div style={{ backgroundColor: '#f6ffed', border: '1px solid #b7eb8f', borderRadius: '4px', padding: '8px 16px', textAlign: 'center', marginTop: '16px' }}>
<Typography.Title level={4} style={{ margin: 0, color: '#262626' }}>
Error Notification Detail
</Typography.Title>
</div>
</div>
<Space direction="vertical" size="large" style={{ width: '100%' }}>
<Row gutter={[24, 24]}>
{/* Kolom Kiri: Data Kompresor */}
<Col xs={24} lg={8}>
<Card size="small" style={{ height: '100%', borderColor: '#d4380d' }} bodyStyle={{ padding: '16px' }}>
<Space direction="vertical" size="large" style={{ width: '100%' }}>
<Row gutter={16} align="middle">
<Col>
<div style={{ width: '32px', height: '32px', borderRadius: '50%', backgroundColor: '#d4380d', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#ffffff', fontSize: '18px' }}><CloseOutlined /></div>
</Col>
<Col>
<Text>{notification.title}</Text>
<div style={{ marginTop: '2px' }}><Text strong style={{ fontSize: '16px' }}>{notification.issue}</Text></div>
</Col>
</Row>
<div>
<Text strong>Plant Subsection</Text>
<div>{notification.location}</div>
<Text strong style={{ display: 'block', marginTop: '8px' }}>Time</Text>
<div>{notification.timestamp.split(' ')[1]} WIB</div>
</div>
<div style={{ border: '1px solid #d4380d', borderRadius: '4px', padding: '8px', background: 'linear-gradient(to right, #ffe7e6, #ffffff)' }}>
<Row justify="space-around" align="middle">
<Col><Text style={{ fontSize: '12px', color: color }}>Value</Text><div style={{ fontWeight: 'bold', fontSize: '16px', color: color }}>N/A</div></Col>
<Col><Text type="secondary" style={{ fontSize: '12px' }}>Treshold</Text><div style={{ fontWeight: 500 }}>N/A</div></Col>
</Row>
</div>
</Space>
</Card>
</Col>
{/* Kolom Tengah: Informasi Teknis */}
<Col xs={24} lg={8}>
<Card title="Informasi Teknis" size="small" style={{ height: '100%' }}>
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
<div><Text strong>PLC</Text><div>{notification.plc || 'N/A'}</div></div>
<div><Text strong>Status</Text><div style={{ color: '#faad14', fontWeight: 500 }}>{notification.status}</div></div>
<div><Text strong>Tag</Text><div style={{ fontFamily: 'monospace', backgroundColor: '#f0f0f0', padding: '2px 6px', borderRadius: '4px', display: 'inline-block' }}>{notification.tag}</div></div>
</Space>
</Card>
</Col>
{/* Kolom Kanan: Log History */}
<Col xs={24} lg={8}>
<LogHistoryCard notificationData={notification} />
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col xs={24} md={8}><Card hoverable bodyStyle={{ padding: '12px', textAlign: 'center' }}><Space><BookOutlined style={{ fontSize: '16px', color: '#1890ff' }} /><Text strong style={{ fontSize: '16px', color: '#262626' }}>Handling Guideline</Text></Space></Card></Col>
<Col xs={24} md={8}><Card hoverable bodyStyle={{ padding: '12px', textAlign: 'center' }}><Space><ToolOutlined style={{ fontSize: '16px', color: '#1890ff' }} /><Text strong style={{ fontSize: '16px', color: '#262626' }}>Spare Part</Text></Space></Card></Col>
<Col xs={24} md={8} style={{ cursor: 'pointer' }}><Card hoverable bodyStyle={{ padding: '12px', textAlign: 'center' }}><Space><HistoryOutlined style={{ fontSize: '16px', color: '#1890ff' }} /><Text strong style={{ fontSize: '16px', color: '#262626' }}>Log Activity</Text></Space></Card></Col>
</Row>
<Row gutter={[16, 16]}>
<Col xs={24} md={8}>
<Card size="small" title="Guideline Documents" style={{ height: '100%' }}>
<Space direction="vertical" size="small" style={{ width: '100%' }}>
{notification.solution && (
<>
{notification.solution.path_document ? (
<Card size="small" bodyStyle={{ padding: '8px 12px' }} hoverable extra={<Text type="secondary" style={{ fontSize: '10px' }}>PDF</Text>}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<Text style={{ fontSize: '12px', color: '#262626' }}><FilePdfOutlined style={{ marginRight: '8px' }} /> {notification.solution.file_upload_name || 'Solution Document.pdf'}</Text>
<Link href={notification.solution.path_document} target="_blank" style={{ fontSize: '12px', display: 'block' }}>lihat disini</Link>
</div>
</div>
</Card>
) : null}
{notification.solution.type_solution === 'text' && notification.solution.text_solution ? (
<Card size="small" bodyStyle={{ padding: '8px 12px' }} extra={<Text type="secondary" style={{ fontSize: '10px' }}>{notification.solution.type_solution.toUpperCase()}</Text>}>
<Paragraph style={{ fontSize: '12px', margin: 0 }}>
<Text strong>{notification.issue}:</Text> {notification.solution.text_solution}
</Paragraph>
</Card>
) : null}
</>
)}
{!notification.solution && (
<div style={{ textAlign: 'center', padding: '20px', color: '#8c8c8c' }}>
Tidak ada dokumen solusi tersedia
</div>
)}
</Space>
</Card>
</Col>
<Col xs={24} md={8}>
<Card size="small" title="Required Spare Parts" style={{ height: '100%' }}>
<Space direction="vertical" size="small" style={{ width: '100%' }}>
{notification.spareparts && notification.spareparts.length > 0 ? (
notification.spareparts.map((sparepart, index) => (
<Card size="small" key={index} bodyStyle={{ padding: '12px' }} hoverable>
<Row gutter={16} align="top">
<Col span={7} style={{ textAlign: 'center' }}>
<div style={{ width: '100%', height: '60px', backgroundColor: '#f0f0f0', display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: '4px', marginBottom: '8px' }}>
<ToolOutlined style={{ fontSize: '24px', color: '#bfbfbf' }} />
</div>
<Text style={{
fontSize: '12px',
color: sparepart.sparepart_stok === 'Available' || sparepart.sparepart_stok === 'available' ? '#52c41a' : '#ff4d4f',
fontWeight: 500
}}>
{sparepart.sparepart_stok}
</Text>
</Col>
<Col span={17}>
<Space direction="vertical" size={4} style={{ width: '100%' }}>
<Text strong>{sparepart.sparepart_name}</Text>
<Paragraph style={{ fontSize: '12px', margin: 0, color: '#595959' }}>
{sparepart.sparepart_description || 'Deskripsi tidak tersedia'}
</Paragraph>
<div style={{
border: '1px solid #d9d9d9',
borderRadius: '4px',
padding: '4px 8px',
fontSize: '11px',
color: '#8c8c8c',
marginTop: '8px'
}}>
Kode: {sparepart.sparepart_code} | Qty: {sparepart.sparepart_qty} | Unit: {sparepart.sparepart_unit}
</div>
</Space>
</Col>
</Row>
</Card>
))
) : (
<div style={{ textAlign: 'center', padding: '20px', color: '#8c8c8c' }}>
Tidak ada spare parts terkait
</div>
)}
</Space>
</Card>
</Col>
<Col span={8}>
<Card size="small" style={{ height: '100%' }}>
<Space direction="vertical" size="small" style={{ width: '100%' }}>
<Card
size="small"
bodyStyle={{
padding: '8px 12px',
backgroundColor: isAddingLog ? '#fafafa' : '#fff',
}}
>
<Space
direction="vertical"
style={{ width: '100%' }}
size="small"
>
{isAddingLog && (
<>
<Text strong style={{ fontSize: '12px' }}>
Add New Log / Update Progress
</Text>
<Input.TextArea
rows={2}
placeholder="Tuliskan update penanganan di sini..."
/>
</>
)}
<Button
type={isAddingLog ? 'primary' : 'dashed'}
size="small"
block
icon={!isAddingLog && <PlusOutlined />}
onClick={() => setIsAddingLog(!isAddingLog)}
>
{isAddingLog ? 'Submit Log' : 'Add Log'}
</Button>
</Space>
</Card>
{logHistoryData.map((log) => (
<Card
key={log.id}
size="small"
bodyStyle={{ padding: '8px 12px' }}
>
<Paragraph
style={{ fontSize: '12px', margin: 0 }}
ellipsis={{ rows: 2 }}
>
<Text strong>{log.addedBy.name}:</Text>{' '}
{log.description}
</Paragraph>
<Text type="secondary" style={{ fontSize: '11px' }}>
{log.timestamp}
</Text>
</Card>
))}
</Space>
</Card>
</Col>
</Row>
</Space>
</Card>
</Content>
<UserHistoryModal
visible={modalContent === 'user'}
onCancel={() => setModalContent(null)}
notificationData={notification}
/>
</Layout>
);
};
export default NotificationDetailTab;