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

1395 lines
41 KiB
TypeScript
Raw Normal View History

2024-05-02 12:14:47 +00:00
import {
ArrowLeftOutlined,
ArrowRightOutlined,
CheckCircleOutlined,
ClockCircleFilled,
FullscreenExitOutlined,
FullscreenOutlined,
} from '@ant-design/icons';
import { t } from '@lingui/macro';
import { Viewer } from '@react-pdf-viewer/core';
import type { GetRef } from 'antd';
import {
Button,
DatePicker,
Form,
Input,
InputNumber,
message,
Modal,
notification,
Select,
Spin,
Table,
Tag,
} from 'antd';
import React, { useContext, useEffect, useRef, useState } from 'react';
import Lightbox from 'react-awesome-lightbox';
import 'react-awesome-lightbox/build/style.css';
import { useHotkeys } from 'react-hotkeys-hook';
import { baseURL } from 'request/api';
import styled from 'styled-components';
// Import the styles
import '@react-pdf-viewer/core/lib/styles/index.css';
import { badQualityReasonSubmit } from 'request';
import { normalizeData } from 'utils/field-value-process';
import { fetchAllRequests, fetchRequest } from './api';
import { counter_measure_map } from './consts';
import FileCard from './FileCard';
const siderStyle: React.CSSProperties = {
backgroundColor: '#fafafa',
padding: 10,
flexBasis: 400,
flexShrink: 0,
};
const StyledTable = styled(Table)`
& .sbt-table-cell {
padding: 4px !important;
}
`;
type InputRef = GetRef<typeof Input>;
type FormInstance<T> = GetRef<typeof Form<T>>;
const EditableContext = React.createContext<FormInstance<any> | null>(null);
interface Item {
key: string;
accuracy: number;
submitted: string;
revised: string;
action: string;
}
interface EditableRowProps {
index: number;
}
const EditableRow: React.FC<EditableRowProps> = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
interface EditableCellProps {
title: React.ReactNode;
editable: boolean;
children: React.ReactNode;
dataIndex: keyof Item;
record: Item;
handleSave: (record: Item) => void;
}
const EditableCell: React.FC<EditableCellProps> = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
...restProps
}) => {
const [editing, setEditing] = useState(false);
const inputRef = useRef<InputRef>(null);
const form = useContext(EditableContext)!;
useEffect(() => {
if (editing) {
inputRef.current!.focus();
}
}, [editing]);
const toggleEdit = () => {
setEditing(!editing);
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
};
const save = async () => {
try {
const values = await form.validateFields();
toggleEdit();
handleSave({ ...record, ...values });
} catch (errInfo) {
console.log('Save failed:', errInfo);
}
};
let childNode = children;
if (editable) {
childNode = editing ? (
<Form.Item style={{ margin: 0 }} name={dataIndex}>
<Input ref={inputRef} onPressEnter={save} onBlur={save} />
</Form.Item>
) : (
<div
className='editable-cell-value-wrap'
style={{ paddingRight: 24 }}
onClick={toggleEdit}
>
{children}
</div>
);
}
return <td {...restProps}>{childNode}</td>;
};
// type EditableTableProps = Parameters<typeof Table>[0];
const ReviewPage = () => {
const [loading, setLoading] = useState(false);
const [fullscreen, setFullscreen] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const [isReasonModalOpen, setIsReasonModalOpen] = useState(false);
const [selectedFileId, setSelectedFileId] = useState(0);
const [selectedFileData, setSelectedFileData] = useState(null);
const [selectedFileName, setSelectedFileName] = useState(null);
// 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 [dataSource, setDataSource] = useState([]);
const [pageIndexToGoto, setPageIndexToGoto] = useState(1);
const [reason, setReason] = useState('');
const [otherReason, setOtherReason] = useState('');
const [solution, setSolution] = useState('');
const [otherSolution, setOtherSolution] = useState('');
useEffect(() => {
if (reason) {
setSolution(counter_measure_map[reason]);
}
}, [reason]);
const setAndLoadSelectedFile = async (requestData, index) => {
setSelectedFileId(index);
if (!requestData['Files'][index]) {
setSelectedFileData('FAILED_TO_LOAD_FILE');
return;
}
const fileName = requestData['Files'][index]['File Name'];
const fileURL = requestData['Files'][index]['File URL'];
const response = await fetch(fileURL);
if (response.status === 200) {
setSelectedFileName(fileName);
setSelectedFileData(fileURL);
console.log('Loading file: ' + fileName);
console.log('URL: ' + fileURL);
} else {
setSelectedFileData('FAILED_TO_LOAD_FILE');
}
};
console.log(dataSource);
const loadCurrentRequest = (requestIndex) => {
setLoading(true);
fetchAllRequests(
filterDateRange,
filterSubsidiaries,
filterReviewState,
filterIncludeTests,
requestIndex,
1,
filterAccuracy,
)
.then((data) => {
// setRequests(data?.subscription_requests);
// setHasNextRequest(data?.subscription_requests.length > 1);
setTotalPages(data?.page?.total_requests);
setHasNextRequest(requestIndex < data?.page?.total_requests);
const requestData = fetchRequest(
data?.subscription_requests[0].RequestID,
);
requestData
.then(async (data) => {
console.log('🚀 ~ .then ~ data:', data);
if (data) setCurrentRequest(data);
const predicted =
data && data['Predicted Result'] ? data['Predicted Result'] : {};
const submitted =
data && data['Feedback Result'] ? data['Feedback Result'] : {};
const revised =
data && data['Reviewed Result'] ? data['Reviewed Result'] : {};
const keys = Object.keys(predicted);
const tableRows = [];
for (let i = 0; i < keys.length; i++) {
let instance = {};
instance['key'] = keys[i];
instance['predicted'] = predicted[keys[i]];
instance['submitted'] = submitted[keys[i]];
instance['revised'] = revised[keys[i]];
tableRows.push(instance);
}
setDataSource(tableRows);
setLoading(false);
setAndLoadSelectedFile(data, 0);
})
.finally(() => {
setLoading(false);
});
})
.finally(() => {
setLoading(false);
});
};
const gotoNextRequest = () => {
if (currentRequestIndex >= totalRequests) {
return;
}
const nextRequestIndex = currentRequestIndex + 1;
setCurrentRequestIndex(nextRequestIndex);
loadCurrentRequest(nextRequestIndex);
};
const gotoPreviousRequest = () => {
if (currentRequestIndex === 1) {
return;
}
const previousRequestIndex = currentRequestIndex - 1;
setCurrentRequestIndex(previousRequestIndex);
loadCurrentRequest(previousRequestIndex);
};
const reloadFilters = () => {
setCurrentRequestIndex(1);
fetchAllRequests(
filterDateRange,
filterSubsidiaries,
filterReviewState,
filterIncludeTests,
1,
1,
filterAccuracy,
).then((data) => {
setTotalPages(data?.page?.total_requests);
// setRequests(data?.subscription_requests);
// setHasNextRequest(data?.subscription_requests.length > 1);
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);
setTimeout(() => {
loadCurrentRequest(1);
}, 500);
});
});
};
useEffect(() => {
setCurrentRequestIndex(1);
fetchAllRequests(
filterDateRange,
filterSubsidiaries,
filterReviewState,
filterIncludeTests,
1,
1,
filterAccuracy,
).then((data) => {
setTotalPages(data?.page?.total_requests);
// setRequests(data?.subscription_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 components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
// "Key", "Accuracy", "Submitted", "Revised"
interface DataType {
key: string;
accuracy: number;
submitted: string;
revised: string;
}
const updateRevisedData = async (newRevisedData: any) => {
const requestID = currentRequest.RequestID;
const token = localStorage.getItem('sbt-token') || '';
const result = await fetch(`${baseURL}/ctel/request/${requestID}/`, {
method: 'POST',
headers: {
Authorization: `${JSON.parse(token)}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
reviewed_result: newRevisedData,
}),
}).catch((error) => {
console.log(error);
throw error;
});
if (result.status != 200) {
throw new Error('Could not update revised data');
}
};
const handleSave = (row: DataType) => {
const newData = [...dataSource];
const index = newData.findIndex((item) => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
const newRevisedData = {};
for (let i = 0; i < newData.length; i++) {
newData[i].revised = normalizeData(newData[i].key, newData[i].revised);
newRevisedData[newData[i].key] = newData[i].revised;
}
updateRevisedData(newRevisedData)
.then(() => {
// "[Is Reviewed]" => true
setCurrentRequest({
...currentRequest,
['Is Reviewed']: true,
});
})
.then(() => {
setDataSource(newData);
})
.catch((error) => {
message.error(
'Could not update revised data. Please check the format.',
);
});
};
const submitRevisedData = async () => {
const newData = [...dataSource];
const newRevisedData = {};
for (let i = 0; i < newData.length; i++) {
newData[i].revised = normalizeData(newData[i].key, newData[i].revised);
newRevisedData[newData[i].key] = newData[i].revised;
}
updateRevisedData(newRevisedData).then(() => {
// "[Is Reviewed]" => true
setCurrentRequest({
...currentRequest,
['Is Reviewed']: true,
});
});
};
const defaultColumns = [
{
title: 'Key',
dataIndex: 'key',
key: 'key',
width: 200,
},
{
title: 'Predicted',
dataIndex: 'predicted',
key: 'predicted',
render: (text) => {
if (!text) return <span style={{ color: '#888' }}>{'<empty>'}</span>;
const displayedContent = text;
if (typeof displayedContent === 'string') {
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
} else if (typeof displayedContent === 'object') {
if (displayedContent.length === 0) {
return <span style={{ color: '#888' }}>{'<empty>'}</span>;
}
// Set all empty values to "<empty>"
for (const key in displayedContent) {
if (!displayedContent[key]) {
displayedContent[key] = '<empty>';
}
}
return (
<span style={{ color: '#000000' }}>
{displayedContent.join(', ')}
</span>
);
}
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
},
},
{
title: 'Submitted',
dataIndex: 'submitted',
key: 'submitted',
render: (text) => {
if (!text) return <span style={{ color: '#888' }}>{'<empty>'}</span>;
const displayedContent = text;
if (typeof displayedContent === 'string') {
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
} else if (typeof displayedContent === 'object') {
if (displayedContent.length === 0) {
return <span style={{ color: '#888' }}>{'<empty>'}</span>;
}
// Set all empty values to "<empty>"
for (const key in displayedContent) {
if (!displayedContent[key]) {
displayedContent[key] = '<empty>';
}
}
return (
<span style={{ color: '#000000' }}>
{displayedContent.join(', ')}
</span>
);
}
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
},
},
{
title: (
<div
style={{
width: 120,
display: 'flex',
lineHeight: '32px',
marginLeft: 10,
}}
>
Revised&nbsp;&nbsp;
<Button
onClick={() => {
if (!dataSource || !dataSource.length) return;
setDataSource(
dataSource.map((item) => {
item.revised = item.predicted;
return item;
}),
);
setTimeout(() => {
submitRevisedData();
}, 1000);
}}
>
Copy Predicted
</Button>
<Button
onClick={() => {
if (!dataSource || !dataSource.length) return;
setDataSource(
dataSource.map((item) => {
item.revised = item.submitted;
return item;
}),
);
setTimeout(() => {
submitRevisedData();
}, 1000);
}}
>
Copy Submitted
</Button>
</div>
),
dataIndex: 'revised',
key: 'revised',
editable: true,
render: (text) => {
if (!text) return <span style={{ color: '#888' }}>{'<empty>'}</span>;
const displayedContent = text;
if (typeof displayedContent === 'string') {
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
} else if (typeof displayedContent === 'object') {
if (displayedContent.length === 0) {
return <span style={{ color: '#888' }}>{'<empty>'}</span>;
}
// Set all empty values to "<empty>"
for (const key in displayedContent) {
if (!displayedContent[key]) {
displayedContent[key] = '<empty>';
}
}
return (
<span style={{ color: '#000000' }}>
{displayedContent.join(', ')}
</span>
);
}
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
},
},
];
const columns = defaultColumns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: DataType) => ({
record,
editable: col.key != 'request_id' && col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave,
}),
};
});
// use left/right keys to navigate
useHotkeys('left', gotoPreviousRequest);
useHotkeys('right', gotoNextRequest);
const fileExtension = selectedFileName
? selectedFileName.split('.').pop()
: '';
const [lightBox, setLightBox] = useState(false);
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>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
margin: '0 0 4px 0',
}}
>
<div style={{ flexGrow: 1 }}>
<Button
onClick={() => {
setFullscreen(!fullscreen);
}}
>
{fullscreen ? <FullscreenExitOutlined /> : <FullscreenOutlined />}
{fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'}
</Button>
{totalRequests ? (
<>
&nbsp;&nbsp;&nbsp;<b>Request ID:</b> {currentRequest?.RequestID}
</>
) : (
''
)}
</div>
<div style={{ display: 'flex', flexBasis: '400px' }}>
<Input
value={`Sub: ${filterSubsidiaries}, Date:${
filterDateRange[0]
? filterDateRange[0] + ' to ' + filterDateRange[1]
: 'All'
}, Reviewed: ${filterReviewState}, Tests: ${filterIncludeTests}`}
readOnly
/>
<Button
type='primary'
size='large'
onClick={() => {
setIsModalOpen(true);
}}
>
Filters
</Button>
</div>
</div>
<div
style={{
overflow: 'auto',
width: '100%',
height: fullscreen ? 'calc(100% - 32px)' : 'calc(100% - 16px)',
maxWidth: '100%',
display: 'flex',
padding: '8px',
background: '#efefef',
}}
>
<div
style={{
textAlign: 'center',
color: '#fff',
backgroundColor: '#efefef',
height: '100%',
display: 'flex',
flexBasis: '200px',
flexShrink: 0,
flexDirection: 'row',
}}
>
{totalRequests > 0 && (
<div
style={{
width: '200px',
display: 'flex',
flexDirection: 'column',
alignItems: 'start',
flexGrow: 0,
padding: '0 4px 0 0',
}}
>
<div
style={{
flexGrow: 1,
overflowY: 'auto',
width: '100%',
}}
>
<p
style={{
color: '#333',
fontWeight: 'bold',
}}
>
Files ({currentRequest?.Files?.length})
</p>
{currentRequest?.Files.map((file, index) => (
<FileCard
key={index}
file={file}
isSelected={index === selectedFileId}
onClick={() => {
setAndLoadSelectedFile(currentRequest, index);
}}
setIsReasonModalOpen={setIsReasonModalOpen}
/>
))}
</div>
{totalRequests > 0 && (
<div
style={{
color: 'black',
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'start',
padding: '0 4px 0 0',
}}
>
<b>Request ID</b>
<Input
size='small'
style={{ margin: '4px 0px 6px' }}
readOnly
value={currentRequest ? currentRequest.RequestID : ''}
/>
<b>Redemption</b>
<Input
size='small'
style={{ margin: '4px 0px 6px' }}
readOnly
value={
currentRequest?.RedemptionID
? currentRequest.RedemptionID
: ''
}
/>
<b>Uploaded date</b>
<Input
size='small'
style={{ margin: '4px 0px 6px' }}
readOnly
value={currentRequest ? currentRequest.created_at : ''}
/>
<b>Request time</b>
<Input
size='small'
style={{ margin: '4px 0px 6px' }}
readOnly
value={
currentRequest
? currentRequest['Client Request Time (ms)']
: ''
}
/>
<b>Processing time</b>
<Input
size='small'
style={{ margin: '4px 0px 6px' }}
readOnly
value={
currentRequest
? currentRequest['Server Processing Time (ms)']
: ''
}
/>
<b>Raw accuracy</b>
<Input
size='small'
style={{ margin: '4px 0px 6px' }}
readOnly
value={currentRequest ? currentRequest['raw_accuracy'] : ''}
/>
</div>
)}
</div>
)}
</div>
<div
style={{
border: '1px solid #ccc',
height: '100%',
// width: '300px',
flexGrow: 1,
display: 'flex',
flexDirection: 'column',
position: 'relative',
// overflow: 'auto'
}}
>
<div
style={{
flexGrow: 1,
textAlign: 'center',
}}
>
{selectedFileData === 'FAILED_TO_LOAD_FILE' ? (
<p style={{ color: '#333' }}>Failed to load file.</p>
) : fileExtension === 'pdf' ? (
<Viewer fileUrl={selectedFileData} />
) : (
<>
<img
style={{
maxHeight: '100%',
maxWidth: '100%',
height: 'auto',
width: 'auto',
cursor: 'zoom-in',
}}
src={selectedFileData}
alt='file'
onClick={() => setLightBox(true)}
/>
{lightBox && (
<Lightbox
image={selectedFileData}
onClose={() => setLightBox(false)}
></Lightbox>
)}
</>
)}
</div>
<div style={{ flexGrow: 0 }}>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
margin: '8px 0 4px',
}}
>
<h2 style={{ margin: '0 8px ' }}>
{totalRequests
? 'Request: ' + currentRequestIndex + '/' + totalRequests
: 'No Request. Adjust your search criteria to see more results.'}
</h2>
{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 />
Previous
</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;
}
setCurrentRequestIndex(pageIndexToGoto);
loadCurrentRequest(pageIndexToGoto);
}}
>
Go to
</Button>
}
value={pageIndexToGoto}
onChange={(e) => {
setPageIndexToGoto(parseInt(e.target.value));
}}
/>
</div>
</div>
</div>
</div>
<div style={siderStyle}>
{/* <Space.Compact style={{ width: '100%', marginBottom: 16 }}>
<Input
value={`Sub: ${filterSubsidiaries}, Date:${
filterDateRange[0]
? filterDateRange[0] + ' to ' + filterDateRange[1]
: 'All'
}, Reviewed: ${filterReviewState}, Tests: ${filterIncludeTests}`}
readOnly
/>
<Button
type='primary'
size='large'
onClick={() => {
setIsModalOpen(true);
}}
>
Filters
</Button>
</Space.Compact> */}
{/* <div
style={{
display: 'flex',
justifyContent: 'space-between',
marginBottom: 8,
}}
>
<h2 style={{ margin: '0 0 4px' }}>
{totalRequests
? 'Request: ' + currentRequestIndex + '/' + totalRequests
: 'No Request. Adjust your search criteria to see more results.'}
</h2>
{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: 'space-between',
marginBottom: 8,
}}
>
<div>
<Button
type='default'
style={{ height: 38 }}
disabled={currentRequestIndex === 1}
onClick={() => {
gotoPreviousRequest();
}}
>
<ArrowLeftOutlined />
Previous
</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;
}
setCurrentRequestIndex(pageIndexToGoto);
loadCurrentRequest(pageIndexToGoto);
}}
>
Go to
</Button>
}
value={pageIndexToGoto}
onChange={(e) => {
setPageIndexToGoto(parseInt(e.target.value));
}}
/>
</div>
</div> */}
<div>
{dataSource?.map((data) => {
return (
<div style={{ margin: '0 0 8px' }}>
<p style={{ fontWeight: 'bold', margin: '0 0 8px' }}>
{' '}
{data.key}
</p>
<Input
addonBefore='Feedback'
size='small'
readOnly
value={data?.submitted}
/>
<Input
addonBefore='Predicted'
readOnly
size='small'
value={data?.predicted}
/>
<Input
addonBefore='Revised&nbsp; &nbsp;'
size='small'
value={data?.revised}
/>
</div>
);
})}
</div>
</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 }}
options={[
{ value: 'SEAO', label: 'SEAO' },
{ value: 'SEAU', label: 'SEAU' },
{ value: 'SESP', label: 'SESP' },
{ value: 'SME', label: 'SME' },
{ value: 'SEPCO', label: 'SEPCO' },
{ value: 'TSE', label: 'TSE' },
{ value: 'SEIN', label: 'SEIN' },
]}
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>
</Form>
</Modal>
<Modal
title={t`Review`}
open={isReasonModalOpen}
width={700}
onOk={async () => {
// call submit api
if (!reason || !solution) {
notification.warning({
message: 'Please select a reason or a solution',
});
} else {
const params = {
request_id: currentRequest?.RequestID,
request_image_id: selectedFileName.replace(/\.[^/.]+$/, ''),
};
let submitReason = reason;
let submitSolution = solution;
if (reason === 'other') {
if (!otherReason) {
notification.warning({
message: 'Please input other reason',
});
return;
}
submitReason = otherReason;
}
if (solution === 'other') {
if (!otherSolution) {
notification.warning({
message: 'Please input other solution',
});
return;
}
submitSolution = otherSolution;
}
const res = await badQualityReasonSubmit(
params,
submitReason,
submitSolution,
);
if (res.message) {
notification.success({ message: 'Update reason success' });
setIsReasonModalOpen(false);
}
}
}}
onCancel={() => {
setIsReasonModalOpen(false);
}}
>
<div style={{ display: 'flex', justifyContent: 'flex-start' }}>
<Form
style={{
marginTop: 30,
}}
>
<Form.Item name='reason' label={t`Reason for bad quality:`}>
<Select
placeholder='Select a reason'
style={{ width: 200 }}
options={[
{ value: 'invalid_image', label: t`Invalid image` },
{
value: 'missing_information',
label: t`Missing information`,
},
{ value: 'too_blurry_text', label: t`Too blurry text` },
{ value: 'too_small_text', label: t`Too small text` },
{ value: 'handwritten', label: t`Handwritten` },
{ value: 'wrong_feedback', label: t`Wrong Feedback` },
{ value: 'ocr_cannot_extract', label: t`Ocr cannot extract` },
{ value: 'other', label: t`Other` },
]}
onChange={setReason}
value={reason}
/>
</Form.Item>
</Form>
{reason === 'other' && (
<Input
placeholder='Other reason'
value={otherReason}
onChange={(e) => {
setOtherReason(e.target.value);
}}
style={{
width: 200,
marginTop: 30,
marginBottom: 24,
marginLeft: 10,
}}
/>
)}
</div>
<div style={{ display: 'flex', justifyContent: 'flex-start' }}>
<Form>
<Form.Item name='reason' label={t`Solution:`}>
<span style={{ display: 'none' }}>
{counter_measure_map[reason]}
</span>
<Select
placeholder='Select a solution'
style={{ width: 200 }}
options={[
{
value: 'Remove this image from the evaluation report',
label: t`Remove this image from the evaluation report`,
},
{ value: 'Improve OCR', label: t`Improve OCR` },
{
value: 'Update revised result and re-calculate accuracy',
label: t`Update revised result and re-calculate accuracy`,
},
{ value: 'other', label: t`Other` },
]}
onChange={setSolution}
value={solution}
// defaultValue={solution}
/>
</Form.Item>
</Form>
{solution === 'other' && (
<Input
placeholder='Other solution'
value={otherSolution}
onChange={(e) => {
setOtherSolution(e.target.value);
}}
style={{
width: 200,
marginBottom: 24,
marginLeft: 10,
}}
/>
)}
</div>
</Modal>
{/* {totalRequests > 0 && (
<div
style={{
height: '25%',
overflowY: 'auto',
}}
>
<StyledTable
components={components}
rowClassName={() => 'editable-row'}
bordered
dataSource={dataSource}
columns={columns}
/>
</div>
)} */}
</div>
);
};
export default ReviewPage;