fixing redirect detail notification tab
This commit is contained in:
@@ -53,6 +53,7 @@ import IndexHistoryEvent from './pages/history/event/IndexHistoryEvent';
|
|||||||
|
|
||||||
// Image Viewer
|
// Image Viewer
|
||||||
import ImageViewer from './Utils/ImageViewer';
|
import ImageViewer from './Utils/ImageViewer';
|
||||||
|
import RedirectWa from './pages/blank/RedirectWa';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return (
|
return (
|
||||||
@@ -72,6 +73,8 @@ const App = () => {
|
|||||||
element={<IndexVerificationSparepart />}
|
element={<IndexVerificationSparepart />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Route path="/redirect" element={<RedirectWa />} />
|
||||||
|
|
||||||
{/* Protected Routes */}
|
{/* Protected Routes */}
|
||||||
<Route path="/dashboard" element={<ProtectedRoute />}>
|
<Route path="/dashboard" element={<ProtectedRoute />}>
|
||||||
<Route path="home" element={<Home />} />
|
<Route path="home" element={<Home />} />
|
||||||
|
|||||||
@@ -70,7 +70,18 @@ async function ApiRequest({ method = 'GET', params = {}, prefix = '/', token = t
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const rawToken = localStorage.getItem('token');
|
const tokenRedirect = sessionStorage.getItem('token_redirect');
|
||||||
|
|
||||||
|
let rawToken = '';
|
||||||
|
|
||||||
|
if (tokenRedirect !== null) {
|
||||||
|
rawToken = tokenRedirect;
|
||||||
|
// console.log(`sessionStorage: ${tokenRedirect}`);
|
||||||
|
} else {
|
||||||
|
rawToken = localStorage.getItem('token');
|
||||||
|
// console.log(`localStorage: ${rawToken}`);
|
||||||
|
}
|
||||||
|
|
||||||
if (token && rawToken) {
|
if (token && rawToken) {
|
||||||
const cleanToken = rawToken.replace(/"/g, '');
|
const cleanToken = rawToken.replace(/"/g, '');
|
||||||
request.headers['Authorization'] = `Bearer ${cleanToken}`;
|
request.headers['Authorization'] = `Bearer ${cleanToken}`;
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ const allItems = [
|
|||||||
{
|
{
|
||||||
key: 'master-sparepart',
|
key: 'master-sparepart',
|
||||||
icon: <ToolOutlined style={{ fontSize: '19px' }} />,
|
icon: <ToolOutlined style={{ fontSize: '19px' }} />,
|
||||||
label: <Link to="/master/sparepart">sparepart</Link>,
|
label: <Link to="/master/sparepart">Sparepart</Link>,
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// key: 'master-shift',
|
// key: 'master-shift',
|
||||||
|
|||||||
49
src/pages/blank/RedirectWa.jsx
Normal file
49
src/pages/blank/RedirectWa.jsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { verifyRedirect } from '../../api/auth';
|
||||||
|
import { encryptData } from '../../components/Global/Formatter';
|
||||||
|
import NotFound from './NotFound';
|
||||||
|
import Waiting from './Waiting';
|
||||||
|
import NotificationDetailTab from '../notificationDetail/IndexNotificationDetail';
|
||||||
|
|
||||||
|
export default function RedirectWa() {
|
||||||
|
const [idData, setIdData] = useState(0);
|
||||||
|
const [ready, setReady] = useState(0);
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
// URLSearchParams untuk ambil query
|
||||||
|
const queryParams = new URLSearchParams(location.search);
|
||||||
|
const token = queryParams.get('token');
|
||||||
|
|
||||||
|
const handleInitForm = async (encodedToken) => {
|
||||||
|
const originalToken = decodeURIComponent(encodedToken);
|
||||||
|
// console.log(originalToken);
|
||||||
|
|
||||||
|
const response = await verifyRedirect({
|
||||||
|
tokenRedirect: originalToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('tes', response);
|
||||||
|
|
||||||
|
const tokenResult = JSON.stringify(response.data?.accessToken);
|
||||||
|
|
||||||
|
sessionStorage.setItem('token_redirect', tokenResult);
|
||||||
|
response.data.auth = true;
|
||||||
|
sessionStorage.setItem('session', encryptData(response?.data));
|
||||||
|
|
||||||
|
setIdData(response.data.data.idData);
|
||||||
|
|
||||||
|
setReady(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
handleInitForm(token);
|
||||||
|
}, [idData]);
|
||||||
|
|
||||||
|
if (ready == 0) return <Waiting />;
|
||||||
|
|
||||||
|
if (idData === 0) return <NotFound />;
|
||||||
|
|
||||||
|
return <NotificationDetailTab id={idData} />;
|
||||||
|
}
|
||||||
@@ -352,7 +352,7 @@ const ListJadwalShift = memo(function ListJadwalShift(props) {
|
|||||||
<Title level={3}>Jadwal Shift</Title>
|
<Title level={3}>Jadwal Shift</Title>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<Row>
|
{/* <Row>
|
||||||
<Col xs={24}>
|
<Col xs={24}>
|
||||||
<Row justify="end" align="middle" gutter={[8, 8]}>
|
<Row justify="end" align="middle" gutter={[8, 8]}>
|
||||||
<Col xs={24} sm={24} md={12} lg={12}>
|
<Col xs={24} sm={24} md={12} lg={12}>
|
||||||
@@ -383,7 +383,7 @@ const ListJadwalShift = memo(function ListJadwalShift(props) {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row> */}
|
||||||
|
|
||||||
<div style={{ marginTop: '24px' }}>
|
<div style={{ marginTop: '24px' }}>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
|
|||||||
@@ -1,6 +1,20 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { Layout, Card, Row, Col, Typography, Space, Button, Spin, Result, Input, message, Avatar, Tag } from 'antd';
|
import {
|
||||||
|
Layout,
|
||||||
|
Card,
|
||||||
|
Row,
|
||||||
|
Col,
|
||||||
|
Typography,
|
||||||
|
Space,
|
||||||
|
Button,
|
||||||
|
Spin,
|
||||||
|
Result,
|
||||||
|
Input,
|
||||||
|
message,
|
||||||
|
Avatar,
|
||||||
|
Tag,
|
||||||
|
} from 'antd';
|
||||||
import {
|
import {
|
||||||
ArrowLeftOutlined,
|
ArrowLeftOutlined,
|
||||||
CloseCircleFilled,
|
CloseCircleFilled,
|
||||||
@@ -20,7 +34,11 @@ import {
|
|||||||
SyncOutlined,
|
SyncOutlined,
|
||||||
SendOutlined,
|
SendOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { getNotificationDetail, createNotificationLog, getNotificationLogByNotificationId } from '../../api/notification';
|
import {
|
||||||
|
getNotificationDetail,
|
||||||
|
createNotificationLog,
|
||||||
|
getNotificationLogByNotificationId,
|
||||||
|
} from '../../api/notification';
|
||||||
|
|
||||||
const { Content } = Layout;
|
const { Content } = Layout;
|
||||||
const { Text, Paragraph, Link } = Typography;
|
const { Text, Paragraph, Link } = Typography;
|
||||||
@@ -41,12 +59,12 @@ const transformNotificationData = (apiData) => {
|
|||||||
description: apiData.message_error_issue || 'No details available',
|
description: apiData.message_error_issue || 'No details available',
|
||||||
timestamp: apiData.created_at
|
timestamp: apiData.created_at
|
||||||
? new Date(apiData.created_at).toLocaleString('id-ID', {
|
? new Date(apiData.created_at).toLocaleString('id-ID', {
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
month: '2-digit',
|
month: '2-digit',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
}) + ' WIB'
|
}) + ' WIB'
|
||||||
: 'N/A',
|
: 'N/A',
|
||||||
location: apiData.plant_sub_section_name || 'Location not specified',
|
location: apiData.plant_sub_section_name || 'Location not specified',
|
||||||
details: apiData.message_error_issue || 'No details available',
|
details: apiData.message_error_issue || 'No details available',
|
||||||
@@ -64,9 +82,9 @@ const transformNotificationData = (apiData) => {
|
|||||||
...activeSolution,
|
...activeSolution,
|
||||||
path_document: activeSolution.path_document
|
path_document: activeSolution.path_document
|
||||||
? activeSolution.path_document.replace(
|
? activeSolution.path_document.replace(
|
||||||
'/detail-notification/pdf/',
|
'/detail-notification/pdf/',
|
||||||
'/notification-detail/pdf/'
|
'/notification-detail/pdf/'
|
||||||
)
|
)
|
||||||
: activeSolution.path_document,
|
: activeSolution.path_document,
|
||||||
}, // Include the active solution data with fixed URL
|
}, // Include the active solution data with fixed URL
|
||||||
error_code: errorCodeData,
|
error_code: errorCodeData,
|
||||||
@@ -113,9 +131,17 @@ const getDummyUsers = (notification) => {
|
|||||||
const getStatusTag = (status) => {
|
const getStatusTag = (status) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'delivered':
|
case 'delivered':
|
||||||
return <Tag icon={<CheckCircleOutlined />} color="success">Delivered</Tag>;
|
return (
|
||||||
|
<Tag icon={<CheckCircleOutlined />} color="success">
|
||||||
|
Delivered
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
case 'sent':
|
case 'sent':
|
||||||
return <Tag icon={<SyncOutlined spin />} color="processing">Sent</Tag>;
|
return (
|
||||||
|
<Tag icon={<SyncOutlined spin />} color="processing">
|
||||||
|
Sent
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return <Tag color="error">Failed</Tag>;
|
return <Tag color="error">Failed</Tag>;
|
||||||
default:
|
default:
|
||||||
@@ -136,8 +162,9 @@ const getIconAndColor = (type) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const NotificationDetailTab = () => {
|
const NotificationDetailTab = (props) => {
|
||||||
const { notificationId } = useParams(); // Mungkin perlu disesuaikan jika route berbeda
|
const params = useParams(); // Mungkin perlu disesuaikan jika route berbeda
|
||||||
|
const notificationId = props.id ?? params.notificationId;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [notification, setNotification] = useState(null);
|
const [notification, setNotification] = useState(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@@ -161,12 +188,12 @@ const NotificationDetailTab = () => {
|
|||||||
id: log.notification_error_log_id,
|
id: log.notification_error_log_id,
|
||||||
timestamp: log.created_at
|
timestamp: log.created_at
|
||||||
? new Date(log.created_at).toLocaleString('id-ID', {
|
? new Date(log.created_at).toLocaleString('id-ID', {
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
month: '2-digit',
|
month: '2-digit',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
}) + ' WIB'
|
}) + ' WIB'
|
||||||
: 'N/A',
|
: 'N/A',
|
||||||
addedBy: {
|
addedBy: {
|
||||||
name: log.contact_name || 'Unknown',
|
name: log.contact_name || 'Unknown',
|
||||||
@@ -296,18 +323,21 @@ const NotificationDetailTab = () => {
|
|||||||
marginBottom: '24px',
|
marginBottom: '24px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Row justify="space-between" align="middle">
|
{!props.id && (
|
||||||
<Col>
|
<Row justify="space-between" align="middle">
|
||||||
<Button
|
<Col>
|
||||||
type="text"
|
<Button
|
||||||
icon={<ArrowLeftOutlined />}
|
type="text"
|
||||||
onClick={() => navigate('/notification')}
|
icon={<ArrowLeftOutlined />}
|
||||||
style={{ paddingLeft: 0 }}
|
onClick={() => navigate('/notification')}
|
||||||
>
|
style={{ paddingLeft: 0 }}
|
||||||
Back to notification list
|
>
|
||||||
</Button>
|
Back to notification list
|
||||||
</Col>
|
</Button>
|
||||||
</Row>
|
</Col>
|
||||||
|
</Row>
|
||||||
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: '#f6ffed',
|
backgroundColor: '#f6ffed',
|
||||||
@@ -426,24 +456,49 @@ const NotificationDetailTab = () => {
|
|||||||
|
|
||||||
{/* Kolom Kanan: User History */}
|
{/* Kolom Kanan: User History */}
|
||||||
<Col xs={24} lg={8}>
|
<Col xs={24} lg={8}>
|
||||||
<Card
|
<Card title="User History" size="small" style={{ height: '100%' }}>
|
||||||
title="User History"
|
<div
|
||||||
size="small"
|
style={{
|
||||||
style={{ height: '100%' }}
|
maxHeight: '400px',
|
||||||
>
|
overflowY: 'auto',
|
||||||
<div style={{ maxHeight: '400px', overflowY: 'auto', padding: '2px' }}>
|
padding: '2px',
|
||||||
<Space direction="vertical" size={2} style={{ width: '100%' }}>
|
}}
|
||||||
|
>
|
||||||
|
<Space
|
||||||
|
direction="vertical"
|
||||||
|
size={2}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
>
|
||||||
{getDummyUsers(notification).map((user) => (
|
{getDummyUsers(notification).map((user) => (
|
||||||
<Card key={user.id} size="small" style={{ width: '100%', margin: 0 }}>
|
<Card
|
||||||
|
key={user.id}
|
||||||
|
size="small"
|
||||||
|
style={{ width: '100%', margin: 0 }}
|
||||||
|
>
|
||||||
<Row align="middle" justify="space-between">
|
<Row align="middle" justify="space-between">
|
||||||
<Col>
|
<Col>
|
||||||
<Space align="center">
|
<Space align="center">
|
||||||
<Avatar size="large" icon={<UserOutlined />} />
|
<Avatar
|
||||||
|
size="large"
|
||||||
|
icon={<UserOutlined />}
|
||||||
|
/>
|
||||||
<div>
|
<div>
|
||||||
<Text strong>{user.name}</Text>
|
<Text strong>{user.name}</Text>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
|
<div
|
||||||
<PhoneOutlined style={{ color: '#8c8c8c' }} />
|
style={{
|
||||||
<Text type="secondary">{user.phone}</Text>
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '4px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PhoneOutlined
|
||||||
|
style={{
|
||||||
|
color: '#8c8c8c',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text type="secondary">
|
||||||
|
{user.phone}
|
||||||
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
@@ -457,7 +512,9 @@ const NotificationDetailTab = () => {
|
|||||||
size="small"
|
size="small"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
console.log(`Resend to ${user.name}`);
|
console.log(
|
||||||
|
`Resend to ${user.name}`
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Resend
|
Resend
|
||||||
@@ -505,9 +562,7 @@ const NotificationDetailTab = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} md={8}>
|
<Col xs={24} md={8}>
|
||||||
<Card
|
<Card bodyStyle={{ padding: '12px', textAlign: 'center' }}>
|
||||||
bodyStyle={{ padding: '12px', textAlign: 'center' }}
|
|
||||||
>
|
|
||||||
<Space>
|
<Space>
|
||||||
<HistoryOutlined
|
<HistoryOutlined
|
||||||
style={{ fontSize: '16px', color: '#1890ff' }}
|
style={{ fontSize: '16px', color: '#1890ff' }}
|
||||||
@@ -533,7 +588,7 @@ const NotificationDetailTab = () => {
|
|||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
>
|
>
|
||||||
{notification.error_code?.solution &&
|
{notification.error_code?.solution &&
|
||||||
notification.error_code.solution.length > 0 ? (
|
notification.error_code.solution.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
{notification.error_code.solution
|
{notification.error_code.solution
|
||||||
.filter((sol) => sol.is_active) // Hanya tampilkan solusi yang aktif
|
.filter((sol) => sol.is_active) // Hanya tampilkan solusi yang aktif
|
||||||
@@ -607,7 +662,7 @@ const NotificationDetailTab = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
) : null}
|
) : null}
|
||||||
{sol.type_solution === 'text' &&
|
{sol.type_solution === 'text' &&
|
||||||
sol.text_solution ? (
|
sol.text_solution ? (
|
||||||
<Card
|
<Card
|
||||||
size="small"
|
size="small"
|
||||||
bodyStyle={{
|
bodyStyle={{
|
||||||
@@ -668,7 +723,7 @@ const NotificationDetailTab = () => {
|
|||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
>
|
>
|
||||||
{notification.spareparts &&
|
{notification.spareparts &&
|
||||||
notification.spareparts.length > 0 ? (
|
notification.spareparts.length > 0 ? (
|
||||||
notification.spareparts.map((sparepart, index) => (
|
notification.spareparts.map((sparepart, index) => (
|
||||||
<Card
|
<Card
|
||||||
size="small"
|
size="small"
|
||||||
@@ -706,7 +761,7 @@ const NotificationDetailTab = () => {
|
|||||||
color:
|
color:
|
||||||
sparepart.sparepart_stok ===
|
sparepart.sparepart_stok ===
|
||||||
'Available' ||
|
'Available' ||
|
||||||
sparepart.sparepart_stok ===
|
sparepart.sparepart_stok ===
|
||||||
'available'
|
'available'
|
||||||
? '#52c41a'
|
? '#52c41a'
|
||||||
: '#ff4d4f',
|
: '#ff4d4f',
|
||||||
@@ -797,7 +852,9 @@ const NotificationDetailTab = () => {
|
|||||||
rows={2}
|
rows={2}
|
||||||
placeholder="Tuliskan update penanganan di sini..."
|
placeholder="Tuliskan update penanganan di sini..."
|
||||||
value={newLogDescription}
|
value={newLogDescription}
|
||||||
onChange={(e) => setNewLogDescription(e.target.value)}
|
onChange={(e) =>
|
||||||
|
setNewLogDescription(e.target.value)
|
||||||
|
}
|
||||||
disabled={submitLoading}
|
disabled={submitLoading}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
@@ -806,8 +863,18 @@ const NotificationDetailTab = () => {
|
|||||||
type={isAddingLog ? 'primary' : 'dashed'}
|
type={isAddingLog ? 'primary' : 'dashed'}
|
||||||
size="small"
|
size="small"
|
||||||
block
|
block
|
||||||
icon={submitLoading ? <LoadingOutlined /> : (!isAddingLog && <PlusOutlined />)}
|
icon={
|
||||||
onClick={isAddingLog ? handleSubmitLog : () => setIsAddingLog(true)}
|
submitLoading ? (
|
||||||
|
<LoadingOutlined />
|
||||||
|
) : (
|
||||||
|
!isAddingLog && <PlusOutlined />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onClick={
|
||||||
|
isAddingLog
|
||||||
|
? handleSubmitLog
|
||||||
|
: () => setIsAddingLog(true)
|
||||||
|
}
|
||||||
loading={submitLoading}
|
loading={submitLoading}
|
||||||
disabled={submitLoading}
|
disabled={submitLoading}
|
||||||
>
|
>
|
||||||
@@ -855,7 +922,6 @@ const NotificationDetailTab = () => {
|
|||||||
</Space>
|
</Space>
|
||||||
</Card>
|
</Card>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user