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