diff --git a/mephisto/client/review_app/client/src/pages/TaskPage/Header/Header.tsx b/mephisto/client/review_app/client/src/pages/TaskPage/Header/Header.tsx index 63d101254..762d68b6a 100644 --- a/mephisto/client/review_app/client/src/pages/TaskPage/Header/Header.tsx +++ b/mephisto/client/review_app/client/src/pages/TaskPage/Header/Header.tsx @@ -10,7 +10,21 @@ import logo from 'static/images/logo.svg'; import './Header.css'; -function Header() { +interface PropsType { + taskStats: TaskStatsType; + workerId?: number; + workerStats: WorkerStatsType; +} + + +function Header(props: PropsType) { + const wStats = props.workerStats; + const tStats = props.taskStats; + + const toPercent = (total: number, value: number): number => { + return total !== 0 ? Math.round(value * 100 / total) : 0; + }; + return @@ -28,19 +42,76 @@ function Header() { + {/* Worker line */} - worker - 15/25 - 16 (80%) - 1 (5%) - 3 (15%) + Worker {props.workerId} + + {wStats.total_count ? (<> + {wStats.reviewed_count}/{wStats.total_count} + ) : (<> + --/-- + )} + + + {wStats.total_count ? (<> + {wStats.approved_count}{' '} + ({toPercent(wStats.total_count, wStats.approved_count)}%) + ) : (<> + -- + )} + + + {wStats.total_count ? (<> + {wStats.soft_rejected_count}{' '} + ({toPercent(wStats.total_count, wStats.soft_rejected_count)}%) + ) : (<> + -- + )} + + + {wStats.total_count ? (<> + {wStats.rejected_count}{' '} + ({toPercent(wStats.total_count, wStats.rejected_count)}%) + ) : (<> + -- + )} + + + {/* Total line */} Total - 64/256 - 186 (78%) - 23 (7%) - 56 (17%) + + {tStats.total_count ? (<> + {tStats.reviewed_count}/{tStats.total_count} + ) : (<> + --/-- + )} + + + {tStats.total_count ? (<> + {tStats.approved_count}{' '} + ({toPercent(tStats.total_count, tStats.approved_count)}%) + ) : (<> + -- + )} + + + {tStats.total_count ? (<> + {tStats.soft_rejected_count}{' '} + ({toPercent(tStats.total_count, tStats.soft_rejected_count)}%) + ) : (<> + -- + )} + + + {tStats.total_count ? (<> + {tStats.rejected_count}{' '} + ({toPercent(tStats.total_count, tStats.rejected_count)}%) + ) : (<> + -- + )} + diff --git a/mephisto/client/review_app/client/src/pages/TaskPage/ModalForm/ModalForm.tsx b/mephisto/client/review_app/client/src/pages/TaskPage/ModalForm/ModalForm.tsx index 3b3bb1712..5804b0d64 100644 --- a/mephisto/client/review_app/client/src/pages/TaskPage/ModalForm/ModalForm.tsx +++ b/mephisto/client/review_app/client/src/pages/TaskPage/ModalForm/ModalForm.tsx @@ -21,9 +21,9 @@ type ModalFormProps = { function ModalForm(props: ModalFormProps) { - const [qualifications, setQualifications] = React.useState>(null); + const [qualifications, setQualifications] = React.useState>(null); const [loading, setLoading] = React.useState(false); - const [errors, setErrors] = React.useState(null); + const [errors, setErrors] = React.useState(null); const onChangeAssign = (value: boolean) => { let prevFormData: FormType = Object(props.data.form); @@ -99,7 +99,7 @@ function ModalForm(props: ModalFormProps) { onChange={(e) => onChangeAssignQualification(e.target.value)} > - {qualifications && qualifications.map((q: Qualification) => { + {qualifications && qualifications.map((q: QualificationType) => { return ; })} diff --git a/mephisto/client/review_app/client/src/pages/TaskPage/TaskPage.tsx b/mephisto/client/review_app/client/src/pages/TaskPage/TaskPage.tsx index 7dd4162e3..e7fcff41a 100644 --- a/mephisto/client/review_app/client/src/pages/TaskPage/TaskPage.tsx +++ b/mephisto/client/review_app/client/src/pages/TaskPage/TaskPage.tsx @@ -4,12 +4,21 @@ * LICENSE file in the root directory of this source tree. */ +import { ReviewType } from 'consts/review'; import cloneDeep from 'lodash/cloneDeep'; import * as React from 'react'; import { useEffect } from 'react'; import { Button } from 'react-bootstrap'; import { useParams } from 'react-router-dom'; -import { getUnits } from 'requests/units'; +import { getStats } from 'requests/stats'; +import { getTaskWorkerUnitsIds } from 'requests/tasks'; +import { + getUnits, + getUnitsDetails, + postUnitsApprove, + postUnitsReject, + postUnitsSoftReject, +} from 'requests/units'; import Header from './Header/Header'; import { APPROVE_MODAL_DATA_STATE, @@ -20,6 +29,15 @@ import ReviewModal from './ReviewModal/ReviewModal'; import './TaskPage.css'; +const defaultStats = { + total_count: 0, + reviewed_count: 0, + approved_count: 0, + rejected_count: 0, + soft_rejected_count: 0, +} + + type ParamsType = { id: string; }; @@ -28,15 +46,47 @@ type ParamsType = { function TaskPage() { const params = useParams(); - const [units, setUnits] = React.useState>(null); + const [units, setUnits] = React.useState>(null); const [loading, setLoading] = React.useState(false); - const [errors, setErrors] = React.useState(null); + const [errors, setErrors] = React.useState(null); const [modalShow, setModalShow] = React.useState(false); const [modalData, setModalData] = React.useState( cloneDeep(APPROVE_MODAL_DATA_STATE) ); + const [taskStats, setTaskStats] = React.useState(defaultStats); + const [workerStats, setWorkerStats] = React.useState(defaultStats); + const [workerUnits, setWorkerUnits] = React.useState>([]); + const [unitsOnReview, setUnitsOnReview] = React.useState<[number, number[]]>(null); + const [currentWorkerOnReview, setcurrentWorkerOnReview] = React.useState(null); + const [currentUnitOnReview, setCurrentUnitOnReview] = React.useState(null); + const [unitDetails, setUnitDetails] = React.useState(null); + + const onGetTaskWorkerUnitsIdsSuccess = (workerUnitsIds: WorkerUnitIdType[]) => { + setWorkerUnits(() => { + const workerUnitsMap = {}; + + workerUnitsIds.forEach((item: WorkerUnitIdType) => { + let prevValue = workerUnitsMap[item.worker_id] || []; + prevValue.push(item.unit_id); + workerUnitsMap[item.worker_id] = prevValue; + }); + + let sortedValue = []; + for (let i in workerUnitsMap) { + sortedValue.push([Number(i), workerUnitsMap[i]]); + } + + // Sort workers by number of their units + sortedValue.sort((a: [number, number[]], b: [number, number[]]) => { + return a[1].length < b[1].length ? 1 : -1; + }); + + return sortedValue; + }) + }; + const onApproveClick = () => { setModalShow(true); setModalData(cloneDeep(APPROVE_MODAL_DATA_STATE)); @@ -52,24 +102,76 @@ function TaskPage() { setModalData(cloneDeep(REJECT_MODAL_DATA_STATE)); }; + const onModalSubmitSuccess = () => { + + }; + const onModalSubmit = () => { setModalShow(false); console.log('Data:', modalData); + + if (modalData.type === ReviewType.APPROVE) { + postUnitsApprove( + onModalSubmitSuccess, setLoading, setErrors, {unit_ids: [currentUnitOnReview]}, + ); + } + else if (modalData.type === ReviewType.SOFT_REJECT) { + postUnitsSoftReject( + onModalSubmitSuccess, setLoading, setErrors, {unit_ids: [currentUnitOnReview]}, + ); + } + else if (modalData.type === ReviewType.REJECT) { + postUnitsReject( + onModalSubmitSuccess, setLoading, setErrors, {unit_ids: [currentUnitOnReview]}, + ); + } }; // Effects useEffect(() => { if (units === null) { - getUnits(setUnits, setLoading, setErrors, {task_id: params.id}); + // getUnits(setUnits, setLoading, setErrors, {task_id: params.id}); + getTaskWorkerUnitsIds( + Number(params.id), onGetTaskWorkerUnitsIdsSuccess, setLoading, setErrors, + ); } }, []); + useEffect(() => { + if (Object.keys(workerUnits).length) { + const firstWrokerUnits = workerUnits[0]; + setUnitsOnReview(firstWrokerUnits); + setcurrentWorkerOnReview(firstWrokerUnits[0]); + setCurrentUnitOnReview(firstWrokerUnits[1][0]); + } + }, [workerUnits]); + + useEffect(() => { + if (unitsOnReview && unitsOnReview[1].length) { + getUnits( + setUnits, setLoading, setErrors, {task_id: params.id, unit_ids: unitsOnReview[1].join(',')}, + ); + } + }, [unitsOnReview]); + + useEffect(() => { + if (units && currentUnitOnReview) { + getStats(setTaskStats, setLoading, setErrors, {task_id: params.id}); + getStats(setWorkerStats, setLoading, setErrors, {worker_id: currentWorkerOnReview}); + getUnitsDetails(setUnitDetails, setLoading, setErrors, {unit_ids: currentUnitOnReview}); + } + }, [units, currentUnitOnReview]); + if (units === null) { return null; } return
-
+
diff --git a/mephisto/client/review_app/client/src/pages/TasksPage/TasksPage.tsx b/mephisto/client/review_app/client/src/pages/TasksPage/TasksPage.tsx index 18a3a9be9..0ec5b697b 100644 --- a/mephisto/client/review_app/client/src/pages/TasksPage/TasksPage.tsx +++ b/mephisto/client/review_app/client/src/pages/TasksPage/TasksPage.tsx @@ -20,9 +20,9 @@ function TasksPage() { const navigate = useNavigate(); const { localStorage } = window; - const [tasks, setTasks] = React.useState>(null); + const [tasks, setTasks] = React.useState>(null); const [loading, setLoading] = React.useState(false); - const [errors, setErrors] = React.useState(null); + const [errors, setErrors] = React.useState(null); const onTaskClick = (id: number) => { localStorage.setItem(STORAGE_TASK_ID_KEY, String(id)); @@ -42,7 +42,7 @@ function TasksPage() {
{errors.error}
)} - {tasks && tasks.map((task: Task, index) => { + {tasks && tasks.map((task: TaskType, index) => { return