lavoce #23

Merged
bragaz_rexita merged 7 commits from lavoce into main 2025-11-28 05:10:27 +00:00
2 changed files with 242 additions and 202 deletions
Showing only changes of commit 14f8a5d472 - Show all commits

View File

@@ -1,9 +1,21 @@
import { SendRequest } from '../components/Global/ApiRequest'; import { SendRequest } from '../components/Global/ApiRequest';
export const getAllNotification = async () => { const getAllNotification = async (queryParams) => {
const response = await SendRequest({ const response = await SendRequest({
method: 'get', method: 'get',
prefix: 'notification', prefix: `notification?${queryParams.toString()}`,
}); });
return response.data; return response.data;
}; };
const getNotificationById = async (id) => {
const response = await SendRequest({
method: 'get',
prefix: `notification/${id}`,
});
return response.data;
};
export { getAllNotification, getNotificationById };

View File

@@ -146,22 +146,44 @@ const ListNotification = memo(function ListNotification(props) {
const navigate = useNavigate(); const navigate = useNavigate();
// Fetch notifications from API // Fetch notifications from API
const fetchNotifications = async () => { const fetchNotifications = async (page = 1, limit = 10, isRead = null) => {
setLoading(true); setLoading(true);
try { try {
const response = await getAllNotification(); const queryParams = new URLSearchParams({
page: page.toString(),
limit: limit.toString(),
});
if (isRead !== null) {
queryParams.append('is_read', isRead.toString());
}
const response = await getAllNotification(queryParams);
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) // Update pagination with API response or calculate from data
if (response.paging) {
setPagination({
current_page: response.paging.current_page || page,
current_limit: response.paging.current_limit || limit,
total_limit: response.paging.total_limit || transformedData.length,
total_page:
response.paging.total_page || Math.ceil(transformedData.length / limit),
});
} else {
// Fallback: calculate pagination from data
const totalItems = transformedData.length; const totalItems = transformedData.length;
setPagination((prev) => ({ setPagination((prev) => ({
...prev, ...prev,
current_page: page,
current_limit: limit,
total_limit: totalItems, total_limit: totalItems,
total_page: Math.ceil(totalItems / prev.current_limit), total_page: Math.ceil(totalItems / limit),
})); }));
} }
}
} catch (error) { } catch (error) {
console.error('Error fetching notifications:', error); console.error('Error fetching notifications:', error);
setNotifications([]); setNotifications([]);
@@ -178,13 +200,10 @@ const ListNotification = memo(function ListNotification(props) {
current_page: page, current_page: page,
current_limit: pageSize, current_limit: pageSize,
})); }));
};
// Get paginated notifications // Fetch notifications with new pagination
const getPaginatedNotifications = () => { const isReadFilter = activeTab === 'read' ? true : activeTab === 'unread' ? false : null;
const startIndex = (pagination.current_page - 1) * pagination.current_limit; fetchNotifications(page, pageSize, isReadFilter);
const endIndex = startIndex + pagination.current_limit;
return filteredNotifications.slice(startIndex, endIndex);
}; };
useEffect(() => { useEffect(() => {
@@ -194,9 +213,10 @@ const ListNotification = memo(function ListNotification(props) {
return; return;
} }
// Fetch notifications on component mount // Fetch notifications on component mount and when tab changes
fetchNotifications(); const isReadFilter = activeTab === 'read' ? true : activeTab === 'unread' ? false : null;
}, []); fetchNotifications(pagination.current_page, pagination.current_limit, isReadFilter);
}, [activeTab]);
const getIconAndColor = (type) => { const getIconAndColor = (type) => {
switch (type) { switch (type) {
@@ -248,22 +268,17 @@ const ListNotification = memo(function ListNotification(props) {
setSearchTerm(''); setSearchTerm('');
}; };
const filteredNotifications = notifications const getUnreadCount = () => notifications.filter((n) => !n.isRead).length;
.filter((n) => {
const matchesTab = // Filter notifications based on search term
activeTab === 'all' || const getFilteredNotifications = () => {
(activeTab === 'unread' && !n.isRead) || if (!searchTerm) return notifications;
(activeTab === 'read' && n.isRead);
return matchesTab;
})
.filter((n) => {
if (!searchTerm) return true;
// Search by title and error code name // Search by title and error code name
return notifications.filter((n) => {
const searchableText = `${n.title} ${n.issue}`.toLowerCase(); const searchableText = `${n.title} ${n.issue}`.toLowerCase();
return searchableText.includes(searchTerm.toLowerCase()); return searchableText.includes(searchTerm.toLowerCase());
}); });
};
const getUnreadCount = () => notifications.filter((n) => !n.isRead).length;
const tabButtonStyle = (isActive) => ({ const tabButtonStyle = (isActive) => ({
padding: '12px 16px', padding: '12px 16px',
@@ -279,8 +294,7 @@ const ListNotification = memo(function ListNotification(props) {
}); });
const renderDeviceNotifications = () => { const renderDeviceNotifications = () => {
const paginatedNotifications = getPaginatedNotifications(); const filteredNotifications = getFilteredNotifications();
return ( return (
<Space direction="vertical" size="middle" style={{ display: 'flex' }}> <Space direction="vertical" size="middle" style={{ display: 'flex' }}>
{filteredNotifications.length === 0 ? ( {filteredNotifications.length === 0 ? (
@@ -288,8 +302,10 @@ const ListNotification = memo(function ListNotification(props) {
Tidak ada notifikasi Tidak ada notifikasi
</div> </div>
) : ( ) : (
paginatedNotifications.map((notification) => { filteredNotifications.map((notification) => {
const { IconComponent, color, bgColor } = getIconAndColor(notification.type); const { IconComponent, color, bgColor } = getIconAndColor(
notification.type
);
return ( return (
<Card <Card
key={notification.id} key={notification.id}
@@ -300,7 +316,13 @@ const ListNotification = memo(function ListNotification(props) {
}} }}
onClick={() => handleMarkAsRead(notification.id)} onClick={() => handleMarkAsRead(notification.id)}
> >
<div style={{ display: 'flex', gap: '16px', alignItems: 'flex-start' }}> <div
style={{
display: 'flex',
gap: '16px',
alignItems: 'flex-start',
}}
>
<div <div
style={{ style={{
width: '40px', width: '40px',
@@ -357,10 +379,17 @@ const ListNotification = memo(function ListNotification(props) {
}} }}
> >
<MailOutlined <MailOutlined
style={{ marginTop: '4px', color: '#1890ff' }} style={{
marginTop: '4px',
color: '#1890ff',
}}
/> />
<Paragraph <Paragraph
style={{ color: '#595959', margin: 0, flex: 1 }} style={{
color: '#595959',
margin: 0,
flex: 1,
}}
> >
{notification.details} {notification.details}
</Paragraph> </Paragraph>
@@ -1212,16 +1241,15 @@ const ListNotification = memo(function ListNotification(props) {
</div> </div>
</div> </div>
<Spin spinning={loading}> <Spin spinning={loading}>{renderDeviceNotifications()}</Spin>
{renderDeviceNotifications()}
</Spin>
{/* PAGINATION */} {/* PAGINATION */}
<Row justify="space-between" align="middle" style={{ marginTop: '16px' }}> <Row justify="space-between" align="middle" style={{ marginTop: '16px' }}>
<Col> <Col>
<div> <div>
Menampilkan {pagination.current_limit} data halaman{' '} Menampilkan {pagination.current_limit} data halaman{' '}
{pagination.current_page} dari total {pagination.total_limit} data {pagination.current_page} dari total {pagination.total_limit}{' '}
data
</div> </div>
</Col> </Col>
<Col> <Col>