Skip to content

Commit

Permalink
[WIP] Task review app - added getting units and stats data
Browse files Browse the repository at this point in the history
  • Loading branch information
meta-paul committed Sep 9, 2023
1 parent 16e6e5d commit 03d4e8f
Show file tree
Hide file tree
Showing 11 changed files with 311 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Container className={'task-header'}>
<Row>
<Col className={"logo"} sm={3}>
Expand All @@ -28,19 +42,76 @@ function Header() {
</tr>
</thead>
<tbody>
{/* Worker line */}
<tr>
<td>worker</td>
<td><b>15</b>/25</td>
<td><b>16</b> (80%)</td>
<td><b>1</b> (5%)</td>
<td><b>3</b> (15%)</td>
<td>Worker {props.workerId}</td>
<td>
{wStats.total_count ? (<>
<b>{wStats.reviewed_count}</b>/{wStats.total_count}
</>) : (<>
<b>--</b>/--
</>)}
</td>
<td>
{wStats.total_count ? (<>
<b>{wStats.approved_count}</b>{' '}
({toPercent(wStats.total_count, wStats.approved_count)}%)
</>) : (<>
<b>--</b>
</>)}
</td>
<td>
{wStats.total_count ? (<>
<b>{wStats.soft_rejected_count}</b>{' '}
({toPercent(wStats.total_count, wStats.soft_rejected_count)}%)
</>) : (<>
<b>--</b>
</>)}
</td>
<td>
{wStats.total_count ? (<>
<b>{wStats.rejected_count}</b>{' '}
({toPercent(wStats.total_count, wStats.rejected_count)}%)
</>) : (<>
<b>--</b>
</>)}
</td>
</tr>

{/* Total line */}
<tr className={"total"}>
<td>Total</td>
<td><b>64</b>/256</td>
<td><b>186</b> (78%)</td>
<td><b>23</b> (7%)</td>
<td><b>56</b> (17%)</td>
<td>
{tStats.total_count ? (<>
<b>{tStats.reviewed_count}</b>/{tStats.total_count}
</>) : (<>
<b>--</b>/--
</>)}
</td>
<td>
{tStats.total_count ? (<>
<b>{tStats.approved_count}</b>{' '}
({toPercent(tStats.total_count, tStats.approved_count)}%)
</>) : (<>
<b>--</b>
</>)}
</td>
<td>
{tStats.total_count ? (<>
<b>{tStats.soft_rejected_count}</b>{' '}
({toPercent(tStats.total_count, tStats.soft_rejected_count)}%)
</>) : (<>
<b>--</b>
</>)}
</td>
<td>
{tStats.total_count ? (<>
<b>{tStats.rejected_count}</b>{' '}
({toPercent(tStats.total_count, tStats.rejected_count)}%)
</>) : (<>
<b>--</b>
</>)}
</td>
</tr>
</tbody>
</Table>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ type ModalFormProps = {


function ModalForm(props: ModalFormProps) {
const [qualifications, setQualifications] = React.useState<Array<Qualification>>(null);
const [qualifications, setQualifications] = React.useState<Array<QualificationType>>(null);
const [loading, setLoading] = React.useState(false);
const [errors, setErrors] = React.useState<ErrorResponse>(null);
const [errors, setErrors] = React.useState<ErrorResponseType>(null);

const onChangeAssign = (value: boolean) => {
let prevFormData: FormType = Object(props.data.form);
Expand Down Expand Up @@ -99,7 +99,7 @@ function ModalForm(props: ModalFormProps) {
onChange={(e) => onChangeAssignQualification(e.target.value)}
>
<option value={''}>---</option>
{qualifications && qualifications.map((q: Qualification) => {
{qualifications && qualifications.map((q: QualificationType) => {
return <option key={"qual" + q.id} value={q.id}>{q.name}</option>;
})}
</Form.Select>
Expand Down
112 changes: 107 additions & 5 deletions mephisto/client/review_app/client/src/pages/TaskPage/TaskPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
};
Expand All @@ -28,15 +46,47 @@ type ParamsType = {
function TaskPage() {
const params = useParams<ParamsType>();

const [units, setUnits] = React.useState<Array<Unit>>(null);
const [units, setUnits] = React.useState<Array<UnitType>>(null);
const [loading, setLoading] = React.useState(false);
const [errors, setErrors] = React.useState<ErrorResponse>(null);
const [errors, setErrors] = React.useState<ErrorResponseType>(null);

const [modalShow, setModalShow] = React.useState<boolean>(false);
const [modalData, setModalData] = React.useState<ModalDataType>(
cloneDeep(APPROVE_MODAL_DATA_STATE)
);

const [taskStats, setTaskStats] = React.useState<TaskStatsType>(defaultStats);
const [workerStats, setWorkerStats] = React.useState<WorkerStatsType>(defaultStats);
const [workerUnits, setWorkerUnits] = React.useState<Array<[number, number[]]>>([]);
const [unitsOnReview, setUnitsOnReview] = React.useState<[number, number[]]>(null);
const [currentWorkerOnReview, setcurrentWorkerOnReview] = React.useState<number>(null);
const [currentUnitOnReview, setCurrentUnitOnReview] = React.useState<number>(null);
const [unitDetails, setUnitDetails] = React.useState<UnitDetailsType>(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));
Expand All @@ -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 <div className={'task'}>
<Header />
<Header
taskStats={taskStats}
workerStats={workerStats}
workerId={unitsOnReview ? currentWorkerOnReview : null}
/>

<div className={"buttons"}>
<Button variant={"success"} size={"sm"} onClick={onApproveClick}>Approve</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ function TasksPage() {
const navigate = useNavigate();
const { localStorage } = window;

const [tasks, setTasks] = React.useState<Array<Task>>(null);
const [tasks, setTasks] = React.useState<Array<TaskType>>(null);
const [loading, setLoading] = React.useState(false);
const [errors, setErrors] = React.useState<ErrorResponse>(null);
const [errors, setErrors] = React.useState<ErrorResponseType>(null);

const onTaskClick = (id: number) => {
localStorage.setItem(STORAGE_TASK_ID_KEY, String(id));
Expand All @@ -42,7 +42,7 @@ function TasksPage() {
<div>{errors.error}</div>
)}

{tasks && tasks.map((task: Task, index) => {
{tasks && tasks.map((task: TaskType, index) => {
return <div key={'task' + index}>
<Button
variant={task.is_reviewed ? "secondary" : "primary"}
Expand Down
Loading

0 comments on commit 03d4e8f

Please sign in to comment.