diff --git a/src/api/notification.jsx b/src/api/notification.jsx
index 2e04cf8..67daa9a 100644
--- a/src/api/notification.jsx
+++ b/src/api/notification.jsx
@@ -46,10 +46,20 @@ const getNotificationLogByNotificationId = async (notificationId) => {
return response.data;
};
+// Resend notification to specific user
+const resendNotificationToUser = async (notificationId, userId) => {
+ const response = await SendRequest({
+ method: 'post',
+ prefix: `notification/${notificationId}/resend/${userId}`,
+ });
+ return response.data;
+};
+
export {
getAllNotification,
getNotificationById,
getNotificationDetail,
createNotificationLog,
- getNotificationLogByNotificationId
+ getNotificationLogByNotificationId,
+ resendNotificationToUser,
};
diff --git a/src/components/Global/ApiRequest.jsx b/src/components/Global/ApiRequest.jsx
index 2a6fedd..73a9a78 100644
--- a/src/components/Global/ApiRequest.jsx
+++ b/src/components/Global/ApiRequest.jsx
@@ -30,18 +30,18 @@ instance.interceptors.response.use(
originalRequest._retry = true;
try {
- console.log('🔄 Refresh token dipanggil...');
+ // console.log('🔄 Refresh token dipanggil...');
const refreshRes = await refreshApi.post('/auth/refresh-token');
const newAccessToken = refreshRes.data.data.accessToken;
localStorage.setItem('token', newAccessToken);
- console.log('✅ Token refreshed successfully');
+ // console.log('✅ Token refreshed successfully');
// update token di header
instance.defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`;
originalRequest.headers['Authorization'] = `Bearer ${newAccessToken}`;
- console.log('🔁 Retrying original request...');
+ // console.log('🔁 Retrying original request...');
return instance(originalRequest);
} catch (refreshError) {
console.error(
@@ -81,24 +81,24 @@ async function ApiRequest({ method = 'GET', params = {}, prefix = '/', token = t
rawToken = localStorage.getItem('token');
// console.log(`localStorage: ${rawToken}`);
}
-
+
if (token && rawToken) {
const cleanToken = rawToken.replace(/"/g, '');
request.headers['Authorization'] = `Bearer ${cleanToken}`;
- console.log('🔐 Sending request with token:', cleanToken.substring(0, 20) + '...');
+ // console.log('🔐 Sending request with token:', cleanToken.substring(0, 20) + '...');
} else {
console.warn('⚠️ No token found in localStorage');
}
- console.log('📤 API Request:', { method, url: prefix, hasToken: !!rawToken });
+ // console.log('📤 API Request:', { method, url: prefix, hasToken: !!rawToken });
try {
const response = await instance(request);
- console.log('✅ API Response:', {
- url: prefix,
- status: response.status,
- statusCode: response.data?.statusCode,
- });
+ // console.log('✅ API Response:', {
+ // url: prefix,
+ // status: response.status,
+ // statusCode: response.data?.statusCode,
+ // });
return { ...response, error: false };
} catch (error) {
const status = error?.response?.status || 500;
@@ -143,17 +143,10 @@ async function cekError(status, message = '') {
const SendRequest = async (queryParams) => {
try {
const response = await ApiRequest(queryParams);
- console.log('📦 SendRequest response:', {
- hasError: response.error,
- status: response.status,
- statusCode: response.data?.statusCode,
- data: response.data,
- });
// If ApiRequest returned error flag, return error structure
if (response.error) {
const errorMsg = response.data?.message || response.statusText || 'Request failed';
- console.error('❌ SendRequest error response:', errorMsg);
// Return consistent error structure instead of empty array
return {
diff --git a/src/pages/contact/component/ListContact.jsx b/src/pages/contact/component/ListContact.jsx
index 84ab232..cc443b8 100644
--- a/src/pages/contact/component/ListContact.jsx
+++ b/src/pages/contact/component/ListContact.jsx
@@ -267,9 +267,6 @@ const ListContact = memo(function ListContact(props) {
}
}
- // Backend doesn't support is_active filter or order parameter
- // Contact hanya supports: criteria, name, code, limit, page
-
const queryParams = new URLSearchParams();
Object.entries(searchParams).forEach(([key, value]) => {
if (value !== '' && value !== null && value !== undefined) {
@@ -309,11 +306,10 @@ const ListContact = memo(function ListContact(props) {
// Listen for saved contact data
useEffect(() => {
if (props.lastSavedContact) {
- fetchContacts(); // Refetch all contacts when data is saved
+ fetchContacts();
}
}, [props.lastSavedContact]);
- // Get contacts (already filtered by backend)
const getFilteredContacts = () => {
return filteredContacts;
};
@@ -326,7 +322,7 @@ const ListContact = memo(function ListContact(props) {
const showAddModal = () => {
props.setSelectedData(null);
props.setActionMode('add');
- // Pass the current active tab to determine contact type
+
props.setContactType?.(activeTab);
};
diff --git a/src/pages/master/plantSubSection/component/DetailPlantSubSection.jsx b/src/pages/master/plantSubSection/component/DetailPlantSubSection.jsx
index 5c942a1..458771a 100644
--- a/src/pages/master/plantSubSection/component/DetailPlantSubSection.jsx
+++ b/src/pages/master/plantSubSection/component/DetailPlantSubSection.jsx
@@ -38,7 +38,7 @@ const DetailPlantSubSection = (props) => {
return;
}
- console.log(`📝 Input change: ${name} = ${value}`);
+ // console.log(`📝 Input change: ${name} = ${value}`);
if (name) {
setFormData((prev) => ({
@@ -74,16 +74,20 @@ const DetailPlantSubSection = (props) => {
return;
try {
- console.log('💾 Current formData before save:', formData);
+ // console.log('💾 Current formData before save:', formData);
const payload = {
plant_sub_section_name: formData.plant_sub_section_name,
- plant_sub_section_description: (formData.plant_sub_section_description && formData.plant_sub_section_description.trim() !== '') ? formData.plant_sub_section_description : ' ',
+ plant_sub_section_description:
+ formData.plant_sub_section_description &&
+ formData.plant_sub_section_description.trim() !== ''
+ ? formData.plant_sub_section_description
+ : ' ',
table_name_value: formData.table_name_value, // Fix field name
is_active: formData.is_active,
};
- console.log('📤 Payload to be sent:', payload);
+ // console.log('📤 Payload to be sent:', payload);
const response =
props.actionMode === 'edit'
@@ -126,17 +130,17 @@ const DetailPlantSubSection = (props) => {
};
useEffect(() => {
- console.log('🔄 Modal state changed:', {
- showModal: props.showModal,
- actionMode: props.actionMode,
- selectedData: props.selectedData,
- });
+ // console.log('🔄 Modal state changed:', {
+ // showModal: props.showModal,
+ // actionMode: props.actionMode,
+ // selectedData: props.selectedData,
+ // });
if (props.selectedData) {
- console.log('📋 Setting form data from selectedData:', props.selectedData);
+ // console.log('📋 Setting form data from selectedData:', props.selectedData);
setFormData(props.selectedData);
} else {
- console.log('📋 Resetting to default data');
+ // console.log('📋 Resetting to default data');
setFormData(defaultData);
}
}, [props.showModal, props.selectedData, props.actionMode]);
diff --git a/src/pages/master/shift/component/DetailShift.jsx b/src/pages/master/shift/component/DetailShift.jsx
index ab2d3bc..ec728d4 100644
--- a/src/pages/master/shift/component/DetailShift.jsx
+++ b/src/pages/master/shift/component/DetailShift.jsx
@@ -112,9 +112,9 @@ const DetailShift = (props) => {
is_active: formData.is_active,
};
- console.log('Payload yang dikirim:', payload);
- console.log('Type start_time:', typeof payload.start_time, payload.start_time);
- console.log('Type end_time:', typeof payload.end_time, payload.end_time);
+ // console.log('Payload yang dikirim:', payload);
+ // console.log('Type start_time:', typeof payload.start_time, payload.start_time);
+ // console.log('Type end_time:', typeof payload.end_time, payload.end_time);
const response =
props.actionMode === 'edit'
diff --git a/src/pages/master/sparepart/component/DetailSparepart.jsx b/src/pages/master/sparepart/component/DetailSparepart.jsx
index d68c864..85482b6 100644
--- a/src/pages/master/sparepart/component/DetailSparepart.jsx
+++ b/src/pages/master/sparepart/component/DetailSparepart.jsx
@@ -95,11 +95,11 @@ const DetailSparepart = (props) => {
const newFile = fileList.length > 0 ? fileList[0] : null;
if (newFile && newFile.originFileObj) {
- console.log('Uploading file:', newFile.originFileObj);
+ // console.log('Uploading file:', newFile.originFileObj);
const uploadResponse = await uploadFile(newFile.originFileObj, 'images');
// Log untuk debugging
- console.log('Upload response:', uploadResponse);
+ // console.log('Upload response:', uploadResponse);
// Cek berbagai kemungkinan struktur respons dari API
let uploadedUrl = null;
@@ -169,7 +169,7 @@ const DetailSparepart = (props) => {
}
if (uploadedUrl) {
- console.log('Successfully extracted image URL:', uploadedUrl);
+ // console.log('Successfully extracted image URL:', uploadedUrl);
imageUrl = uploadedUrl;
} else {
console.error('Upload response structure:', uploadResponse);
@@ -209,7 +209,10 @@ const DetailSparepart = (props) => {
sparepart_name: formData.sparepart_name, // Wajib
};
- payload.sparepart_description = (formData.sparepart_description && formData.sparepart_description.trim() !== '') ? formData.sparepart_description : ' ';
+ payload.sparepart_description =
+ formData.sparepart_description && formData.sparepart_description.trim() !== ''
+ ? formData.sparepart_description
+ : ' ';
if (formData.sparepart_model && formData.sparepart_model.trim() !== '') {
payload.sparepart_model = formData.sparepart_model;
}
@@ -233,13 +236,13 @@ const DetailSparepart = (props) => {
payload.sparepart_foto = imageUrl;
}
- console.log('Sending payload:', payload);
+ // console.log('Sending payload:', payload);
const response = formData.sparepart_id
? await updateSparepart(formData.sparepart_id, payload)
: await createSparepart(payload);
- console.log('API response:', response);
+ // console.log('API response:', response);
if (response && (response.statusCode === 200 || response.statusCode === 201)) {
NotifOk({
diff --git a/src/pages/master/unit/component/ListUnit.jsx b/src/pages/master/unit/component/ListUnit.jsx
index 2181f64..ada8871 100644
--- a/src/pages/master/unit/component/ListUnit.jsx
+++ b/src/pages/master/unit/component/ListUnit.jsx
@@ -164,7 +164,7 @@ const ListUnit = memo(function ListUnit(props) {
const handleDelete = async (param) => {
try {
const response = await deleteUnit(param.unit_id);
- console.log('deleteUnit response:', response);
+ // console.log('deleteUnit response:', response);
if (response.statusCode === 200) {
NotifAlert({
diff --git a/src/pages/notification/component/ListNotification.jsx b/src/pages/notification/component/ListNotification.jsx
index a959cd9..9889007 100644
--- a/src/pages/notification/component/ListNotification.jsx
+++ b/src/pages/notification/component/ListNotification.jsx
@@ -38,7 +38,11 @@ import {
SearchOutlined,
} from '@ant-design/icons';
import { useNavigate, Link as RouterLink } from 'react-router-dom';
-import { getAllNotification, getNotificationLogByNotificationId } from '../../../api/notification';
+import {
+ getAllNotification,
+ getNotificationLogByNotificationId,
+ getNotificationDetail,
+} from '../../../api/notification';
const { Text, Paragraph, Link: AntdLink } = Typography;
@@ -77,31 +81,6 @@ const transformNotificationData = (apiData) => {
}));
};
-// Dummy data untuk user history
-const userHistoryData = [
- {
- id: '1',
- name: 'John Doe',
- phone: '081234567890',
- status: 'Delivered',
- timestamp: '04-11-2025 11:40 WIB',
- },
- {
- id: '2',
- name: 'Jane Smith',
- phone: '087654321098',
- status: 'Delivered',
- timestamp: '04-11-2025 11:41 WIB',
- },
- {
- id: '3',
- name: 'Peter Jones',
- phone: '082345678901',
- status: 'Delivered',
- timestamp: '04-11-2025 11:42 WIB',
- },
-];
-
const ListNotification = memo(function ListNotification(props) {
const [notifications, setNotifications] = useState([]);
const [activeTab, setActiveTab] = useState('all');
@@ -113,6 +92,8 @@ const ListNotification = memo(function ListNotification(props) {
const [selectedNotification, setSelectedNotification] = useState(null);
const [logHistoryData, setLogHistoryData] = useState([]);
const [logLoading, setLogLoading] = useState(false);
+ const [userHistoryData, setUserHistoryData] = useState([]);
+ const [userLoading, setUserLoading] = useState(false);
const [pagination, setPagination] = useState({
current_page: 1,
current_limit: 10,
@@ -290,6 +271,42 @@ const ListNotification = memo(function ListNotification(props) {
}
};
+ // Fetch user history from API
+ const fetchUserHistory = async (notificationId) => {
+ try {
+ setUserLoading(true);
+
+ const response = await getNotificationDetail(notificationId);
+
+ if (response && response.data && response.data.users) {
+ // Transform API data to component format
+ const transformedUsers = response.data.users.map((user) => ({
+ id: user.notification_error_user_id.toString(),
+ name: user.contact_name,
+ phone: user.contact_phone,
+ status: user.is_send ? 'Delivered' : 'Pending',
+ timestamp: user.created_at
+ ? new Date(user.created_at).toLocaleString('id-ID', {
+ day: '2-digit',
+ month: '2-digit',
+ year: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit',
+ }) + ' WIB'
+ : 'N/A',
+ }));
+ setUserHistoryData(transformedUsers);
+ } else {
+ setUserHistoryData([]);
+ }
+ } catch (err) {
+ console.error('Error fetching user history:', err);
+ setUserHistoryData([]); // Set empty array on error
+ } finally {
+ setUserLoading(false);
+ }
+ };
+
const tabButtonStyle = (isActive) => ({
padding: '12px 16px',
border: 'none',
@@ -467,8 +484,18 @@ const ListNotification = memo(function ListNotification(props) {
border: '1px solid #1890ff',
borderRadius: '4px',
}}
- onClick={(e) => {
+ onClick={async (e) => {
e.stopPropagation();
+
+ setSelectedNotification(notification);
+
+ // Extract notification ID from the notification object
+ const notificationId =
+ notification.id.split('-')[1];
+
+ // Fetch user history for the selected notification
+ await fetchUserHistory(notificationId);
+
setModalContent('user');
}}
/>
@@ -535,37 +562,69 @@ const ListNotification = memo(function ListNotification(props) {
const renderUserHistory = () => (
<>
-