Skip to content

Commit

Permalink
feat: Edit criterion type functionality (#2516)
Browse files Browse the repository at this point in the history
* refactor(#2405): Extract components for reuse

* refactor(#2405): remove unneeded constructions

* refactor: move TaskType to constants

* refactor: criteria actions as text

* refactor: rename prop

* refactor: get rid of input type

* feat: try to change select value

* feat: changeTaskType

* fix cancel

* chore: comment

* refactor: cleanup props

* refactor: use map

* refactor: debug

* refactor: make it work

* chore: remove todo

* chore: remove unneeded

* chore: format
  • Loading branch information
Natalia Gulko authored Jul 24, 2024
1 parent 364c88f commit c957ad5
Show file tree
Hide file tree
Showing 13 changed files with 232 additions and 145 deletions.
85 changes: 43 additions & 42 deletions client/src/modules/CrossCheck/AddCriteriaForCrossCheck.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Button, Form, InputNumber, Select, Input, message } from 'antd';
import { TaskType } from 'modules/CrossCheck/components/CrossCheckCriteriaForm';
import React, { useMemo, useState } from 'react';
import { Button, Form, InputNumber, Input, message } from 'antd';
import React, { ChangeEventHandler, useMemo, useState } from 'react';
import { CrossCheckCriteriaType, IAddCriteriaForCrossCheck } from 'services/course';
import { CriteriaTypeSelect } from './CriteriaTypeSelect';
import { TaskType } from './constants';

const { Item } = Form;
const { TextArea } = Input;
const { Option } = Select;

export const AddCriteriaForCrossCheck = ({ onCreate }: IAddCriteriaForCrossCheck) => {
const [type, setType] = useState<CrossCheckCriteriaType>('title');
Expand All @@ -19,27 +19,28 @@ export const AddCriteriaForCrossCheck = ({ onCreate }: IAddCriteriaForCrossCheck
setMax(0);
setMaxPenalty(0);
setText('');
setType('title');
};

const onFinish = () => {
let criteriaDetails;
type.toLowerCase() === TaskType.Title
? (criteriaDetails = {
key: DEFAULT_KEY,
text: text,
type: type,
index: DEFAULT_INDEX,
})
: (criteriaDetails = {
key: DEFAULT_KEY,
max: type.toLowerCase() === TaskType.Penalty ? -Math.abs(maxPenalty) : max,
text: text,
type: type,
index: DEFAULT_INDEX,
});
clearInputs();
const onSave = () => {
const criteriaDetails =
type === TaskType.Title
? {
key: DEFAULT_KEY,
text: text,
type: type,
index: DEFAULT_INDEX,
}
: {
key: DEFAULT_KEY,
max: type === TaskType.Penalty ? -Math.abs(maxPenalty) : max,
text: text,
type: type,
index: DEFAULT_INDEX,
};
onCreate(criteriaDetails);
message.success('Criteria added!');
clearInputs();
message.success('Criteria added.');
};

function changeMax(value: number | null) {
Expand All @@ -54,52 +55,52 @@ export const AddCriteriaForCrossCheck = ({ onCreate }: IAddCriteriaForCrossCheck
setType(value);
}

const isDisabled: boolean = useMemo(() => {
if (type.toLocaleLowerCase() === TaskType.Title) {
return text ? false : true;
const changeText: ChangeEventHandler<HTMLTextAreaElement> = event => {
setText(event.target.value);
};

const canSave: boolean = useMemo(() => {
if (type === TaskType.Title) {
return !!text;
}
if (type.toLocaleLowerCase() === TaskType.Penalty) {
return text && maxPenalty ? false : true;

if (type === TaskType.Penalty) {
return !!(text && maxPenalty);
}
return text && max ? false : true;

return !!(text && max);
}, [type, max, maxPenalty, text]);

return (
<>
<Item label="Criteria Type">
<Select placeholder="Select type" onChange={changeType}>
<Option data-testid="Title" value="Title">
Title
</Option>
<Option data-testid="Subtask" value="Subtask">
Subtask
</Option>
<Option value="Penalty">Penalty</Option>
</Select>
<CriteriaTypeSelect onChange={changeType} />
</Item>

{type.toLowerCase() === TaskType.Subtask ? (
{type === TaskType.Subtask && (
<Item label="Add Max Score">
<InputNumber value={max} min={0} step={1} onChange={changeMax} />
</Item>
) : type.toLowerCase() === TaskType.Penalty ? (
)}

{type === TaskType.Penalty && (
<Item label="Add Max Penalty">
<InputNumber value={maxPenalty} step={1} onChange={changeMaxPenalty} />
</Item>
) : null}
)}

<Item label="Add Text">
<TextArea
rows={3}
style={{ maxWidth: 1200 }}
placeholder="Add description"
value={text}
onChange={e => setText(e.target.value)}
onChange={changeText}
></TextArea>
</Item>

<div style={{ textAlign: 'right' }}>
<Button type="primary" onClick={onFinish} disabled={isDisabled}>
<Button type="primary" onClick={onSave} disabled={!canSave}>
Add New Criteria
</Button>
</div>
Expand Down
49 changes: 49 additions & 0 deletions client/src/modules/CrossCheck/CriteriaActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { FC } from 'react';
import { CriteriaDto } from '../../api';
import { Popconfirm, Typography, Space } from 'antd';

interface CriteriaActionsProps {
editing: boolean;
record: CriteriaDto;
editingKey: string;
save: (key: string) => void;
remove: (key: string) => void;
cancel: () => void;
edit: (record: CriteriaDto) => void;
}

export const CriteriaActions: FC<CriteriaActionsProps> = ({
editing,
cancel,
remove,
edit,
save,
record,
editingKey,
}) =>
editing ? (
<Space direction="horizontal">
<Typography.Link onClick={() => save(record.key)}>Save</Typography.Link>
<Typography.Link type="secondary" onClick={cancel}>
Cancel
</Typography.Link>
</Space>
) : (
<Space direction="horizontal">
<Typography.Link disabled={!!editingKey} onClick={() => edit(record)}>
Edit
</Typography.Link>
<Popconfirm
title="Are you sure to delete this criteria?"
okText="Delete"
okButtonProps={{
danger: true,
}}
onConfirm={() => remove(record.key)}
>
<Typography.Link disabled={!!editingKey} type="danger">
Delete
</Typography.Link>
</Popconfirm>
</Space>
);
18 changes: 18 additions & 0 deletions client/src/modules/CrossCheck/CriteriaTypeSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Select } from 'antd';
import { FC } from 'react';
import { TaskType } from './constants';
import { SelectProps } from 'antd/lib';

const options = Object.entries(TaskType).map(([label, value]) => ({ label, value }));

interface CriteriaTypeSelectProps extends SelectProps {}

export const CriteriaTypeSelect: FC<CriteriaTypeSelectProps> = props => (
<Select placeholder="Select type" {...props}>
{options.map(({ label, value }) => (
<Select.Option data-testid={label} value={value} key={label}>
{label}
</Select.Option>
))}
</Select>
);
40 changes: 11 additions & 29 deletions client/src/modules/CrossCheck/EditableCellForCrossCheck.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,31 @@
import { Form, InputNumber, Input } from 'antd';
import { CriteriaDto } from 'api';
import React from 'react';
import { TaskType } from './components/CrossCheckCriteriaForm';

const { TextArea } = Input;
import { EditableCriteriaInput } from './EditableCriteriaInput';
import { EditableTableColumnsDataIndex } from './constants';

interface EditableCellProps extends React.HTMLAttributes<HTMLElement> {
editing: boolean;
dataIndex: string;
inputType: 'max' | 'text' | 'description';
dataIndex: EditableTableColumnsDataIndex;
record: CriteriaDto;
index: number;
children: React.ReactNode;
type: TaskType;
points: number | undefined;
onSelectChange: (value: string) => void;
}

export const EditableCellForCrossCheck: React.FC<EditableCellProps> = ({
editing,
dataIndex,
inputType,
children,
type,
points,
...restProps
record,
onSelectChange,
...props
}) => {
const isPointsEqualZero = points === 0;

const inputNode =
inputType === 'description' ? (
<TextArea rows={3} />
) : type !== TaskType.Title ? (
<InputNumber style={{ width: 65 }} />
) : null;
const hasMax = record?.max !== 0;

return (
<td
{...restProps}
title={isPointsEqualZero ? 'Check points for this line' : ''}
style={{ color: isPointsEqualZero ? 'red' : 'black' }}
>
{editing && inputType !== 'text' ? (
<Form.Item name={dataIndex} style={{ margin: 0 }}>
{inputNode}
</Form.Item>
<td {...props} title={hasMax ? '' : 'Check points for this line'} style={{ color: hasMax ? 'black' : 'red' }}>
{editing ? (
<EditableCriteriaInput dataIndex={dataIndex} onSelectChange={onSelectChange} type={record?.type} />
) : (
children
)}
Expand Down
36 changes: 36 additions & 0 deletions client/src/modules/CrossCheck/EditableCriteriaInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Form, Input, InputNumber } from 'antd';
import { FC } from 'react';
import { CriteriaTypeSelect } from './CriteriaTypeSelect';
import { EditableTableColumnsDataIndex, TaskType } from './constants';
import { CriteriaDtoTypeEnum } from 'api';

interface EditableCriteriaInputProps {
type?: CriteriaDtoTypeEnum;
dataIndex: EditableTableColumnsDataIndex;
onSelectChange: (value: string) => void;
}

export const EditableCriteriaInput: FC<EditableCriteriaInputProps> = ({ dataIndex, onSelectChange, type }) => {
switch (dataIndex) {
case EditableTableColumnsDataIndex.Max:
return type !== TaskType.Title ? (
<Form.Item name={dataIndex} style={{ margin: 0 }}>
<InputNumber style={{ width: 65 }} />
</Form.Item>
) : null;
case EditableTableColumnsDataIndex.Type:
return (
<Form.Item name={dataIndex} style={{ margin: 0 }}>
<CriteriaTypeSelect onChange={onSelectChange} />
</Form.Item>
);
case EditableTableColumnsDataIndex.Text:
return (
<Form.Item name={dataIndex} style={{ margin: 0 }}>
<Input.TextArea rows={3} />
</Form.Item>
);
default:
return null;
}
};
Loading

0 comments on commit c957ad5

Please sign in to comment.