import { ArrowLeftOutlined, ArrowRightOutlined, CheckCircleOutlined, ClockCircleFilled, CopyOutlined, FullscreenExitOutlined, FullscreenOutlined, } from '@ant-design/icons'; import { t } from '@lingui/macro'; import { Viewer } from '@react-pdf-viewer/core'; 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'; import { Button, DatePicker, Form, Input, InputNumber, message, Modal, notification, Select, Spin, Tag, } from 'antd'; import { isEmpty } from 'lodash-es'; import { useEffect, useMemo, useState } from 'react'; import Lightbox from 'react-awesome-lightbox'; import 'react-awesome-lightbox/build/style.css'; import { useHotkeys } from 'react-hotkeys-hook'; import { useSearchParams } from 'react-router-dom'; import { getErrorMessage } from 'utils/error-handler'; import { addRecentRequest, confirmRequest, fetchAllRequests, fetchRequest, updateRevisedDataByFile, } from './api'; import { counter_measure_map, FEEDBACK_ACCURACY, FEEDBACK_RESULT, PREDICTED_RESULT, REASON_BAD_QUALITY, REVIEWED_RESULT, SOLUTION_BAD_QUALITY, SOURCE_KEYS, SOURCE_OBJECT_NAMES, SUBSIDIARIES, } from './const'; import FileCard from './FileCard'; import RecentRequest from './RecentRequest'; import './style.css'; 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); const [selectedFileDataSource, setSelectedFileDataSource] = useState({}); const [selectedFileName, setSelectedFileName] = useState(null); const [isOpenConfirmRequest, setIsOpenConfirmRequest] = useState(false); // 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'); // 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(''); const [imageLoading, setImageLoading] = useState(false); const defaultLayoutPluginInstance = defaultLayoutPlugin(); 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, 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); }); }); } }, []); // useEffect(() => { // if (reason) { // setSolution(counter_measure_map[reason]); // } // }, [reason]); const updateSelectedFileDataSource = (fileContent) => { let tempData = {}; SOURCE_KEYS.forEach((k) => { tempData[k] = {}; SOURCE_OBJECT_NAMES.forEach((name) => { tempData[k][name] = fileContent[name][k]; }); if (!isEmpty(tempData[k][PREDICTED_RESULT])) { let isEmptyResult = false; if (isEmpty(tempData[k][REVIEWED_RESULT])) { isEmptyResult = true; } if ( Array.isArray(tempData[k][REVIEWED_RESULT]) && tempData[k][REVIEWED_RESULT].length > 0 ) { isEmptyResult = tempData[k][REVIEWED_RESULT].every((r: any) => isEmpty(r), ); } if (isEmptyResult) { tempData[k][REVIEWED_RESULT] = tempData[k][PREDICTED_RESULT]; } } }); setSelectedFileDataSource(tempData); }; const setAndLoadSelectedFile = async (requestData, index) => { setSelectedFileId(index); if (!requestData['Files'][index]) { setSelectedFileData('FAILED_TO_LOAD_FILE'); setImageLoading(false); 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); setOtherReason(null); } else if (REASON_BAD_QUALITY.some((r) => r.value === reason)) { setReason(reason); setOtherReason(null); } else { setReason('other'); setOtherReason(reason); } if (!solution) { setSolution(null); setOtherSolution(null); } else if (SOLUTION_BAD_QUALITY.some((r) => r.value === solution)) { setSolution(solution); setOtherSolution(null); } else { setSolution('other'); setOtherSolution(solution); } setSelectedFileName(fileName); try { const response = await fetch(fileURL); if (response.status === 200) { setSelectedFileData(fileURL); } else { setSelectedFileData('FAILED_TO_LOAD_FILE'); } } catch (error) { setSelectedFileData('FAILED_TO_LOAD_FILE'); } setImageLoading(false); }; const loadCurrentRequest = (requestIndex) => { setCurrentRequestIndex(requestIndex); setLoading(true); setImageLoading(true); fetchAllRequests( filterDateRange, filterSubsidiaries, filterReviewState, filterIncludeTests, requestIndex, 1, filterAccuracy, ) .then((data) => { setTotalPages(data?.page?.total_requests); setHasNextRequest(requestIndex < data?.page?.total_requests); fetchRequest(data?.subscription_requests[0].RequestID) .then(async (data) => { if (data) setCurrentRequest(data); setAndLoadSelectedFile(data, 0); }) .catch((error) => { notification.error({ message: getErrorMessage(error) }); }) .finally(() => { setLoading(false); }); }) .catch((error) => { notification.error({ message: getErrorMessage(error) }); }) .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); if (searchParams.has('request_id')) { searchParams.delete('request_id'); setSearchParams(searchParams); } fetchAllRequests( filterDateRange, filterSubsidiaries, filterReviewState, filterIncludeTests, 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); }); }); }; const handleConfirmRequest = async () => { try { 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()); } } catch (error) { addRecentRequest(currentRequest?.RequestID, error, false); notification.error({ message: getErrorMessage(error), }); } }; const submitRevisedData = async () => { const fileId = selectedFileName.split('.')[0]; let request_file_result = {}; SOURCE_KEYS.forEach((k) => { request_file_result[k] = selectedFileDataSource[k][REVIEWED_RESULT]; if (Array.isArray(selectedFileDataSource[k][REVIEWED_RESULT])) { request_file_result[k] = selectedFileDataSource[k][REVIEWED_RESULT].toString(); } else { request_file_result[k] = selectedFileDataSource[k][REVIEWED_RESULT]; } }); let data = { request_file_result, reason: reason !== 'other' ? reason : otherReason, solution: solution !== 'other' ? solution : otherSolution, }; try { 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; setCurrentRequest({ ...newData, }); notification.success({ message: 'Update file success' }); const requestData = await fetchRequest(currentRequest?.RequestID); setCurrentRequest(requestData); } catch (error) { notification.error({ message: getErrorMessage(error), }); } }; // use left/right keys to navigate useHotkeys('left', gotoPreviousRequest); useHotkeys('right', gotoNextRequest); useHotkeys('u', submitRevisedData); useHotkeys('c', handleConfirmRequest); const fileExtension = selectedFileName ? selectedFileName.split('.').pop() : ''; const [lightBox, setLightBox] = useState(false); const updateRevisedByFeedback = (fieldName) => { setSelectedFileDataSource((prevData) => { prevData[fieldName][REVIEWED_RESULT] = prevData[fieldName][FEEDBACK_RESULT]; return { ...prevData, }; }); }; const handleUpdateFileInField = (fieldName, fieldValue) => { setSelectedFileDataSource((prevData) => { prevData[fieldName][REVIEWED_RESULT] = fieldValue; return { ...prevData, }; }); }; 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]); return (
Files ({currentRequest?.Files?.length})
{currentRequest?.Files.map((file, index) => (Failed to load file.
{data}
} size='small' onClick={() => updateRevisedByFeedback(data)} />There is still at least 1 photo need to be reviewed!{' '}
)}Are you sure you want to confirm this request is reviewed?