diff --git a/cope2n-fe/src/pages/reviews2/DocumentCompareInfo.tsx b/cope2n-fe/src/pages/reviews2/DocumentCompareInfo.tsx new file mode 100644 index 0000000..b1963fe --- /dev/null +++ b/cope2n-fe/src/pages/reviews2/DocumentCompareInfo.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { Button, Descriptions, Input } from 'antd'; +import type { DescriptionsProps } from 'antd'; +import { CopyOutlined } from '@ant-design/icons'; +import { FEEDBACK_RESULT, PREDICTED_RESULT, REVIEWED_RESULT } from './const'; + +const DocumentCompareInfo = ({ key, data, selectedFileDataSource, updateRevisedByFeedback, handleUpdateFileInField, shouldRevised, disabledInput }) => { + const items: DescriptionsProps['items'] = [ + { + key: selectedFileDataSource[data]?.[FEEDBACK_RESULT] || '1', + label: 'Feedback', + children: selectedFileDataSource[data]?.[FEEDBACK_RESULT], + labelStyle: { color: '#333', padding: '4px 16px' }, + contentStyle: { padding: '4px 16px' }, + span: 3 + }, + { + key: selectedFileDataSource[data]?.[PREDICTED_RESULT] || '2', + label: 'Predicted', + children: selectedFileDataSource[data]?.[PREDICTED_RESULT], + labelStyle: { color: '#333', padding: '4px 16px' }, + contentStyle: { padding: '4px 16px' }, + span: 3 + }, + { + key: selectedFileDataSource[data]?.[REVIEWED_RESULT] || '3', + label: 'Revised', + children: <Input + style={{ background: shouldRevised ? 'yellow' : '' }} + value={selectedFileDataSource[data]?.[REVIEWED_RESULT]} + size='small' + onChange={(e) => + handleUpdateFileInField(data, e.target.value) + } + variant="borderless" + disabled={disabledInput === undefined || disabledInput === 0} + />, + labelStyle: { color: '#333', padding: '4px 16px' }, + contentStyle: { padding: '4px 16px' }, + span: 3 + }, + ]; + return ( + <div style={{ margin: '0 0 8px' }} className='file-input-group' key={key}> + <div + style={{ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + margin: '0 0 4px', + }} + > + <p style={{ fontWeight: 'bold', margin: 0 }}>{data}</p> + <Button + shape='round' + type='primary' + ghost + icon={<CopyOutlined />} + size='small' + onClick={() => updateRevisedByFeedback(data)} + disabled={disabledInput === undefined || disabledInput === 0} + /> + </div> + <Descriptions bordered items={items} layout="horizontal" size='small' contentStyle={{ height: '13px' }} /> + </div> + ) +} + +export default DocumentCompareInfo; \ No newline at end of file diff --git a/cope2n-fe/src/pages/reviews2/DocumentHeadInfo.tsx b/cope2n-fe/src/pages/reviews2/DocumentHeadInfo.tsx new file mode 100644 index 0000000..50b811b --- /dev/null +++ b/cope2n-fe/src/pages/reviews2/DocumentHeadInfo.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { Descriptions } from 'antd'; +import type { DescriptionsProps } from 'antd'; + +const DocumentHeadInfo = ({ currentRequest }) => { + const items: DescriptionsProps['items'] = [ + { + key: '1', + label: 'Request ID', + children: currentRequest?.RequestID, + span: 2, + labelStyle: { color: '#333', width: '200px' } + }, + { + key: '2', + label: 'Redemption ID', + children: currentRequest?.RedemptionID, + labelStyle: { color: '#333', width: '200px' } + }, + { + key: '3', + label: 'Raw accuracy', + children: currentRequest?.raw_accuracy, + span: 2, + labelStyle: { color: '#333', width: '200px' } + }, + { + key: '4', + label: 'Processing time', + children: currentRequest?.['Server Processing Time (ms)'], + labelStyle: { color: '#333', width: '200px' } + } + ]; + return ( + <Descriptions bordered items={items} size="small" /> + ) +} + +export default DocumentHeadInfo; \ No newline at end of file diff --git a/cope2n-fe/src/pages/reviews2/api.ts b/cope2n-fe/src/pages/reviews2/api.ts index 229ac6c..4f32ff9 100644 --- a/cope2n-fe/src/pages/reviews2/api.ts +++ b/cope2n-fe/src/pages/reviews2/api.ts @@ -10,6 +10,8 @@ export const fetchAllRequests = async ( filterFeedbackResult: string, filterPredictResult: string, filterReviewedResult: string, + filterBadReason: string, + filterOtherReason: string, page = 1, page_size = 20, max_accuracy = 100, @@ -42,6 +44,11 @@ export const fetchAllRequests = async ( if (filterReviewedResult) { filterStr += `reviewed_result=${filterReviewedResult}&`; } + if (filterBadReason === 'other' && filterOtherReason.trim()) { + filterStr += `bad_reason=${filterOtherReason}&`; + } else if(filterBadReason !== 'other') { + filterStr += `bad_reason=${filterBadReason}&`; + } // if (startDate && endDate) { filterStr += `start_date=${startDate}&end_date=${endDate}&`; @@ -105,7 +112,7 @@ export const fetchRequest = async (id: string) => { }); return await ( await response.json() - ).subscription_requests?.[0] || null; + ).subscription_requests?.[0]; }; export const addRecentRequest = ( diff --git a/cope2n-fe/src/pages/reviews2/index.tsx b/cope2n-fe/src/pages/reviews2/index.tsx index a6d73ab..cd00d33 100644 --- a/cope2n-fe/src/pages/reviews2/index.tsx +++ b/cope2n-fe/src/pages/reviews2/index.tsx @@ -3,7 +3,6 @@ import { ArrowRightOutlined, CheckCircleOutlined, ClockCircleFilled, - CopyOutlined, FullscreenExitOutlined, FullscreenOutlined, } from '@ant-design/icons'; @@ -55,6 +54,8 @@ import { import FileCard from './FileCard'; import RecentRequest from './RecentRequest'; import './style.css'; +import DocumentHeadInfo from './DocumentHeadInfo'; +import DocumentCompareInfo from './DocumentCompareInfo'; const ReviewPage = () => { const [loading, setLoading] = useState(false); @@ -76,6 +77,9 @@ const ReviewPage = () => { const [filterFeedbackResult, setFilterFeedbackResult] = useState(''); const [filterPredictResult, setFilterPredictResult] = useState(''); const [filterReviewedResult, setFilterReviewedResult] = useState(''); + const [filterBadReason, setFilterBadReason] = useState('other'); + const [filterOtherReason, setFilterOtherReason] = useState(''); + // const [requests, setRequests] = useState([]); const [currentRequest, setCurrentRequest] = useState(null); const [currentRequestIndex, setCurrentRequestIndex] = useState(1); @@ -118,6 +122,8 @@ const ReviewPage = () => { filterFeedbackResult, filterPredictResult, filterReviewedResult, + filterBadReason, + filterOtherReason, 1, 1, filterAccuracy, @@ -125,7 +131,7 @@ const ReviewPage = () => { setTotalPages(data?.page?.total_requests); setHasNextRequest(1 < data?.page?.total_requests); const firstRequest = fetchRequest( - data?.subscription_requests[0].RequestID, + data?.subscription_requests?.[0]?.RequestID, ); firstRequest.then(async (data) => { if (data) setCurrentRequest(data); @@ -231,6 +237,8 @@ const ReviewPage = () => { filterFeedbackResult, filterPredictResult, filterReviewedResult, + filterBadReason, + filterOtherReason, requestIndex, 1, filterAccuracy, @@ -290,6 +298,8 @@ const ReviewPage = () => { filterFeedbackResult, filterPredictResult, filterReviewedResult, + filterBadReason, + filterOtherReason, 1, 1, filterAccuracy, @@ -491,42 +501,10 @@ const ReviewPage = () => { > {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: </b> - {currentRequest?.RequestID} - </div>{' '} - <div> - <b>Created at: </b> - {currentRequest?.created_at} - </div>{' '} - <div> - <b>Request time: </b> - {currentRequest?.['Client Request Time (ms)']} - </div>{' '} - <div style={{ gridColumn: 'span 2 / span 2' }}> - <b>Redemption ID: </b> - {currentRequest?.RedemptionID} - </div>{' '} - <div> - <b>Raw accuracy: </b> - {currentRequest?.raw_accuracy} - </div>{' '} - <div style={{ gridColumn: 'span 2 / span 2' }}> - <b>Processing time: </b> - {currentRequest?.['Server Processing Time (ms)']} - </div>{' '} - </div> - )} </div> + {totalRequests && ( + <DocumentHeadInfo currentRequest={currentRequest} /> + )} {totalRequests > 0 && ( <div style={{ @@ -787,47 +765,14 @@ const ReviewPage = () => { } } catch (error) { } return ( - <div style={{ margin: '0 0 8px' }} className='file-input-group' key={data}> - <div - style={{ - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - margin: '0 0 4px', - }} - > - <p style={{ fontWeight: 'bold', margin: 0 }}>{data}</p> - <Button - shape='round' - type='primary' - ghost - icon={<CopyOutlined />} - size='small' - onClick={() => updateRevisedByFeedback(data)} - /> - </div> - <Input - addonBefore='Feedback' - size='small' - readOnly - value={selectedFileDataSource[data]?.[FEEDBACK_RESULT]} - /> - <Input - addonBefore='Predicted' - readOnly - size='small' - value={selectedFileDataSource[data]?.[PREDICTED_RESULT]} - /> - <Input - addonBefore='Revised' - style={{ background: shouldRevised ? 'yellow' : '' }} - size='small' - value={selectedFileDataSource[data]?.[REVIEWED_RESULT]} - onChange={(e) => - handleUpdateFileInField(data, e.target.value) - } - /> - </div> + <DocumentCompareInfo key={data} + data={data} + selectedFileDataSource={selectedFileDataSource} + updateRevisedByFeedback={updateRevisedByFeedback} + handleUpdateFileInField={handleUpdateFileInField} + shouldRevised={shouldRevised} + disabledInput = {currentRequest?.Files?.length} + /> ); })} <b>{t`Bad image reason:`}</b> @@ -919,31 +864,75 @@ const ReviewPage = () => { style={{ marginTop: 30, }} + layout="vertical" > - <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='dateRange' + label={t`Date (GMT+8)`} + rules={[ + { + required: true, + message: 'Please select a date range', + }, + ]} + style={{ + flex: 1, + }} + > + <DatePicker.RangePicker + onChange={(date, dateString) => { + setFilterDateRange(dateString); + }} + style={{ width: 300 }} + /> + </Form.Item> + <div style={{ + flex: 1, + }}> + <Form.Item + name='bad_reason' + label={t`Bad image reason`} + > + <Select + placeholder='Select a reason' + style={{ width: 300 }} + options={REASON_BAD_QUALITY} + value={filterBadReason} + defaultValue={filterBadReason} + onChange={setFilterBadReason} + /> + </Form.Item> + {filterBadReason === 'other' && ( + <Form.Item + name='other_reason' + style={{ + flex: 1, + }} + > + <Input + placeholder='Other reason' + value={filterOtherReason} + style={{ width: 300 }} + onChange={(e) => { + setFilterOtherReason(e.target.value); + }} + /> + </Form.Item> + )} + </div> + </div> + <div + style={{ + display: 'flex', justifyContent: 'space-between', marginLeft: 0, @@ -959,10 +948,13 @@ const ReviewPage = () => { message: 'Please select a subsidiary', }, ]} + style={{ + flex: 1, + }} > <Select placeholder='Select a subsidiary' - style={{ width: 200 }} + style={{ width: 300 }} options={SUBSIDIARIES} value={filterSubsidiaries} defaultValue={filterSubsidiaries} @@ -978,8 +970,12 @@ const ReviewPage = () => { message: 'Please select max accuracy', }, ]} + style={{ + flex: 1, + }} > <InputNumber + style={{ width: 300 }} min={1} max={100} defaultValue={filterAccuracy} @@ -989,7 +985,7 @@ const ReviewPage = () => { </div> <div style={{ - marginTop: 10, + display: 'flex', justifyContent: 'space-between', marginLeft: 0, @@ -1005,9 +1001,12 @@ const ReviewPage = () => { message: 'Please select review status', }, ]} + style={{ + flex: 1, + }} > <Select - style={{ width: 200 }} + style={{ width: 300 }} options={[ { label: 'All', value: 'all' }, { label: 'Reviewed', value: 'reviewed' }, @@ -1027,10 +1026,10 @@ const ReviewPage = () => { message: 'Please select test status', }, ]} - style={{ marginLeft: 16 }} + style={{ flex: 1 }} > <Select - style={{ width: 200 }} + style={{ width: 300 }} options={[ { label: 'Include tests', value: 'true' }, { label: 'Exclude tests', value: 'false' }, @@ -1044,7 +1043,7 @@ const ReviewPage = () => { {/* add 4 more filter fields */} <div style={{ - marginTop: 10, + display: 'flex', justifyContent: 'space-between', marginLeft: 0, @@ -1054,16 +1053,13 @@ const ReviewPage = () => { <Form.Item name='doc_type' label={t`Only type`} - rules={[ - { - required: true, - message: 'Please select a document type', - }, - ]} + style={{ + flex: 1, + }} > <Select placeholder='Select a document type' - style={{ width: 200 }} + style={{ width: 300 }} options={DOCTYPE} value={filterDoctype} defaultValue={filterDoctype} @@ -1073,8 +1069,12 @@ const ReviewPage = () => { <Form.Item name='feedback_result' label={t`Feedback includes`} + style={{ + flex: 1, + }} > <Input + style={{ width: 300 }} defaultValue={filterFeedbackResult} onChange={(e) => setFilterFeedbackResult(e.target.value)} /> @@ -1082,7 +1082,7 @@ const ReviewPage = () => { </div> <div style={{ - marginTop: 10, + display: 'flex', justifyContent: 'space-between', marginLeft: 0, @@ -1092,17 +1092,25 @@ const ReviewPage = () => { <Form.Item name='predict_result' label={t`Predict includes`} + style={{ + flex: 1, + }} > <Input + style={{ width: 300 }} defaultValue={filterPredictResult} onChange={(e) => setFilterPredictResult(e.target.value)} /> </Form.Item> <Form.Item name='reviewed_result' - label={t`Review inculdes`} + label={t`Review includes`} + style={{ + flex: 1, + }} > <Input + style={{ width: 300 }} defaultValue={filterReviewedResult} onChange={(e) => setFilterReviewedResult(e.target.value)} />