Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Implement read-only task components #35

Merged
merged 5 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Flex } from '@radix-ui/themes'
import React, { FC } from 'react'

import Markdown from '../../Markdown'
import { TaskType } from './constants'
import './index.css'
import Markdown from '../../../Markdown'
import { TaskType } from '../constants'
import '../styles/common.css'
import { CodeTask, CodeTaskProps } from './variants/CodeTask'
import { EssayTask, EssayTaskProps } from './variants/EssayTask'
import { FlagTask, FlagTaskProps } from './variants/FlagTask'
Expand Down Expand Up @@ -38,7 +38,7 @@ const taskComponentMap = {

type TaskComponent = FC<TaskProps & { instructions?: string }>

export const TaskFactory: TaskComponent = ({ instructions, ...taskProps }) => {
export const EditableTaskFactory: TaskComponent = ({ instructions, ...taskProps }) => {
const Component = taskComponentMap[taskProps.type] as TaskComponent
return (
<Flex gap="3" direction="column">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,17 @@ import { TextArea, TextField } from '@radix-ui/themes'
import { isEqual } from 'lodash'
import React, { FC, useEffect, useMemo, useState } from 'react'

import useDebounce from '../../../../hooks/debouncing'
import { TaskType } from '../constants'
import '../index.css'
import { TaskBaseProps } from '../types'
import useDebounce from '../../../../../hooks/debouncing'
import { TaskType } from '../../constants'
import '../../styles/common.css'
import { EditableTaskProps } from '../../types'

export interface CodeTaskProps extends TaskBaseProps {
export interface CodeTaskProps extends EditableTaskProps {
type: TaskType.CODE
lines?: number
}

export const CodeTask: FC<CodeTaskProps> = ({
answer,
onAnswerUpdate,
lines = 5,
disabled = false,
}) => {
export const CodeTask: FC<CodeTaskProps> = ({ answer, onAnswerUpdate, lines = 5 }) => {
const initialValue = useMemo(() => answer?.answer ?? '', [answer])
const [value, setValue] = useState(initialValue)
const debouncedValue = useDebounce(value)
Expand All @@ -38,7 +33,6 @@ export const CodeTask: FC<CodeTaskProps> = ({
value: value,
onChange: handleOnChange,
placeholder: 'Your answer here…',
disabled: disabled,
variant: 'soft' as 'soft',
className: 'monospaced',
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,16 @@ import { TextArea, TextField } from '@radix-ui/themes'
import { isEqual } from 'lodash'
import React, { FC, useEffect, useMemo, useState } from 'react'

import useDebounce from '../../../../hooks/debouncing'
import { TaskType } from '../constants'
import { TaskBaseProps } from '../types'
import useDebounce from '../../../../../hooks/debouncing'
import { TaskType } from '../../constants'
import { EditableTaskProps } from '../../types'

export interface EssayTaskProps extends TaskBaseProps {
export interface EssayTaskProps extends EditableTaskProps {
type: TaskType.ESSAY
lines?: number
}

export const EssayTask: FC<EssayTaskProps> = ({
answer,
onAnswerUpdate,
lines = 5,
disabled = false,
}) => {
export const EssayTask: FC<EssayTaskProps> = ({ answer, onAnswerUpdate, lines = 5 }) => {
const initialValue = useMemo(() => answer?.answer ?? '', [answer])
const [value, setValue] = useState(initialValue)
const debouncedValue = useDebounce(value)
Expand All @@ -38,7 +33,6 @@ export const EssayTask: FC<EssayTaskProps> = ({
value: value,
onChange: handleOnChange,
placeholder: 'Your answer here…',
disabled: disabled,
variant: 'soft' as 'soft',
}
if (lines === 1) return <TextField.Root {...commonProps} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,15 @@ import React, { FC, useEffect, useMemo, useState } from 'react'

import useDebounce from '../../../../../hooks/debouncing'
import { TaskType } from '../../constants'
import { TaskBaseProps } from '../../types'
import './index.css'
import '../../styles/flag.css'
import { EditableTaskProps } from '../../types'

export interface FlagTaskProps extends TaskBaseProps {
export interface FlagTaskProps extends EditableTaskProps {
type: TaskType.FLAG
showOrnament?: boolean
}

export const FlagTask: FC<FlagTaskProps> = ({
answer,
onAnswerUpdate,
showOrnament = true,
disabled = false,
}) => {
export const FlagTask: FC<FlagTaskProps> = ({ answer, onAnswerUpdate, showOrnament = true }) => {
const FLAG_LENGTH = 32
const initialValue = useMemo(() => answer?.answer ?? '', [answer])
const [value, setValue] = useState(initialValue)
Expand All @@ -45,7 +40,6 @@ export const FlagTask: FC<FlagTaskProps> = ({
</Flex>
)}
<TextField.Root
disabled={disabled}
value={value}
onChange={handleChange}
radius="none"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,25 @@ import { CheckboxGroup, RadioGroup } from '@radix-ui/themes'
import { isEqual, map } from 'lodash'
import React, { FC, useEffect, useMemo, useState } from 'react'

import { TaskType } from '../constants'
import { TaskBaseProps } from '../types'
import { TaskType } from '../../constants'
import { EditableTaskProps } from '../../types'

type MCQOption = {
value: string
label: string
}

export interface MCQOneTaskProps extends TaskBaseProps {
export interface MCQOneTaskProps extends EditableTaskProps {
type: TaskType.MULTIPLE_CHOICE_SELECT_ONE
choices: MCQOption[]
}

export interface MCQMultiTaskProps extends TaskBaseProps {
export interface MCQMultiTaskProps extends EditableTaskProps {
type: TaskType.MULTIPLE_CHOICE_SELECT_SEVERAL
choices: MCQOption[]
}

export const MCQOneTask: FC<MCQOneTaskProps> = ({
answer,
onAnswerUpdate,
choices,
disabled = false,
}) => {
export const MCQOneTask: FC<MCQOneTaskProps> = ({ answer, onAnswerUpdate, choices }) => {
const initialValue = useMemo(() => answer?.answer ?? '', [answer])
const [value, setValue] = useState(initialValue)
useEffect(() => {
Expand All @@ -41,7 +36,7 @@ export const MCQOneTask: FC<MCQOneTaskProps> = ({
}

return (
<RadioGroup.Root variant="soft" disabled={disabled} value={value} onClick={handleOnClick}>
<RadioGroup.Root variant="soft" value={value} onClick={handleOnClick}>
{map(choices, (o) => (
<RadioGroup.Item key={o.value} value={o.value}>
{o.label}
Expand All @@ -51,12 +46,7 @@ export const MCQOneTask: FC<MCQOneTaskProps> = ({
)
}

export const MCQMultiTask: FC<MCQMultiTaskProps> = ({
answer,
onAnswerUpdate,
choices,
disabled = false,
}) => {
export const MCQMultiTask: FC<MCQMultiTaskProps> = ({ answer, onAnswerUpdate, choices }) => {
const initialValue = useMemo(() => (answer?.answer ? answer.answer.split(',') : []), [answer])
const [value, setValue] = useState(initialValue)
useEffect(() => {
Expand All @@ -74,7 +64,7 @@ export const MCQMultiTask: FC<MCQMultiTaskProps> = ({
}

return (
<CheckboxGroup.Root disabled={disabled} variant="soft" value={value}>
<CheckboxGroup.Root variant="soft" value={value}>
{map(choices, (o) => (
<CheckboxGroup.Item key={o.value} value={o.value} onClick={handleOnClick}>
{o.label}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { TextField } from '@radix-ui/themes'
import { isEqual } from 'lodash'
import React, { FC, useEffect, useMemo, useState } from 'react'

import useDebounce from '../../../../hooks/debouncing'
import { TaskType } from '../constants'
import { TaskBaseProps } from '../types'
import useDebounce from '../../../../../hooks/debouncing'
import { TaskType } from '../../constants'
import { EditableTaskProps } from '../../types'

export interface NumberTaskProps extends TaskBaseProps {
export interface NumberTaskProps extends EditableTaskProps {
type: TaskType.INTEGER
}

export const NumberTask: FC<NumberTaskProps> = ({ answer, onAnswerUpdate, disabled = false }) => {
export const NumberTask: FC<NumberTaskProps> = ({ answer, onAnswerUpdate }) => {
const initialValue = useMemo(() => answer?.answer ?? '', [answer])
const [value, setValue] = useState(initialValue)
const debouncedValue = useDebounce(value)
Expand All @@ -34,7 +34,6 @@ export const NumberTask: FC<NumberTaskProps> = ({ answer, onAnswerUpdate, disabl
onChange={handleChange}
type="number"
variant="soft"
disabled={disabled}
placeholder="Your number here..."
/>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@ import { MathJax } from 'better-react-mathjax'
import { isEmpty, isEqual } from 'lodash'
import { FC, useEffect, useMemo, useState } from 'react'

import { TaskType } from '../../constants'
import { TaskBaseProps } from '../../types'
import { TaskType } from '../../../constants'
import { EditableTaskProps } from '../../../types'
import { ViewOnlyCanvas } from './components/ViewOnlyCanvas'
import { ProcessedHandwritingEditor } from './editors/ProcessedHandwritingEditor'

export interface ProcessedHandwritingProps extends TaskBaseProps {
export interface ProcessedHandwritingProps extends EditableTaskProps {
type: TaskType.PROCESSED_HANDWRITING
}

export const ProcessedHandwritingTask: FC<ProcessedHandwritingProps> = ({
answer,
onAnswerUpdate,
disabled = false,
}) => {
const initialValue = useMemo(
() => (answer?.answer ? JSON.parse(answer.answer) : { latex: '' }),
Expand All @@ -35,14 +34,12 @@ export const ProcessedHandwritingTask: FC<ProcessedHandwritingProps> = ({
return (
<Dialog.Root>
<Flex gap="3" align="center">
{!disabled && (
<Dialog.Trigger>
<Button disabled={disabled} size="4">
{strokes?.length ? 'Edit answer' : 'Enter answer'}{' '}
<Pencil2Icon width="1.5rem" height="1.5rem" />
</Button>
</Dialog.Trigger>
)}
<Dialog.Trigger>
<Button size="4">
{strokes?.length ? 'Edit answer' : 'Enter answer'}{' '}
<Pencil2Icon width="1.5rem" height="1.5rem" />
</Button>
</Dialog.Trigger>
{value?.latex && (
<Card className="latex-preview">
<Flex p="3">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,16 @@ import { Button, Dialog, Flex } from '@radix-ui/themes'
import { isEmpty, isEqual } from 'lodash'
import { FC, useEffect, useMemo, useState } from 'react'

import { TaskType } from '../../constants'
import { TaskBaseProps } from '../../types'
import { TaskType } from '../../../constants'
import { EditableTaskProps } from '../../../types'
import { ViewOnlyCanvas } from './components/ViewOnlyCanvas'
import RawHandwritingEditor from './editors/RawHandwritingEditor/rawHandwritingEditor'

export interface RawHandwritingProps extends TaskBaseProps {
export interface RawHandwritingProps extends EditableTaskProps {
type: TaskType.RAW_HANDWRITING
}

export const RawHandwritingTask: FC<RawHandwritingProps> = ({
answer,
onAnswerUpdate,
disabled = false,
}) => {
export const RawHandwritingTask: FC<RawHandwritingProps> = ({ answer, onAnswerUpdate }) => {
const initialValue = useMemo(() => (answer?.answer ? JSON.parse(answer.answer) : {}), [answer])

const [value, setValue] = useState(initialValue)
Expand All @@ -29,16 +25,14 @@ export const RawHandwritingTask: FC<RawHandwritingProps> = ({

return (
<Dialog.Root>
{!disabled && (
<Flex gap="3" align="center">
<Dialog.Trigger>
<Button disabled={disabled} size="4">
{value?.raw?.elements.length ? 'Edit answer' : 'Enter answer'}{' '}
<Pencil2Icon width="1.5rem" height="1.5rem" />
</Button>
</Dialog.Trigger>
</Flex>
)}
<Flex gap="3" align="center">
<Dialog.Trigger>
<Button size="4">
{value?.raw?.elements.length ? 'Edit answer' : 'Enter answer'}{' '}
<Pencil2Icon width="1.5rem" height="1.5rem" />
</Button>
</Dialog.Trigger>
</Flex>
{!isEmpty(value?.raw?.elements) && (
<ViewOnlyCanvas initialData={value?.raw?.elements ?? []} minHeight="500px" />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import React, {
useState,
} from 'react'

import { ConfirmDialog } from '../../../../../../ConfirmDialog'
import { ConfirmDialog } from '../../../../../../../ConfirmDialog'
import { RawHandwritingAnswer } from '../../types'
import './index.scss'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { SceneData } from '@excalidraw/excalidraw/types/types'
import axios, { AxiosError } from 'axios'
import { useCallback, useEffect, useRef, useState } from 'react'

import axiosInstance from '../../../../../../api/axiosInstance'
import routes from '../../../../../../api/routes'
import axiosInstance from '../../../../../../../api/axiosInstance'
import routes from '../../../../../../../api/routes'

// Constants
/** Time to wait with user putting no stroks on the pages before API call */
Expand Down
50 changes: 50 additions & 0 deletions src/components/questionStructure/Task/readonly/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Flex } from '@radix-ui/themes'
import React, { FC } from 'react'

import Markdown from '../../../Markdown'
import { TaskType } from '../constants'
import '../styles/common.css'
import { CodeTask, CodeTaskProps } from './variants/CodeTask'
import { EssayTask, EssayTaskProps } from './variants/EssayTask'
import { FlagTask, FlagTaskProps } from './variants/FlagTask'
import { MCQMultiTask, MCQMultiTaskProps, MCQOneTask, MCQOneTaskProps } from './variants/MCQ'
import NoAnswerBanner from './variants/NoAnswerBanner'
import { NumberTask, NumberTaskProps } from './variants/NumberTask'
import {
ProcessedHandwritingProps,
ProcessedHandwritingTask,
} from './variants/handwriting/ProcessedHandwritingTask'
import { RawHandwritingProps, RawHandwritingTask } from './variants/handwriting/RawHandwritingTask'

export type TaskProps =
| FlagTaskProps
| NumberTaskProps
| EssayTaskProps
| CodeTaskProps
| MCQOneTaskProps
| MCQMultiTaskProps
| ProcessedHandwritingProps
| RawHandwritingProps

const taskComponentMap = {
[TaskType.ESSAY]: EssayTask,
[TaskType.CODE]: CodeTask,
[TaskType.INTEGER]: NumberTask,
[TaskType.FLAG]: FlagTask,
[TaskType.MULTIPLE_CHOICE_SELECT_ONE]: MCQOneTask,
[TaskType.MULTIPLE_CHOICE_SELECT_SEVERAL]: MCQMultiTask,
[TaskType.RAW_HANDWRITING]: RawHandwritingTask,
[TaskType.PROCESSED_HANDWRITING]: ProcessedHandwritingTask,
} as const

type TaskComponent = FC<TaskProps & { instructions?: string }>

export const ReadOnlyTaskFactory: TaskComponent = ({ instructions, ...taskProps }) => {
const Component = taskComponentMap[taskProps.type] as TaskComponent
return (
<Flex gap="3" direction="column">
{instructions && <Markdown>{instructions}</Markdown>}
{taskProps.answer ? <Component {...taskProps} /> : <NoAnswerBanner />}
</Flex>
)
}
Loading