enhance search and pagination functionality to ListNotification component

This commit is contained in:
2025-11-20 19:45:45 +07:00
parent 73b5cd6e97
commit f304a28493

View File

@@ -12,6 +12,8 @@ import {
Modal,
Tag,
message,
Spin,
Pagination,
} from 'antd';
import {
CloseCircleFilled,
@@ -33,6 +35,7 @@ import {
FilePdfOutlined,
PlusOutlined,
ExclamationCircleOutlined,
SearchOutlined,
} from '@ant-design/icons';
import { useNavigate, Link as RouterLink } from 'react-router-dom';
import { getAllNotification } from '../../../api/notification';
@@ -129,25 +132,61 @@ const ListNotification = memo(function ListNotification(props) {
const [notifications, setNotifications] = useState([]);
const [activeTab, setActiveTab] = useState('all');
const [searchTerm, setSearchTerm] = useState('');
const [searchValue, setSearchValue] = useState('');
const [loading, setLoading] = useState(false);
const [modalContent, setModalContent] = useState(null); // 'user', 'log', 'details', or null
const [isAddingLog, setIsAddingLog] = useState(false);
const [selectedNotification, setSelectedNotification] = useState(null);
const [pagination, setPagination] = useState({
current_page: 1,
current_limit: 10,
total_limit: 0,
total_page: 1,
});
const navigate = useNavigate();
// Fetch notifications from API
const fetchNotifications = async () => {
setLoading(true);
try {
const response = await getAllNotification();
if (response && response.data) {
const transformedData = transformNotificationData(response.data);
setNotifications(transformedData);
// Update pagination with mock data (since API doesn't provide pagination info)
const totalItems = transformedData.length;
setPagination((prev) => ({
...prev,
total_limit: totalItems,
total_page: Math.ceil(totalItems / prev.current_limit),
}));
}
} catch (error) {
console.error('Error fetching notifications:', error);
setNotifications([]);
} finally {
setTimeout(() => {
setLoading(false);
}, 500);
}
};
const handlePaginationChange = (page, pageSize) => {
setPagination((prev) => ({
...prev,
current_page: page,
current_limit: pageSize,
}));
};
// Get paginated notifications
const getPaginatedNotifications = () => {
const startIndex = (pagination.current_page - 1) * pagination.current_limit;
const endIndex = startIndex + pagination.current_limit;
return filteredNotifications.slice(startIndex, endIndex);
};
useEffect(() => {
const token = localStorage.getItem('token');
if (!token) {
@@ -200,6 +239,15 @@ const ListNotification = memo(function ListNotification(props) {
);
};
const handleSearch = () => {
setSearchTerm(searchValue);
};
const handleSearchClear = () => {
setSearchValue('');
setSearchTerm('');
};
const filteredNotifications = notifications
.filter((n) => {
const matchesTab =
@@ -210,8 +258,8 @@ const ListNotification = memo(function ListNotification(props) {
})
.filter((n) => {
if (!searchTerm) return true;
const searchableText =
`${n.title} ${n.issue} ${n.description} ${n.location} ${n.details}`.toLowerCase();
// Search by title and error code name
const searchableText = `${n.title} ${n.issue}`.toLowerCase();
return searchableText.includes(searchTerm.toLowerCase());
});
@@ -230,14 +278,17 @@ const ListNotification = memo(function ListNotification(props) {
transition: 'all 0.3s',
});
const renderDeviceNotifications = () => (
<Space direction="vertical" size="middle" style={{ display: 'flex' }}>
{filteredNotifications.length === 0 ? (
<div style={{ textAlign: 'center', padding: '40px 0', color: '#8c8c8c' }}>
Tidak ada notifikasi
</div>
) : (
filteredNotifications.map((notification) => {
const renderDeviceNotifications = () => {
const paginatedNotifications = getPaginatedNotifications();
return (
<Space direction="vertical" size="middle" style={{ display: 'flex' }}>
{filteredNotifications.length === 0 ? (
<div style={{ textAlign: 'center', padding: '40px 0', color: '#8c8c8c' }}>
Tidak ada notifikasi
</div>
) : (
paginatedNotifications.map((notification) => {
const { IconComponent, color, bgColor } = getIconAndColor(notification.type);
return (
<Card
@@ -333,7 +384,10 @@ const ListNotification = memo(function ListNotification(props) {
</Space>
<Space>
<LinkOutlined />
<AntdLink href={notification.link} target="_blank">
<AntdLink
href={notification.link}
target="_blank"
>
{notification.link}
</AntdLink>
<Button
@@ -381,14 +435,20 @@ const ListNotification = memo(function ListNotification(props) {
}}
/>
<RouterLink
to={`/detail-notification/${notification.id.split('-')[1]}`}
to={`/detail-notification/${
notification.id.split('-')[1]
}`}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
>
<Button
type="text"
icon={<EyeOutlined style={{ color: '#1890ff' }} />}
icon={
<EyeOutlined
style={{ color: '#1890ff' }}
/>
}
title="Details"
style={{
border: '1px solid #1890ff',
@@ -422,8 +482,9 @@ const ListNotification = memo(function ListNotification(props) {
);
})
)}
</Space>
);
</Space>
);
};
const renderUserHistory = () => (
<>
@@ -635,9 +696,7 @@ const ListNotification = memo(function ListNotification(props) {
<Text type="secondary" style={{ fontSize: '12px' }}>
Treshold
</Text>
<div style={{ fontWeight: 500 }}>
N/A
</div>
<div style={{ fontWeight: 500 }}>N/A</div>
</Col>
</Row>
</div>
@@ -1042,23 +1101,23 @@ const ListNotification = memo(function ListNotification(props) {
</Space>
</Card>
{logHistoryData.map((log) => (
<Card
key={log.id}
size="small"
bodyStyle={{ padding: '8px 12px' }}
<Card
key={log.id}
size="small"
bodyStyle={{ padding: '8px 12px' }}
>
<Paragraph
style={{ fontSize: '12px', margin: 0 }}
ellipsis={{ rows: 2 }}
>
<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>
))}
<Text strong>{log.addedBy.name}:</Text>{' '}
{log.description}
</Paragraph>
<Text type="secondary" style={{ fontSize: '11px' }}>
{log.timestamp}
</Text>
</Card>
))}
</Space>
</Card>
</Col>
@@ -1087,17 +1146,35 @@ const ListNotification = memo(function ListNotification(props) {
Riwayat notifikasi yang dikirim ke engineer
</p>
<Row
justify="space-between"
align="middle"
style={{ marginBottom: '24px' }}
>
<Col>
<Row justify="space-between" align="middle" gutter={[8, 8]}>
<Col xs={24} sm={24} md={12} lg={12}>
<Input.Search
placeholder="Search notifications..."
onSearch={setSearchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
style={{ width: 300 }}
placeholder="Search by notification name or error code name..."
value={searchValue}
onChange={(e) => {
const value = e.target.value;
setSearchValue(value);
if (value === '') {
handleSearchClear();
}
}}
onSearch={handleSearch}
allowClear={{
clearIcon: <span onClick={handleSearchClear}></span>,
}}
enterButton={
<Button
type="primary"
icon={<SearchOutlined />}
style={{
backgroundColor: '#23A55A',
borderColor: '#23A55A',
}}
>
Search
</Button>
}
size="large"
/>
</Col>
</Row>
@@ -1135,7 +1212,29 @@ const ListNotification = memo(function ListNotification(props) {
</div>
</div>
{renderDeviceNotifications()}
<Spin spinning={loading}>
{renderDeviceNotifications()}
</Spin>
{/* PAGINATION */}
<Row justify="space-between" align="middle" style={{ marginTop: '16px' }}>
<Col>
<div>
Menampilkan {pagination.current_limit} data halaman{' '}
{pagination.current_page} dari total {pagination.total_limit} data
</div>
</Col>
<Col>
<Pagination
showSizeChanger
onChange={handlePaginationChange}
onShowSizeChange={handlePaginationChange}
current={pagination.current_page}
pageSize={pagination.current_limit}
total={pagination.total_limit}
/>
</Col>
</Row>
</Col>
</Row>
</Card>