sbt-idp/cope2n-fe/src/pages/reviews2/index.tsx

1138 lines
34 KiB
TypeScript
Raw Normal View History

2024-05-02 12:14:47 +00:00
import {
ArrowLeftOutlined,
ArrowRightOutlined,
CheckCircleOutlined,
ClockCircleFilled,
2024-05-03 02:52:15 +00:00
CopyOutlined,
2024-05-02 12:14:47 +00:00
FullscreenExitOutlined,
FullscreenOutlined,
} from '@ant-design/icons';
import { t } from '@lingui/macro';
import { Viewer } from '@react-pdf-viewer/core';
2024-05-31 09:55:02 +00:00
import '@react-pdf-viewer/core/lib/styles/index.css';
import { defaultLayoutPlugin } from '@react-pdf-viewer/default-layout';
import '@react-pdf-viewer/default-layout/lib/styles/index.css';
2024-05-02 12:14:47 +00:00
import {
Button,
DatePicker,
Form,
Input,
InputNumber,
message,
Modal,
notification,
Select,
Spin,
Tag,
} from 'antd';
2024-06-03 06:59:26 +00:00
import { isEmpty } from 'lodash-es';
2024-07-04 09:17:32 +00:00
import { useEffect, useMemo, useState } from 'react';
2024-05-02 12:14:47 +00:00
import Lightbox from 'react-awesome-lightbox';
import 'react-awesome-lightbox/build/style.css';
import { useHotkeys } from 'react-hotkeys-hook';
2024-06-03 06:59:26 +00:00
import { useSearchParams } from 'react-router-dom';
import { getErrorMessage } from 'utils/error-handler';
import {
2024-06-03 06:59:26 +00:00
addRecentRequest,
2024-06-28 04:29:05 +00:00
confirmRequest,
fetchAllRequests,
fetchRequest,
updateRevisedDataByFile,
} from './api';
2024-05-03 02:52:15 +00:00
import {
counter_measure_map,
2024-12-03 09:18:20 +00:00
DOCTYPE,
2024-05-23 11:48:21 +00:00
FEEDBACK_ACCURACY,
FEEDBACK_RESULT,
PREDICTED_RESULT,
2024-05-03 02:52:15 +00:00
REASON_BAD_QUALITY,
2024-05-23 11:48:21 +00:00
REVIEWED_RESULT,
2024-05-03 02:52:15 +00:00
SOLUTION_BAD_QUALITY,
2024-05-23 11:48:21 +00:00
SOURCE_KEYS,
SOURCE_OBJECT_NAMES,
2024-05-03 02:52:15 +00:00
SUBSIDIARIES,
} from './const';
2024-05-02 12:14:47 +00:00
import FileCard from './FileCard';
2024-06-03 06:59:26 +00:00
import RecentRequest from './RecentRequest';
2024-06-06 04:05:05 +00:00
import './style.css';
2024-05-02 12:14:47 +00:00
const ReviewPage = () => {
const [loading, setLoading] = useState(false);
const [fullscreen, setFullscreen] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const [selectedFileId, setSelectedFileId] = useState(0);
const [selectedFileData, setSelectedFileData] = useState(null);
2024-05-23 11:48:21 +00:00
const [selectedFileDataSource, setSelectedFileDataSource] = useState({});
2024-05-02 12:14:47 +00:00
const [selectedFileName, setSelectedFileName] = useState(null);
2024-06-20 06:27:41 +00:00
const [isOpenConfirmRequest, setIsOpenConfirmRequest] = useState(false);
2024-05-02 12:14:47 +00:00
// Default date range: 1 month ago to today
const [filterDateRange, setFilterDateRange] = useState(['', '']);
const [filterSubsidiaries, setFilterSubsidiaries] = useState('SEAO');
const [filterAccuracy, setFilterAccuracy] = useState(100);
const [filterReviewState, setFilterReviewState] = useState('all');
const [filterIncludeTests, setFilterIncludesTests] = useState('true');
2024-12-03 09:18:20 +00:00
const [filterDoctype, setFilterDoctype] = useState(null);
const [filterFeedbackResult, setFilterFeedbackResult] = useState('');
const [filterPredictResult, setFilterPredictResult] = useState('');
const [filterReviewedResult, setFilterReviewedResult] = useState('');
2024-05-02 12:14:47 +00:00
// const [requests, setRequests] = useState([]);
const [currentRequest, setCurrentRequest] = useState(null);
const [currentRequestIndex, setCurrentRequestIndex] = useState(1);
const [hasNextRequest, setHasNextRequest] = useState(true);
const [totalRequests, setTotalPages] = useState(0);
const [pageIndexToGoto, setPageIndexToGoto] = useState(1);
const [reason, setReason] = useState('');
const [otherReason, setOtherReason] = useState('');
const [solution, setSolution] = useState('');
const [otherSolution, setOtherSolution] = useState('');
2024-05-09 09:12:14 +00:00
const [imageLoading, setImageLoading] = useState(false);
2024-05-31 09:55:02 +00:00
const defaultLayoutPluginInstance = defaultLayoutPlugin();
2024-06-03 06:59:26 +00:00
let [searchParams, setSearchParams] = useSearchParams();
useEffect(() => {
const request_id = searchParams.get('request_id');
if (request_id) {
setLoading(true);
setTotalPages(1);
setHasNextRequest(false);
const requestData = fetchRequest(request_id);
requestData
.then(async (data) => {
if (data) setCurrentRequest(data);
setAndLoadSelectedFile(data, 0);
})
.finally(() => {
setLoading(false);
});
} else {
setCurrentRequestIndex(1);
fetchAllRequests(
filterDateRange,
filterSubsidiaries,
filterReviewState,
filterIncludeTests,
2024-12-03 09:18:20 +00:00
filterDoctype,
filterFeedbackResult,
filterPredictResult,
filterReviewedResult,
2024-06-03 06:59:26 +00:00
1,
1,
filterAccuracy,
).then((data) => {
setTotalPages(data?.page?.total_requests);
setHasNextRequest(1 < data?.page?.total_requests);
const firstRequest = fetchRequest(
data?.subscription_requests[0].RequestID,
);
firstRequest.then(async (data) => {
if (data) setCurrentRequest(data);
setAndLoadSelectedFile(data, 0);
});
});
}
}, []);
2024-06-06 06:56:05 +00:00
// useEffect(() => {
// if (reason) {
// setSolution(counter_measure_map[reason]);
// }
// }, [reason]);
2024-05-02 12:14:47 +00:00
2024-05-23 11:48:21 +00:00
const updateSelectedFileDataSource = (fileContent) => {
let tempData = {};
SOURCE_KEYS.forEach((k) => {
tempData[k] = {};
SOURCE_OBJECT_NAMES.forEach((name) => {
tempData[k][name] = fileContent[name][k];
});
2024-06-06 04:05:05 +00:00
if (!isEmpty(tempData[k][PREDICTED_RESULT])) {
let isEmptyResult = false;
if (isEmpty(tempData[k][REVIEWED_RESULT])) {
isEmptyResult = true;
}
2024-06-19 08:33:33 +00:00
if (
Array.isArray(tempData[k][REVIEWED_RESULT]) &&
tempData[k][REVIEWED_RESULT].length > 0
) {
2024-06-06 04:05:05 +00:00
isEmptyResult = tempData[k][REVIEWED_RESULT].every((r: any) =>
isEmpty(r),
);
}
if (isEmptyResult) {
tempData[k][REVIEWED_RESULT] = tempData[k][PREDICTED_RESULT];
}
2024-06-03 06:59:26 +00:00
}
2024-05-23 11:48:21 +00:00
});
setSelectedFileDataSource(tempData);
};
2024-05-02 12:14:47 +00:00
const setAndLoadSelectedFile = async (requestData, index) => {
setSelectedFileId(index);
if (!requestData['Files'][index]) {
setSelectedFileData('FAILED_TO_LOAD_FILE');
2024-05-09 09:12:14 +00:00
setImageLoading(false);
2024-05-02 12:14:47 +00:00
return;
}
const fileName = requestData['Files'][index]['File Name'];
const fileURL = requestData['Files'][index]['File URL'];
let fileDataSource = requestData['Files'][index];
updateSelectedFileDataSource(fileDataSource);
let reason = fileDataSource.Reason;
let solution = fileDataSource['Counter Measures'];
if (!reason) {
setReason(null);
2024-06-06 06:56:05 +00:00
setOtherReason(null);
} else if (REASON_BAD_QUALITY.some((r) => r.value === reason)) {
setReason(reason);
2024-06-06 06:56:05 +00:00
setOtherReason(null);
} else {
setReason('other');
setOtherReason(reason);
}
if (!solution) {
setSolution(null);
2024-06-06 06:56:05 +00:00
setOtherSolution(null);
} else if (SOLUTION_BAD_QUALITY.some((r) => r.value === solution)) {
setSolution(solution);
2024-06-06 06:56:05 +00:00
setOtherSolution(null);
} else {
setSolution('other');
setOtherSolution(solution);
}
2024-05-23 11:48:21 +00:00
setSelectedFileName(fileName);
2024-06-19 08:33:33 +00:00
try {
const response = await fetch(fileURL);
if (response.status === 200) {
setSelectedFileData(fileURL);
} else {
setSelectedFileData('FAILED_TO_LOAD_FILE');
}
} catch (error) {
2024-05-02 12:14:47 +00:00
setSelectedFileData('FAILED_TO_LOAD_FILE');
}
2024-06-19 08:33:33 +00:00
setImageLoading(false);
2024-05-02 12:14:47 +00:00
};
const loadCurrentRequest = (requestIndex) => {
2024-06-28 04:29:05 +00:00
setCurrentRequestIndex(requestIndex);
2024-05-02 12:14:47 +00:00
setLoading(true);
2024-05-09 09:12:14 +00:00
setImageLoading(true);
2024-05-02 12:14:47 +00:00
fetchAllRequests(
filterDateRange,
filterSubsidiaries,
filterReviewState,
filterIncludeTests,
2024-12-03 09:18:20 +00:00
filterDoctype,
filterFeedbackResult,
filterPredictResult,
filterReviewedResult,
2024-05-02 12:14:47 +00:00
requestIndex,
1,
filterAccuracy,
)
.then((data) => {
setTotalPages(data?.page?.total_requests);
setHasNextRequest(requestIndex < data?.page?.total_requests);
2024-07-04 09:17:32 +00:00
2024-06-28 04:29:05 +00:00
fetchRequest(data?.subscription_requests[0].RequestID)
2024-05-02 12:14:47 +00:00
.then(async (data) => {
if (data) setCurrentRequest(data);
setAndLoadSelectedFile(data, 0);
})
2024-06-28 04:29:05 +00:00
.catch((error) => {
notification.error({ message: getErrorMessage(error) });
})
2024-05-02 12:14:47 +00:00
.finally(() => {
setLoading(false);
});
})
2024-06-28 04:29:05 +00:00
.catch((error) => {
notification.error({ message: getErrorMessage(error) });
})
2024-05-02 12:14:47 +00:00
.finally(() => {
setLoading(false);
});
};
const gotoNextRequest = () => {
if (currentRequestIndex >= totalRequests) {
return;
}
const nextRequestIndex = currentRequestIndex + 1;
loadCurrentRequest(nextRequestIndex);
};
const gotoPreviousRequest = () => {
if (currentRequestIndex === 1) {
return;
}
const previousRequestIndex = currentRequestIndex - 1;
loadCurrentRequest(previousRequestIndex);
};
const reloadFilters = () => {
setCurrentRequestIndex(1);
2024-06-03 06:59:26 +00:00
if (searchParams.has('request_id')) {
searchParams.delete('request_id');
setSearchParams(searchParams);
}
2024-05-02 12:14:47 +00:00
fetchAllRequests(
filterDateRange,
filterSubsidiaries,
filterReviewState,
filterIncludeTests,
2024-12-03 09:18:20 +00:00
filterDoctype,
filterFeedbackResult,
filterPredictResult,
filterReviewedResult,
2024-05-02 12:14:47 +00:00
1,
1,
filterAccuracy,
).then((data) => {
setTotalPages(data?.page?.total_requests);
setHasNextRequest(1 < data?.page?.total_requests);
const firstRequest = fetchRequest(
data?.subscription_requests[0].RequestID,
);
firstRequest.then(async (data) => {
if (data) setCurrentRequest(data);
setAndLoadSelectedFile(data, 0);
});
});
};
2024-06-28 04:29:05 +00:00
const handleConfirmRequest = async () => {
2024-06-20 06:27:41 +00:00
try {
2024-06-28 04:29:05 +00:00
const res = await confirmRequest(currentRequest?.RequestID);
if (res.status == 200) {
addRecentRequest(currentRequest?.RequestID, res, true);
notification.success({ message: 'Update file success' });
setCurrentRequest({
...currentRequest,
['Is Reviewed']: true,
});
} else {
throw new Error(await res.text());
}
2024-06-20 06:27:41 +00:00
} catch (error) {
2024-06-28 04:29:05 +00:00
addRecentRequest(currentRequest?.RequestID, error, false);
2024-06-20 06:27:41 +00:00
notification.error({
message: getErrorMessage(error),
});
}
2024-05-02 12:14:47 +00:00
};
const submitRevisedData = async () => {
const fileId = selectedFileName.split('.')[0];
let request_file_result = {};
SOURCE_KEYS.forEach((k) => {
request_file_result[k] = selectedFileDataSource[k][REVIEWED_RESULT];
2024-06-19 08:33:33 +00:00
if (Array.isArray(selectedFileDataSource[k][REVIEWED_RESULT])) {
request_file_result[k] =
selectedFileDataSource[k][REVIEWED_RESULT].toString();
} else {
2024-06-06 06:56:05 +00:00
request_file_result[k] = selectedFileDataSource[k][REVIEWED_RESULT];
}
});
let data = {
request_file_result,
2024-06-06 06:56:05 +00:00
reason: reason !== 'other' ? reason : otherReason,
solution: solution !== 'other' ? solution : otherSolution,
};
try {
2024-06-28 04:29:05 +00:00
let res = await updateRevisedDataByFile(
currentRequest?.RequestID,
fileId,
data,
);
if (res.status != 200) {
throw new Error('Could not update revised data');
}
let newData = currentRequest;
newData.Files[selectedFileId]['Is Reviewed'] = true;
2024-05-02 12:14:47 +00:00
setCurrentRequest({
...newData,
2024-05-02 12:14:47 +00:00
});
notification.success({ message: 'Update file success' });
2024-05-31 09:55:02 +00:00
const requestData = await fetchRequest(currentRequest?.RequestID);
setCurrentRequest(requestData);
} catch (error) {
notification.error({
message: getErrorMessage(error),
});
}
2024-05-02 12:14:47 +00:00
};
// use left/right keys to navigate
useHotkeys('left', gotoPreviousRequest);
useHotkeys('right', gotoNextRequest);
2024-06-06 04:05:05 +00:00
useHotkeys('u', submitRevisedData);
2024-06-28 04:29:05 +00:00
useHotkeys('c', handleConfirmRequest);
2024-05-02 12:14:47 +00:00
const fileExtension = selectedFileName
? selectedFileName.split('.').pop()
: '';
const [lightBox, setLightBox] = useState(false);
2024-06-06 06:56:05 +00:00
const updateRevisedByFeedback = (fieldName) => {
2024-05-23 11:48:21 +00:00
setSelectedFileDataSource((prevData) => {
prevData[fieldName][REVIEWED_RESULT] =
2024-06-19 08:33:33 +00:00
prevData[fieldName][FEEDBACK_RESULT];
2024-05-23 11:48:21 +00:00
return {
...prevData,
};
});
};
const handleUpdateFileInField = (fieldName, fieldValue) => {
setSelectedFileDataSource((prevData) => {
prevData[fieldName][REVIEWED_RESULT] = fieldValue;
return {
...prevData,
};
});
};
2024-05-23 11:48:21 +00:00
2024-07-04 09:17:32 +00:00
const isSafeToConfirm = useMemo(() => {
let isSafe = true;
isSafe = !currentRequest?.Files.some((file) => {
const isRequired = file['Is Required'];
const isReviewed = file['Is Reviewed'];
return isRequired && !isReviewed;
});
return isSafe;
}, [currentRequest]);
2024-05-02 12:14:47 +00:00
return (
<div
style={
fullscreen
? {
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
backgroundColor: '#fff',
zIndex: 1000,
}
: {
height: '100%',
position: 'relative',
}
}
>
<div
style={{
height: '100%',
position: 'absolute',
top: 0,
left: 0,
width: '100%',
background: '#00000033',
zIndex: 1000,
display: loading ? 'block' : 'none',
}}
>
<Spin
spinning={true}
style={{
position: 'absolute',
top: '50%',
left: '50%',
marginTop: -12,
marginLeft: -12,
width: 24,
height: 24,
borderRadius: '50%',
}}
size='large'
/>
</div>
2024-05-02 12:14:47 +00:00
<div
style={{
overflow: 'auto',
width: '100%',
height: fullscreen ? 'calc(100% - 32px)' : '100%',
2024-05-02 12:14:47 +00:00
maxWidth: '100%',
display: 'flex',
}}
>
<div
style={{
height: '100%',
2024-05-23 11:48:21 +00:00
flexGrow: 1,
2024-05-02 12:14:47 +00:00
display: 'flex',
2024-05-23 11:48:21 +00:00
flexDirection: 'column',
position: 'relative',
2024-05-02 12:14:47 +00:00
}}
>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
margin: '0 0 4px 0',
}}
>
<Button
onClick={() => {
setFullscreen(!fullscreen);
}}
>
{fullscreen ? <FullscreenExitOutlined /> : <FullscreenOutlined />}
</Button>
{totalRequests && (
<div
style={{
flexGrow: 1,
display: 'grid',
gridTemplateColumns: '1fr 1fr 1fr',
marginLeft: '16px',
}}
>
<div style={{ gridColumn: 'span 2 / span 2' }}>
<b>Request ID: &nbsp;</b>
{currentRequest?.RequestID}
</div>{' '}
<div>
<b>Created at: &nbsp;</b>
{currentRequest?.created_at}
</div>{' '}
<div>
<b>Request time: &nbsp;</b>
{currentRequest?.['Client Request Time (ms)']}
</div>{' '}
<div style={{ gridColumn: 'span 2 / span 2' }}>
<b>Redemption ID: &nbsp;</b>
{currentRequest?.RedemptionID}
</div>{' '}
<div>
<b>Raw accuracy: &nbsp;</b>
{currentRequest?.raw_accuracy}
</div>{' '}
<div style={{ gridColumn: 'span 2 / span 2' }}>
<b>Processing time: &nbsp;</b>
{currentRequest?.['Server Processing Time (ms)']}
</div>{' '}
</div>
)}
</div>
2024-05-02 12:14:47 +00:00
{totalRequests > 0 && (
<div
style={{
display: 'flex',
2024-05-23 11:48:21 +00:00
flexDirection: 'row',
alignItems: 'center',
2024-05-02 12:14:47 +00:00
flexGrow: 0,
margin: '8px 0 0 0',
2024-05-02 12:14:47 +00:00
}}
>
2024-05-23 11:48:21 +00:00
<p
2024-05-02 12:14:47 +00:00
style={{
2024-05-23 11:48:21 +00:00
color: '#333',
fontWeight: 'bold',
margin: '0 16px 0 0',
2024-05-02 12:14:47 +00:00
}}
>
2024-05-23 11:48:21 +00:00
Files ({currentRequest?.Files?.length})
</p>
{currentRequest?.Files.map((file, index) => (
<FileCard
key={index}
file={file}
isSelected={index === selectedFileId}
onClick={() => {
setAndLoadSelectedFile(currentRequest, index);
setImageLoading(true);
2024-05-02 12:14:47 +00:00
}}
2024-05-23 11:48:21 +00:00
/>
))}
2024-05-02 12:14:47 +00:00
</div>
)}
2024-05-31 09:55:02 +00:00
{selectedFileData === 'FAILED_TO_LOAD_FILE' ? (
2024-06-19 08:33:33 +00:00
<div
style={{
flexGrow: 1,
overflow: 'auto',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<p style={{ color: '#333' }}>Failed to load file.</p>
</div>
2024-05-31 09:55:02 +00:00
) : fileExtension === 'pdf' ? (
<div
style={{
flexGrow: 1,
overflow: 'auto',
}}
>
<Viewer
plugins={[defaultLayoutPluginInstance]}
fileUrl={selectedFileData}
onDocumentLoad={() => setImageLoading(false)}
/>
</div>
) : (
<div
style={{
flexGrow: 1,
overflow: 'auto',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
overflowX: 'visible',
}}
>
2024-06-06 04:05:05 +00:00
<Spin spinning={imageLoading} wrapperClassName='image-spin'>
2024-06-03 06:59:26 +00:00
<img
style={{
2024-06-06 04:05:05 +00:00
height: '100%',
width: '100%',
objectFit: 'contain',
2024-06-03 06:59:26 +00:00
cursor: 'zoom-in',
}}
src={selectedFileData}
alt='file'
onClick={() => setLightBox(true)}
onLoad={() => {
setImageLoading(false);
}}
/>
2024-05-02 12:14:47 +00:00
2024-06-03 06:59:26 +00:00
{lightBox && (
<Lightbox
image={selectedFileData}
onClose={() => setLightBox(false)}
></Lightbox>
)}
</Spin>
2024-05-31 09:55:02 +00:00
</div>
)}
2024-05-02 12:14:47 +00:00
2024-05-03 02:52:15 +00:00
<div
style={{
flexGrow: 0,
display: 'flex',
justifyContent: 'space-between',
2024-05-09 09:12:14 +00:00
margin: '8px 0 0',
2024-05-03 02:52:15 +00:00
alignItems: 'center',
}}
>
2024-05-02 12:14:47 +00:00
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
2024-06-03 06:59:26 +00:00
<RecentRequest />
2024-05-03 02:52:15 +00:00
<h3 style={{ margin: '0 8px ' }}>
2024-05-02 12:14:47 +00:00
{totalRequests
2024-05-03 02:52:15 +00:00
? 'No: ' + currentRequestIndex + '/' + totalRequests
2024-05-02 12:14:47 +00:00
: 'No Request. Adjust your search criteria to see more results.'}
2024-05-03 02:52:15 +00:00
</h3>
2024-05-02 12:14:47 +00:00
{currentRequest &&
(currentRequest['Is Reviewed'] ? (
<Tag
icon={<CheckCircleOutlined />}
color='success'
style={{ padding: '4px 16px' }}
>
Reviewed
</Tag>
) : (
<Tag
icon={<ClockCircleFilled />}
color='warning'
style={{ padding: '4px 16px' }}
>
Not Reviewed
</Tag>
))}
</div>
<div
style={{
display: 'flex',
justifyContent: 'center',
}}
>
<div>
<Button
type='default'
style={{ height: 38 }}
disabled={currentRequestIndex === 1}
onClick={() => {
gotoPreviousRequest();
}}
>
<ArrowLeftOutlined />
2024-05-03 02:52:15 +00:00
Prev
2024-05-02 12:14:47 +00:00
</Button>
<Button
type='default'
style={{ height: 38 }}
disabled={!hasNextRequest}
onClick={() => {
if (!hasNextRequest) {
return;
}
gotoNextRequest();
}}
>
Next
<ArrowRightOutlined />
</Button>
<Input
size='middle'
addonBefore='To'
style={{ marginBottom: '4px', marginLeft: '4px', width: 180 }}
defaultValue={currentRequestIndex}
addonAfter={
<Button
type='default'
onClick={() => {
if (pageIndexToGoto > totalRequests) {
message.error('RequestID is out of range.');
return;
}
if (pageIndexToGoto < 1) {
message.error('RequestID is out of range.');
return;
}
2024-06-28 04:29:05 +00:00
2024-05-02 12:14:47 +00:00
loadCurrentRequest(pageIndexToGoto);
}}
>
Go to
</Button>
}
value={pageIndexToGoto}
onChange={(e) => {
setPageIndexToGoto(parseInt(e.target.value));
}}
/>
</div>
</div>
2024-05-23 11:48:21 +00:00
<Button
type='primary'
shape='default'
2024-05-23 11:48:21 +00:00
size='middle'
2024-05-31 09:55:02 +00:00
disabled={currentRequest && currentRequest['Is Reviewed']}
2024-06-03 06:59:26 +00:00
style={{ minWidth: '120px' }}
2024-06-20 06:27:41 +00:00
onClick={() => setIsOpenConfirmRequest(true)}
2024-05-23 11:48:21 +00:00
>
2024-06-19 08:33:33 +00:00
Confirm request
2024-05-23 11:48:21 +00:00
</Button>
2024-05-02 12:14:47 +00:00
</div>
</div>
2024-05-09 09:12:14 +00:00
<div
style={{
flexBasis: 400,
flexShrink: 0,
display: 'flex',
flexDirection: 'column',
paddingLeft: '8px',
2024-05-09 09:12:14 +00:00
}}
>
2024-05-23 11:48:21 +00:00
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginBottom: '8px',
2024-05-23 11:48:21 +00:00
}}
>
<Input
size='middle'
value={`Sub: ${filterSubsidiaries}, Date:${
filterDateRange[0]
? filterDateRange[0] + ' to ' + filterDateRange[1]
: 'All'
}, Reviewed: ${filterReviewState}, Tests: ${filterIncludeTests}`}
readOnly
/>
<Button
type='primary'
size='middle'
style={{
height: '36px',
}}
2024-05-23 11:48:21 +00:00
onClick={() => {
setIsModalOpen(true);
}}
>
Filters
</Button>
</div>
<div style={{ flexGrow: 1, overflowY: 'auto', overflowX: 'hidden' }}>
2024-05-23 11:48:21 +00:00
{SOURCE_KEYS?.map((data) => {
let shouldRevised = false;
try {
if (
selectedFileDataSource[data]?.[FEEDBACK_ACCURACY].length > 0
) {
shouldRevised =
selectedFileDataSource[data][FEEDBACK_ACCURACY][0] < 1;
}
} catch (error) {}
2024-05-02 12:14:47 +00:00
return (
2024-06-06 04:05:05 +00:00
<div style={{ margin: '0 0 8px' }} className='file-input-group'>
2024-05-09 09:12:14 +00:00
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
margin: '0 0 4px',
}}
>
2024-05-23 11:48:21 +00:00
<p style={{ fontWeight: 'bold', margin: 0 }}>{data}</p>
2024-05-03 02:52:15 +00:00
<Button
shape='round'
2024-05-09 09:12:14 +00:00
type='primary'
ghost
2024-05-03 02:52:15 +00:00
icon={<CopyOutlined />}
size='small'
2024-06-06 06:56:05 +00:00
onClick={() => updateRevisedByFeedback(data)}
2024-05-03 02:52:15 +00:00
/>
</div>
2024-05-02 12:14:47 +00:00
<Input
addonBefore='Feedback'
size='small'
readOnly
2024-05-23 11:48:21 +00:00
value={selectedFileDataSource[data]?.[FEEDBACK_RESULT]}
2024-05-02 12:14:47 +00:00
/>
<Input
addonBefore='Predicted'
readOnly
size='small'
2024-05-23 11:48:21 +00:00
value={selectedFileDataSource[data]?.[PREDICTED_RESULT]}
2024-05-02 12:14:47 +00:00
/>
<Input
addonBefore='Revised'
2024-05-23 11:48:21 +00:00
style={{ background: shouldRevised ? 'yellow' : '' }}
2024-05-02 12:14:47 +00:00
size='small'
2024-05-23 11:48:21 +00:00
value={selectedFileDataSource[data]?.[REVIEWED_RESULT]}
2024-05-31 09:55:02 +00:00
onChange={(e) =>
handleUpdateFileInField(data, e.target.value)
}
2024-05-02 12:14:47 +00:00
/>
</div>
);
})}
2024-05-23 11:48:21 +00:00
<b>{t`Bad image reason:`}</b>
<div
style={{
display: 'flex',
margin: '8px 0px',
justifyContent: 'space-between',
}}
>
<Select
placeholder='Select a reason'
style={{ width: 170, flexBasis: '50%', height: '32px' }}
2024-05-23 11:48:21 +00:00
options={REASON_BAD_QUALITY}
2024-06-06 06:56:05 +00:00
onChange={(value) => {
let newReason = value;
setReason(newReason);
setSolution(counter_measure_map[newReason]);
}}
2024-05-23 11:48:21 +00:00
value={reason}
/>
{reason === 'other' && (
<Input
placeholder='Other reason'
value={otherReason}
style={{ width: 170, flexBasis: '50%', height: '32px' }}
2024-05-23 11:48:21 +00:00
onChange={(e) => {
setOtherReason(e.target.value);
}}
/>
)}
</div>
<b>{t`Solution:`}</b>
<div
style={{
display: 'flex',
margin: '8px 0px',
justifyContent: 'space-between',
}}
>
<Select
placeholder='Select a solution'
style={{ width: 170, flexBasis: '50%', height: '32px' }}
2024-05-23 11:48:21 +00:00
options={SOLUTION_BAD_QUALITY}
onChange={setSolution}
value={solution}
// defaultValue={solution}
/>
{solution === 'other' && (
<Input
style={{ width: 170, flexBasis: '50%', height: '32px' }}
2024-05-23 11:48:21 +00:00
placeholder='Other solution'
value={otherSolution}
onChange={(e) => {
setOtherSolution(e.target.value);
}}
/>
)}
</div>
2024-05-02 12:14:47 +00:00
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button
type='primary'
color='success'
size='middle'
style={{
height: '36px',
}}
onClick={submitRevisedData}
>
2024-06-06 06:56:05 +00:00
Update File
</Button>
</div>
2024-05-02 12:14:47 +00:00
</div>
</div>
<Modal
title={t`Report Filters`}
open={isModalOpen}
width={700}
onOk={() => {
setIsModalOpen(false);
reloadFilters();
}}
onCancel={() => {
setIsModalOpen(false);
}}
>
<Form
style={{
marginTop: 30,
}}
>
<Form.Item
name='dateRange'
label={t`Date (GMT+8)`}
rules={[
{
required: true,
message: 'Please select a date range',
},
]}
style={{
marginBottom: 24,
}}
>
<DatePicker.RangePicker
onChange={(date, dateString) => {
setFilterDateRange(dateString);
}}
style={{ width: 200 }}
/>
</Form.Item>
<div
style={{
marginTop: 10,
display: 'flex',
justifyContent: 'space-between',
marginLeft: 0,
padding: 0,
}}
>
<Form.Item
name='subsidiary'
label={t`Subsidiary`}
rules={[
{
required: true,
message: 'Please select a subsidiary',
},
]}
>
<Select
placeholder='Select a subsidiary'
style={{ width: 200 }}
2024-05-03 02:52:15 +00:00
options={SUBSIDIARIES}
2024-05-02 12:14:47 +00:00
value={filterSubsidiaries}
defaultValue={filterSubsidiaries}
onChange={setFilterSubsidiaries}
/>
</Form.Item>
<Form.Item
name='max_accuracy'
label={t`max_accuracy` + ' (1-100)'}
rules={[
{
required: true,
message: 'Please select max accuracy',
},
]}
>
<InputNumber
min={1}
max={100}
defaultValue={filterAccuracy}
onChange={(value) => setFilterAccuracy(value)}
/>
</Form.Item>
</div>
<div
style={{
marginTop: 10,
display: 'flex',
justifyContent: 'space-between',
marginLeft: 0,
padding: 0,
}}
>
<Form.Item
name='reviewed'
label={t`Reviewed`}
rules={[
{
required: true,
message: 'Please select review status',
},
]}
>
<Select
style={{ width: 200 }}
options={[
{ label: 'All', value: 'all' },
{ label: 'Reviewed', value: 'reviewed' },
{ label: 'Not Reviewed', value: 'not_reviewed' },
]}
value={filterReviewState}
defaultValue={filterReviewState}
onChange={setFilterReviewState}
/>
</Form.Item>
<Form.Item
name='is_test'
label={t`Is Test`}
rules={[
{
required: true,
message: 'Please select test status',
},
]}
style={{ marginLeft: 16 }}
>
<Select
style={{ width: 200 }}
options={[
{ label: 'Include tests', value: 'true' },
{ label: 'Exclude tests', value: 'false' },
]}
value={filterIncludeTests}
defaultValue={filterIncludeTests}
onChange={setFilterIncludesTests}
/>
</Form.Item>
</div>
2024-12-03 09:18:20 +00:00
{/* add 4 more filter fields */}
<div
style={{
marginTop: 10,
display: 'flex',
justifyContent: 'space-between',
marginLeft: 0,
padding: 0,
}}
>
<Form.Item
name='doc_type'
label={t`Only type`}
rules={[
{
required: true,
message: 'Please select a document type',
},
]}
>
<Select
placeholder='Select a document type'
style={{ width: 200 }}
options={DOCTYPE}
value={filterDoctype}
defaultValue={filterDoctype}
onChange={setFilterDoctype}
/>
</Form.Item>
<Form.Item
name='feedback_result'
label={t`Feedback includes`}
>
<Input
defaultValue={filterFeedbackResult}
onChange={(e) => setFilterFeedbackResult(e.target.value)}
/>
</Form.Item>
</div>
<div
style={{
marginTop: 10,
display: 'flex',
justifyContent: 'space-between',
marginLeft: 0,
padding: 0,
}}
>
<Form.Item
name='predict_result'
label={t`Predict includes`}
>
<Input
defaultValue={filterPredictResult}
onChange={(e) => setFilterPredictResult(e.target.value)}
/>
</Form.Item>
<Form.Item
name='reviewed_result'
label={t`Review inculdes`}
>
<Input
defaultValue={filterReviewedResult}
onChange={(e) => setFilterReviewedResult(e.target.value)}
/>
</Form.Item>
</div>
2024-05-02 12:14:47 +00:00
</Form>
</Modal>
2024-06-20 06:27:41 +00:00
<Modal
title='Confirm this request?'
centered
open={isOpenConfirmRequest}
onOk={() => {
setIsOpenConfirmRequest(false);
2024-06-28 04:29:05 +00:00
handleConfirmRequest();
2024-06-20 06:27:41 +00:00
}}
onCancel={() => {
setIsOpenConfirmRequest(false);
}}
>
2024-07-04 09:17:32 +00:00
{!isSafeToConfirm && (
<p style={{ fontWeight: 'bold', color: 'red' }}>
There is still at least 1 photo need to be reviewed!{' '}
</p>
)}
2024-06-20 06:27:41 +00:00
<p>Are you sure you want to confirm this request is reviewed?</p>
</Modal>
2024-05-02 12:14:47 +00:00
</div>
);
};
export default ReviewPage;