feat: replace LogHistoryModal with LogHistoryCard and update DetailNotification for improved log history display

This commit is contained in:
2025-12-09 13:32:53 +07:00
parent 3225a0865e
commit a014d6b370
4 changed files with 238 additions and 152 deletions

View File

@@ -29,7 +29,7 @@ import {
// Path disesuaikan karena lokasi file berubah
// import { getNotificationById } from '../../api/notification'; // Dihapus karena belum ada di file API
import UserHistoryModal from '../notification/component/UserHistoryModal';
import LogHistoryModal from '../notification/component/LogHistoryModal';
import LogHistoryCard from '../notification/component/LogHistoryCard'; // Ganti LogHistoryModal dengan LogHistoryCard
const { Content } = Layout;
const { Text, Paragraph, Link } = Typography;
@@ -79,7 +79,7 @@ const DetailNotificationTab = () => {
const [notification, setNotification] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [modalContent, setModalContent] = useState(null); // 'user', 'log', atau null
const [modalContent, setModalContent] = useState(null); // 'user', atau null
const [isAddingLog, setIsAddingLog] = useState(false);
const logHistoryData = [
@@ -196,7 +196,7 @@ const DetailNotificationTab = () => {
<Space direction="vertical" size="large" style={{ width: '100%' }}>
<Row gutter={[24, 24]}>
{/* Kolom Kiri: Data Kompresor */}
<Col xs={24} lg={12}>
<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">
@@ -224,8 +224,8 @@ const DetailNotificationTab = () => {
</Card>
</Col>
{/* Kolom Kanan: Informasi Teknis */}
<Col xs={24} lg={12}>
{/* 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>
@@ -234,12 +234,17 @@ const DetailNotificationTab = () => {
</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} onClick={() => setModalContent('log')} 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>
<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]}>
@@ -345,11 +350,6 @@ const DetailNotificationTab = () => {
onCancel={() => setModalContent(null)}
notificationData={notification}
/>
<LogHistoryModal
visible={modalContent === 'log'}
onCancel={() => setModalContent(null)}
notificationData={notification}
/>
</Layout>
);
};

View File

@@ -1,7 +1,7 @@
import React, { memo, useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useBreadcrumb } from '../../layout/LayoutBreadcrumb';
import { Typography } from 'antd';
import { Typography, Row, Col } from 'antd';
import ListNotification from './component/ListNotification';
import DetailNotification from './component/DetailNotification';
@@ -10,10 +10,7 @@ const { Text } = Typography;
const IndexNotification = memo(function IndexNotification() {
const navigate = useNavigate();
const { setBreadcrumbItems } = useBreadcrumb();
const [actionMode, setActionMode] = useState('list');
const [selectedData, setSelectedData] = useState(null);
const [isModalVisible, setIsModalVisible] = useState(false);
useEffect(() => {
const token = localStorage.getItem('token');
@@ -32,33 +29,34 @@ const IndexNotification = memo(function IndexNotification() {
}
}, [navigate, setBreadcrumbItems]);
useEffect(() => {
if (actionMode === 'preview') {
setIsModalVisible(true);
} else {
setIsModalVisible(false);
}
}, [actionMode]);
const handleCancel = () => {
setActionMode('list');
const handleCloseDetail = () => {
setSelectedData(null);
};
// This handler will be passed to ListNotification to update the selected item
const handleSelectNotification = (data) => {
setSelectedData(data);
};
return (
<React.Fragment>
<ListNotification
actionMode={actionMode}
setActionMode={setActionMode}
selectedData={selectedData}
setSelectedData={setSelectedData}
/>
<DetailNotification
visible={isModalVisible}
onCancel={handleCancel}
selectedData={selectedData}
/>
</React.Fragment>
<Row gutter={16}>
<Col span={selectedData ? 16 : 24}>
<ListNotification
// The setActionMode is likely not needed anymore,
// but we pass the selection handler
setActionMode={() => {}} // Keep prop for safety, but can be empty
setSelectedData={handleSelectNotification}
/>
</Col>
{selectedData && (
<Col span={8}>
<DetailNotification
selectedData={selectedData}
onClose={handleCloseDetail}
/>
</Col>
)}
</Row>
);
});

View File

@@ -1,8 +1,12 @@
import React, { memo } from 'react';
import { Modal, Row, Col, Tag, Divider } from 'antd';
import { Row, Col, Tag, Divider, Card, Button } from 'antd';
import { CloseCircleFilled, WarningFilled, CheckCircleFilled, InfoCircleFilled } from '@ant-design/icons';
const DetailNotification = memo(function DetailNotification({ visible, onCancel, form, selectedData }) {
const DetailNotification = memo(function DetailNotification({ selectedData, onClose }) {
if (!selectedData) {
return null;
}
const getIconAndColor = (type) => {
switch (type) {
case 'critical':
@@ -36,133 +40,127 @@ const DetailNotification = memo(function DetailNotification({ visible, onCancel,
}
};
const { IconComponent, color, bgColor, tagColor } = selectedData ? getIconAndColor(selectedData.type) : {};
const { IconComponent, color, bgColor, tagColor } = getIconAndColor(selectedData.type);
return (
<Modal
<Card
title="Detail Notifikasi"
open={visible}
onCancel={onCancel}
onOk={onCancel}
okText="Tutup"
cancelButtonProps={{ style: { display: 'none' } }}
width={700}
extra={<Button onClick={onClose}>Tutup</Button>}
style={{ height: '100%' }}
>
{selectedData && (
<div>
{/* Header with Icon and Status */}
<div>
{/* Header with Icon and Status */}
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '16px',
marginBottom: '24px',
padding: '16px',
backgroundColor: '#fafafa',
borderRadius: '8px',
}}
>
<div
style={{
width: '64px',
height: '64px',
borderRadius: '50%',
backgroundColor: bgColor,
color: color,
display: 'flex',
alignItems: 'center',
gap: '16px',
marginBottom: '24px',
padding: '16px',
backgroundColor: '#fafafa',
borderRadius: '8px',
justifyContent: 'center',
fontSize: '32px',
flexShrink: 0,
}}
>
<div
style={{
width: '64px',
height: '64px',
borderRadius: '50%',
backgroundColor: bgColor,
color: color,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '32px',
flexShrink: 0,
}}
>
{IconComponent && <IconComponent style={{ fontSize: '32px' }} />}
</div>
<div style={{ flex: 1 }}>
<Tag color={tagColor} style={{ marginBottom: '8px', fontSize: '12px' }}>
{selectedData.type.toUpperCase()}
</Tag>
<div style={{ fontSize: '16px', fontWeight: 600, color: '#262626' }}>
{selectedData.title}
</div>
</div>
{IconComponent && <IconComponent style={{ fontSize: '32px' }} />}
</div>
<Divider style={{ margin: '16px 0' }} />
{/* Information Grid */}
<Row gutter={[16, 16]}>
<Col span={12}>
<div style={{ marginBottom: '16px' }}>
<div style={{ fontSize: '12px', color: '#8c8c8c', marginBottom: '4px' }}>
PLC
</div>
<div style={{ fontSize: '14px', color: '#262626', fontWeight: 500 }}>
{selectedData.plc}
</div>
</div>
</Col>
<Col span={12}>
<div style={{ marginBottom: '16px' }}>
<div style={{ fontSize: '12px', color: '#8c8c8c', marginBottom: '4px' }}>Tag</div>
<div style={{ fontSize: '14px', color: '#262626', fontWeight: 500 }}>
{selectedData.tag}
</div>
</div>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={12}>
<div style={{ marginBottom: '16px' }}>
<div style={{ fontSize: '12px', color: '#8c8c8c', marginBottom: '4px' }}>
Engineer
</div>
<div style={{ fontSize: '14px', color: '#262626', fontWeight: 500 }}>
{selectedData.engineer}
</div>
</div>
</Col>
<Col span={12}>
<div style={{ marginBottom: '16px' }}>
<div style={{ fontSize: '12px', color: '#8c8c8c', marginBottom: '4px' }}>
Waktu
</div>
<div style={{ fontSize: '14px', color: '#262626', fontWeight: 500 }}>
{selectedData.time}
</div>
</div>
</Col>
</Row>
<Divider style={{ margin: '16px 0' }} />
{/* Status */}
<div style={{ marginBottom: '16px' }}>
<div style={{ fontSize: '12px', color: '#8c8c8c', marginBottom: '8px' }}>Status</div>
<Tag color={selectedData.isRead ? 'default' : 'blue'}>
{selectedData.isRead ? 'Sudah Dibaca' : 'Belum Dibaca'}
<div style={{ flex: 1 }}>
<Tag color={tagColor} style={{ marginBottom: '8px', fontSize: '12px' }}>
{selectedData.type.toUpperCase()}
</Tag>
</div>
{/* Additional Info */}
<div
style={{
marginTop: '16px',
padding: '12px',
backgroundColor: '#f6f9ff',
borderRadius: '6px',
border: '1px solid #d6e4ff',
}}
>
<div style={{ fontSize: '12px', color: '#595959' }}>
<strong>Catatan:</strong> Notifikasi ini telah dikirim ke engineer yang bersangkutan
untuk ditindaklanjuti sesuai dengan prosedur yang berlaku.
<div style={{ fontSize: '16px', fontWeight: 600, color: '#262626' }}>
{selectedData.title}
</div>
</div>
</div>
)}
</Modal>
<Divider style={{ margin: '16px 0' }} />
{/* Information Grid */}
<Row gutter={[16, 16]}>
<Col span={12}>
<div style={{ marginBottom: '16px' }}>
<div style={{ fontSize: '12px', color: '#8c8c8c', marginBottom: '4px' }}>
PLC
</div>
<div style={{ fontSize: '14px', color: '#262626', fontWeight: 500 }}>
{selectedData.plc}
</div>
</div>
</Col>
<Col span={12}>
<div style={{ marginBottom: '16px' }}>
<div style={{ fontSize: '12px', color: '#8c8c8c', marginBottom: '4px' }}>Tag</div>
<div style={{ fontSize: '14px', color: '#262626', fontWeight: 500 }}>
{selectedData.tag}
</div>
</div>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={12}>
<div style={{ marginBottom: '16px' }}>
<div style={{ fontSize: '12px', color: '#8c8c8c', marginBottom: '4px' }}>
Engineer
</div>
<div style={{ fontSize: '14px', color: '#262626', fontWeight: 500 }}>
{selectedData.engineer}
</div>
</div>
</Col>
<Col span={12}>
<div style={{ marginBottom: '16px' }}>
<div style={{ fontSize: '12px', color: '#8c8c8c', marginBottom: '4px' }}>
Waktu
</div>
<div style={{ fontSize: '14px', color: '#262626', fontWeight: 500 }}>
{selectedData.time}
</div>
</div>
</Col>
</Row>
<Divider style={{ margin: '16px 0' }} />
{/* Status */}
<div style={{ marginBottom: '16px' }}>
<div style={{ fontSize: '12px', color: '#8c8c8c', marginBottom: '8px' }}>Status</div>
<Tag color={selectedData.isRead ? 'default' : 'blue'}>
{selectedData.isRead ? 'Sudah Dibaca' : 'Belum Dibaca'}
</Tag>
</div>
{/* Additional Info */}
<div
style={{
marginTop: '16px',
padding: '12px',
backgroundColor: '#f6f9ff',
borderRadius: '6px',
border: '1px solid #d6e4ff',
}}
>
<div style={{ fontSize: '12px', color: '#595959' }}>
<strong>Catatan:</strong> Notifikasi ini telah dikirim ke engineer yang bersangkutan
untuk ditindaklanjuti sesuai dengan prosedur yang berlaku.
</div>
</div>
</div>
</Card>
);
});

View File

@@ -0,0 +1,90 @@
import React from 'react';
import { Card, Table, Tag, Typography } from 'antd';
import { ClockCircleOutlined } from '@ant-design/icons';
import dayjs from 'dayjs';
const { Text } = Typography;
const getDummyLogHistory = (notification) => {
if (!notification) return [];
return [
{
key: '1',
timestamp: dayjs().subtract(2, 'hour').format('DD-MM-YYYY HH:mm:ss'),
activity: 'Notification Created',
details: `System generated a ${notification.type} notification for: ${notification.issue}`,
},
{
key: '2',
timestamp: dayjs().subtract(1, 'hour').format('DD-MM-YYYY HH:mm:ss'),
activity: 'Notification Sent',
details: 'Sent to 2 engineers',
},
{
key: '3',
timestamp: dayjs().subtract(30, 'minute').format('DD-MM-YYYY HH:mm:ss'),
activity: 'Notification Read',
details: 'Read by Engineer A',
},
{
key: '4',
timestamp: dayjs().subtract(5, 'minute').format('DD-MM-YYYY HH:mm:ss'),
activity: 'Resend Triggered',
details: 'Notification resent by Admin',
},
];
};
const columns = [
{
title: 'Timestamp',
dataIndex: 'timestamp',
key: 'timestamp',
render: (text) => (
<span>
<ClockCircleOutlined style={{ marginRight: 8 }} />
{text}
</span>
),
},
{
title: 'Activity',
dataIndex: 'activity',
key: 'activity',
render: (text) => {
let color = 'blue';
if (text.includes('Created')) {
color = 'geekblue';
} else if (text.includes('Sent')) {
color = 'purple';
} else if (text.includes('Read')) {
color = 'green';
} else if (text.includes('Triggered')) {
color = 'orange';
}
return <Tag color={color}>{text.toUpperCase()}</Tag>;
},
},
{
title: 'Details',
dataIndex: 'details',
key: 'details',
},
];
const LogHistoryCard = ({ notificationData }) => {
const logHistoryData = getDummyLogHistory(notificationData);
return (
<Card title="Log History" size="small" style={{ height: '100%' }}>
<Table
columns={columns}
dataSource={logHistoryData}
pagination={{ pageSize: 3, size: 'small' }}
size="small"
/>
</Card>
);
};
export default LogHistoryCard;