move move frontend to progress-test
This commit is contained in:
222
app/javascript/pages/question/Form/Form.tsx
Normal file
222
app/javascript/pages/question/Form/Form.tsx
Normal file
@@ -0,0 +1,222 @@
|
||||
import React, {FC, useState} from 'react'
|
||||
import {useForm} from 'react-hook-form';
|
||||
import {ExclamationCircleIcon} from '@heroicons/react/outline';
|
||||
import {useHistory} from "react-router-dom";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {gql} from '@apollo/client';
|
||||
|
||||
import {Question, QuestionCreateInput, QuestionStatus} from '../../../__generated__/graphql-schema';
|
||||
import {formatInput} from '../formatInputs';
|
||||
import {validateQuestionInputs} from '../../../utils/questions/questionValidations';
|
||||
import {RootState} from '../../../services/store';
|
||||
import {FormProvider} from './FormContext'
|
||||
import {SteppedForm, Step} from './SteppedForm'
|
||||
import {
|
||||
EnunciationFormStep,
|
||||
EnunciationFragment,
|
||||
AnswerFormStep,
|
||||
AnswerFragment,
|
||||
DistractorsFormStep,
|
||||
DistractorsFragment,
|
||||
FeaturesFormStep,
|
||||
FeaturesFragment,
|
||||
} from './steps'
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
AlertV2Props,
|
||||
AlertV2,
|
||||
List,
|
||||
ListItem,
|
||||
} from '../../../components';
|
||||
import {QuestionRoutePaths} from "../../../routes";
|
||||
import {turnOff, turnOn} from "../../../services/store/unsavedChanges";
|
||||
|
||||
export const FormFragments = gql`
|
||||
${EnunciationFragment}
|
||||
${AnswerFragment}
|
||||
${DistractorsFragment}
|
||||
${FeaturesFragment}
|
||||
fragment FormFields on Question {
|
||||
...EnunciationFields
|
||||
...AnswerFields
|
||||
...DistractorsFields
|
||||
...FeaturesFields
|
||||
status
|
||||
}
|
||||
`
|
||||
|
||||
type Props = {
|
||||
question?: Question
|
||||
onSubmit?: (inputs: any) => void
|
||||
onDraftSubmit?: (inputs: any) => void
|
||||
alert?: AlertV2Props
|
||||
}
|
||||
|
||||
export const Form: FC<Props> = ({question, onSubmit, onDraftSubmit, alert}) => {
|
||||
const [validationErrors, setValidationErrors] = useState<string[]>([])
|
||||
const [confirmSaveDialogIsOpen, setConfirmFinishDialogIsOpen] = useState(false)
|
||||
const [leaveDialogIsOpen, setLeaveDialogIsOpen] = useState(false)
|
||||
const {register, control, setValue, getValues, reset, formState} = useForm()
|
||||
const [currentStep, setCurrentStep] = useState(0)
|
||||
const unsavedChanges = useSelector((state: RootState) => state.unsavedChanges)
|
||||
const history = useHistory()
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const minStep = 0
|
||||
const maxStep = 3
|
||||
const onFirstStep = currentStep === minStep
|
||||
const onLastStep = currentStep === maxStep
|
||||
|
||||
if (formState.isDirty) {
|
||||
dispatch(turnOn())
|
||||
}
|
||||
|
||||
const handleNextStep = () => {
|
||||
if (onLastStep) return
|
||||
|
||||
setCurrentStep(currentStep + 1)
|
||||
}
|
||||
|
||||
const handlePreviousStep = () => {
|
||||
if (onFirstStep) return
|
||||
|
||||
setCurrentStep(currentStep - 1)
|
||||
}
|
||||
|
||||
const getFormattedInputValues = () => formatInput(getValues())
|
||||
|
||||
const handleCancel = () => {
|
||||
if (unsavedChanges && !leaveDialogIsOpen) {
|
||||
setLeaveDialogIsOpen(true)
|
||||
} else {
|
||||
history.push(QuestionRoutePaths.index)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDraftSave = () => {
|
||||
if (onDraftSubmit) {
|
||||
onDraftSubmit({...getFormattedInputValues(), status: QuestionStatus.Draft} as QuestionCreateInput)
|
||||
reset(getValues(), {
|
||||
isDirty: false
|
||||
})
|
||||
dispatch(turnOff())
|
||||
}
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
const inputs = {...getFormattedInputValues(), status: QuestionStatus.WaitingReview} as QuestionCreateInput
|
||||
const errors = validateQuestionInputs(inputs)
|
||||
|
||||
setConfirmFinishDialogIsOpen(false)
|
||||
|
||||
if (onSubmit && !errors.length) {
|
||||
dispatch(turnOff())
|
||||
onSubmit(inputs)
|
||||
} else {
|
||||
setValidationErrors(errors)
|
||||
}
|
||||
|
||||
reset(getValues(), {
|
||||
isDirty: false
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<FormProvider props={{question, hooks: {register, control, setValue}}}>
|
||||
{alert && (
|
||||
<AlertV2 severity={alert.severity} text={alert.text}></AlertV2>
|
||||
)}
|
||||
<Dialog
|
||||
isOpen={leaveDialogIsOpen}
|
||||
setIsOpen={setLeaveDialogIsOpen}
|
||||
onConfirmation={handleCancel}
|
||||
title="Modificações não Salvas"
|
||||
text="Todas as alterações serão descartadas. Deseja continuar?"
|
||||
/>
|
||||
<Dialog
|
||||
isOpen={confirmSaveDialogIsOpen}
|
||||
setIsOpen={setConfirmFinishDialogIsOpen}
|
||||
onConfirmation={handleSave}
|
||||
title="Modificações não Salvas"
|
||||
text="Ao finalizar a questão, o revisor receberá uma notificação para revisá-la. Deseja continuar?"
|
||||
/>
|
||||
<Dialog
|
||||
isOpen={!!validationErrors.length}
|
||||
setIsOpen={() => setValidationErrors([])}
|
||||
onConfirmation={() => setValidationErrors([])}
|
||||
title="Falha de Validação"
|
||||
type="notice"
|
||||
text={
|
||||
<>
|
||||
<List>
|
||||
{validationErrors?.map((errorMessage) => (
|
||||
<ListItem
|
||||
key={errorMessage}
|
||||
icon={<ExclamationCircleIcon className="w-5 text-gray-800"/>}
|
||||
text={errorMessage}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<form className="m-auto max-w-screen-md">
|
||||
<SteppedForm
|
||||
currentStep={currentStep}
|
||||
className="mb-3"
|
||||
>
|
||||
<Step step={0}>
|
||||
<EnunciationFormStep/>
|
||||
</Step>
|
||||
<Step step={1}>
|
||||
<AnswerFormStep/>
|
||||
</Step>
|
||||
<Step step={2}>
|
||||
<DistractorsFormStep/>
|
||||
</Step>
|
||||
<Step step={3}>
|
||||
<FeaturesFormStep/>
|
||||
</Step>
|
||||
</SteppedForm>
|
||||
|
||||
<div
|
||||
className="mx-3 sm:mx-0 flex justify-items-center flex-col-reverse sm:flex-row justify-end space-x-0 sm:space-x-2">
|
||||
<Button
|
||||
className={"mb-3 sm:mb-0"}
|
||||
onClick={handleCancel}
|
||||
>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button
|
||||
className={`mb-3 sm:mb-0 ${onFirstStep ? "hidden" : ""}`}
|
||||
onClick={handlePreviousStep}
|
||||
>
|
||||
Retornar
|
||||
</Button>
|
||||
{(question?.status === QuestionStatus.Draft || question?.status === undefined) &&
|
||||
<Button className={"mb-3 sm:mb-0"} onClick={handleDraftSave}>
|
||||
Salvar Rascunho
|
||||
</Button>
|
||||
}
|
||||
<Button
|
||||
type="primary"
|
||||
className={`mb-3 sm:mb-0 ${onLastStep ? "hidden" : ""}`}
|
||||
onClick={handleNextStep}
|
||||
>
|
||||
Prosseguir
|
||||
</Button>
|
||||
{onLastStep &&
|
||||
<Button
|
||||
type="primary"
|
||||
className="mb-3 sm:mb-0"
|
||||
onClick={handleSave}
|
||||
>
|
||||
Finalizar
|
||||
</Button>
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
</FormProvider>
|
||||
)
|
||||
}
|
||||
39
app/javascript/pages/question/Form/FormContext.tsx
Normal file
39
app/javascript/pages/question/Form/FormContext.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import React, { FC, useContext } from 'react'
|
||||
import { Control, FieldValues } from 'react-hook-form';
|
||||
import { Question } from '../../../__generated__/graphql-schema';
|
||||
|
||||
type FormContextHooks = {
|
||||
register: any
|
||||
setValue: Function
|
||||
control: Control<FieldValues>
|
||||
}
|
||||
|
||||
type FormContextProps = {
|
||||
hooks: FormContextHooks
|
||||
question?: Question
|
||||
}
|
||||
|
||||
const FormContext = React.createContext<FormContextProps | null>(null);
|
||||
|
||||
export const useFormProvider = () => {
|
||||
const context = useContext(FormContext)
|
||||
|
||||
if (context === null) {
|
||||
throw new Error('You probably forgot to put <FormProvider>.')
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
type Props = {
|
||||
children?: any
|
||||
props: FormContextProps
|
||||
}
|
||||
|
||||
export const FormProvider: FC<Props> = ({ children, props }) => {
|
||||
return (
|
||||
<FormContext.Provider value={props}>
|
||||
{children}
|
||||
</FormContext.Provider>
|
||||
)
|
||||
}
|
||||
34
app/javascript/pages/question/Form/SteppedForm.tsx
Normal file
34
app/javascript/pages/question/Form/SteppedForm.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React, { FC } from "react";
|
||||
|
||||
type StepProps = {
|
||||
children: any
|
||||
step: number
|
||||
}
|
||||
|
||||
export const Step: FC<StepProps> = ({ children }) => (children);
|
||||
|
||||
type Props = {
|
||||
children: any;
|
||||
currentStep: number;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const SteppedForm: FC<Props> = ({
|
||||
children,
|
||||
currentStep,
|
||||
className = '',
|
||||
}) => {
|
||||
return (
|
||||
<div className={className}>
|
||||
{children?.map((x: any) => {
|
||||
const visible = x.props.step === currentStep;
|
||||
|
||||
return (
|
||||
<div key={x.props.step} className={visible ? "" : "hidden"}>
|
||||
{x}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
55
app/javascript/pages/question/Form/components/TextEditor.tsx
Normal file
55
app/javascript/pages/question/Form/components/TextEditor.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import React, { FC } from "react";
|
||||
import { Controller } from "react-hook-form";
|
||||
import CKEditor from "@ckeditor/ckeditor5-react";
|
||||
import * as ClassicEditor from "ckeditor5-mathtype/build/ckeditor";
|
||||
|
||||
import { useFormProvider } from '../FormContext'
|
||||
|
||||
const toolbarOptions = [
|
||||
"bold",
|
||||
"italic",
|
||||
"blockQuote",
|
||||
"numberedList",
|
||||
"bulletedList",
|
||||
"imageUpload",
|
||||
"insertTable",
|
||||
"tableColumn",
|
||||
"tableRow",
|
||||
"mergeTableCells",
|
||||
"|",
|
||||
"MathType",
|
||||
"ChemType",
|
||||
"|",
|
||||
"undo",
|
||||
"redo",
|
||||
];
|
||||
|
||||
type Props = {
|
||||
name: string
|
||||
defaultValue: string
|
||||
}
|
||||
|
||||
export const TextEditor: FC<Props> = ({ name, defaultValue }) => {
|
||||
const { hooks: { control } } = useFormProvider()
|
||||
|
||||
return (
|
||||
<Controller
|
||||
control={control}
|
||||
name={name}
|
||||
defaultValue={defaultValue}
|
||||
render={({ onChange, value }) => (
|
||||
<CKEditor
|
||||
editor={ClassicEditor}
|
||||
data={value}
|
||||
config={{
|
||||
toolbar: toolbarOptions,
|
||||
ckfinder: {
|
||||
uploadUrl: `${process.env.REACT_APP_BACKEND_URL}/uploads`,
|
||||
},
|
||||
}}
|
||||
onChange={(_: any, editor: any) => onChange(editor.getData())}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
1
app/javascript/pages/question/Form/index.ts
Normal file
1
app/javascript/pages/question/Form/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./Form";
|
||||
61
app/javascript/pages/question/Form/steps/AnswerFormStep.tsx
Normal file
61
app/javascript/pages/question/Form/steps/AnswerFormStep.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { gql } from "@apollo/client";
|
||||
import React, { FC } from "react";
|
||||
import { Controller } from "react-hook-form";
|
||||
|
||||
import { Card } from "../../../../components/Card/Card";
|
||||
import { TextEditor } from "../components/TextEditor";
|
||||
import { useFormProvider } from '../FormContext'
|
||||
|
||||
export const AnswerFragment = gql`
|
||||
fragment AnswerFields on Question {
|
||||
alternatives {
|
||||
correct
|
||||
text
|
||||
}
|
||||
explanation
|
||||
references
|
||||
}
|
||||
`
|
||||
|
||||
export const AnswerFormStep: FC = () => {
|
||||
const { question, hooks: { control } } = useFormProvider()
|
||||
|
||||
const alternativesMaped = question?.alternatives || [
|
||||
{ text: "", correct: true },
|
||||
];
|
||||
|
||||
const correctAlternative = alternativesMaped.find(
|
||||
(alternative) => alternative.correct === true,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card title="Resposta Correta" className="mb-3">
|
||||
<div className="flex flex-col">
|
||||
<div className="w-full">
|
||||
<TextEditor
|
||||
name={`alternatives[0].text`}
|
||||
defaultValue={correctAlternative?.text ?? ''}
|
||||
/>
|
||||
<Controller
|
||||
name={`alternatives[0].correct`}
|
||||
control={control}
|
||||
defaultValue={true}
|
||||
render={() => (<></>)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col w-full border border-gray-300 rounded p-4 mt-4 shadow-sm">
|
||||
<div>
|
||||
<h2 className="text-xl font-medium">Explicação</h2>
|
||||
<TextEditor name="explanation" defaultValue={question?.explanation ?? ''} />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-medium">Referências</h2>
|
||||
<TextEditor defaultValue={question?.references ?? ''} name="references" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
import React, { FC } from "react";
|
||||
import { Controller } from "react-hook-form";
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
import { Card } from "../../../../components";
|
||||
import { TextEditor } from "../components/TextEditor";
|
||||
import { useFormProvider } from '../FormContext'
|
||||
|
||||
export const DistractorsFragment = gql`
|
||||
fragment DistractorsFields on Question {
|
||||
alternatives {
|
||||
correct
|
||||
text
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const DistractorsFormStep: FC = () => {
|
||||
const { question, hooks: { control } } = useFormProvider()
|
||||
|
||||
const incorrectAnswers = question?.alternatives?.filter(
|
||||
(alternative) => alternative.correct === false,
|
||||
) || [
|
||||
{ text: "", correct: false },
|
||||
{ text: "", correct: false },
|
||||
{ text: "", correct: false },
|
||||
{ text: "", correct: false },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card title="Distratores">
|
||||
<div className="flex flex-col">
|
||||
<div className="">
|
||||
{incorrectAnswers.map(({ text }, index) => (
|
||||
<div className="w-full mb-3" key={index}>
|
||||
<TextEditor
|
||||
name={`alternatives[${index + 1}].text`}
|
||||
defaultValue={text ?? ""}
|
||||
/>
|
||||
<Controller
|
||||
name={`alternatives[${index + 1}].correct`}
|
||||
control={control}
|
||||
defaultValue={false}
|
||||
render={() => (<></>)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import { gql } from "@apollo/client";
|
||||
import React, { FC } from "react";
|
||||
|
||||
import { Card } from "../../../../components";
|
||||
import { TextEditor } from '../components/TextEditor'
|
||||
import { useFormProvider } from '../FormContext'
|
||||
|
||||
export const EnunciationFragment = gql`
|
||||
fragment EnunciationFields on Question {
|
||||
instruction
|
||||
support
|
||||
body
|
||||
}
|
||||
`
|
||||
|
||||
export const EnunciationFormStep: FC = () => {
|
||||
const { question } = useFormProvider()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="h-full mb-3" title="Instrução (opcional)">
|
||||
<TextEditor name="instruction" defaultValue={question?.instruction ?? ""} />
|
||||
</Card>
|
||||
<Card className="h-full mb-3" title="Suporte (opcional)">
|
||||
<TextEditor name="support" defaultValue={question?.support ?? ""} />
|
||||
</Card>
|
||||
<Card className="h-full mb-3" title="Enunciado">
|
||||
<TextEditor name="body" defaultValue={question?.body ?? ""} />
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,192 @@
|
||||
import React, { FC, useState } from "react";
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
import { Card } from "../../../../../components";
|
||||
import { SubjectSelect, SubjectFragment } from "./SubjectSelect";
|
||||
import { ReviewerSelect, ReviewerFragment } from "./ReviewSelect";
|
||||
import { useFormProvider } from '../../FormContext'
|
||||
|
||||
import { BLOOM_TAXONOMY, CHECK_TYPE, DIFFICULTY } from "../../../../../utils/types";
|
||||
import { Question } from "../../../../../__generated__/graphql-schema";
|
||||
|
||||
export const FeaturesFragment = gql`
|
||||
${ReviewerFragment}
|
||||
${SubjectFragment}
|
||||
fragment FeaturesFields on Question {
|
||||
... ReviewerFields
|
||||
... SubjectFields
|
||||
authorship
|
||||
authorshipYear
|
||||
difficulty
|
||||
checkType
|
||||
intention
|
||||
bloomTaxonomy
|
||||
}
|
||||
`
|
||||
|
||||
export const FeaturesFormStep: FC = () => {
|
||||
const { question, hooks: { setValue, register } } = useFormProvider();
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
const {
|
||||
authorship,
|
||||
authorshipYear,
|
||||
difficulty,
|
||||
bloomTaxonomy,
|
||||
checkType,
|
||||
} = question || {} as Question
|
||||
|
||||
const [ownQuestion, setOwnQuestion] = useState<boolean>(authorship === "UNIFESO" || authorship === undefined || authorship === null);
|
||||
|
||||
const handleOwnCheck = (value: string) => {
|
||||
if (value === 'UNIFESO') {
|
||||
setOwnQuestion(true)
|
||||
setValue("authorship", "UNIFESO");
|
||||
setValue("authorshipYear", currentYear.toString());
|
||||
} else {
|
||||
setOwnQuestion(false)
|
||||
setValue("authorship", "");
|
||||
setValue("authorshipYear", "");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card title="Características">
|
||||
<div className="grid grid-cols-2 col-gap-2">
|
||||
<div className="flex">
|
||||
<label htmlFor="own" className="mr-3 my-auto">
|
||||
Autoria
|
||||
</label>
|
||||
<div className="my-auto">
|
||||
<input
|
||||
className="my-auto"
|
||||
type="radio"
|
||||
id="authorship-own"
|
||||
checked={!!ownQuestion}
|
||||
ref={register}
|
||||
onChange={() => handleOwnCheck("UNIFESO")}
|
||||
name="__nonused"
|
||||
/>
|
||||
<label htmlFor="authorship-own" className="ml-1">Própria</label>
|
||||
</div>
|
||||
<div className="my-auto ml-3">
|
||||
<input
|
||||
className="my-auto"
|
||||
type="radio"
|
||||
id="authorship-third"
|
||||
checked={!ownQuestion}
|
||||
ref={register}
|
||||
onChange={() => handleOwnCheck("")}
|
||||
name="__nonused"
|
||||
/>
|
||||
<label htmlFor="authorship-third" className="ml-1">Outro</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="flex">
|
||||
<h2 className="pr-2 my-auto">Fonte</h2>
|
||||
<div className="w-full">
|
||||
<div style={{ maxWidth: "194px" }}>
|
||||
<input
|
||||
className="block rounded p-1 w-full border-gray-400 border shadow-sm"
|
||||
ref={register}
|
||||
name="authorship"
|
||||
defaultValue={authorship || (ownQuestion ? "UNIFESO" : "")}
|
||||
readOnly={!!ownQuestion}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<h2 className="pr-2 pl-3 my-auto">Ano</h2>
|
||||
<div style={{ maxWidth: "62px" }}>
|
||||
<input
|
||||
className="w-full rounded p-1 border-gray-400 border shadow-sm"
|
||||
ref={register}
|
||||
type="number"
|
||||
min="1999"
|
||||
max={currentYear}
|
||||
step="1"
|
||||
name="authorshipYear"
|
||||
defaultValue={authorshipYear ?? new Date().getFullYear().toString()}
|
||||
readOnly={!!ownQuestion}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 col-gap-2 mt-3">
|
||||
<div className="w-full grid grid-cols-1 row-gap-4">
|
||||
<div className="flex flex-col">
|
||||
<h2>Grau de Dificuldade</h2>
|
||||
<select
|
||||
ref={register}
|
||||
className="w-full rounded p-1 border-gray-400 border shadow-sm"
|
||||
name="difficulty"
|
||||
defaultValue={difficulty ?? ""}
|
||||
>
|
||||
<option />
|
||||
{DIFFICULTY.map((item, index) => (
|
||||
<option key={index} value={item.value}>
|
||||
{item.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<h2>Tipo</h2>
|
||||
<select
|
||||
ref={register}
|
||||
className="w-full rounded p-1 border-gray-400 border shadow-sm"
|
||||
name="checkType"
|
||||
defaultValue={checkType ?? ""}
|
||||
>
|
||||
<option />
|
||||
{CHECK_TYPE.map((item, index) => (
|
||||
<option key={index} value={item.value}>
|
||||
{item.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<h2>Habilidade Cognitiva</h2>
|
||||
<select
|
||||
ref={register}
|
||||
className="w-full rounded p-1 border-gray-400 border shadow-sm"
|
||||
name="bloomTaxonomy"
|
||||
defaultValue={bloomTaxonomy ?? ""}
|
||||
>
|
||||
<option />
|
||||
{BLOOM_TAXONOMY.map((item, index) => (
|
||||
<option key={index} value={item.value}>
|
||||
{item.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<SubjectSelect />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col mt-4">
|
||||
<h2>Intenção</h2>
|
||||
<textarea
|
||||
className="block rounded p-1 w-full border-gray-400 border shadow-sm"
|
||||
ref={register}
|
||||
name="intention"
|
||||
defaultValue={question?.intention ?? ""}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col mt-4">
|
||||
<h2>Revisor</h2>
|
||||
<ReviewerSelect />
|
||||
</div>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
import React, { FC } from "react";
|
||||
import { gql, useQuery } from "@apollo/client";
|
||||
|
||||
import { useFormProvider } from '../../FormContext'
|
||||
import { Query, QuestionStatus, User } from "../../../../../__generated__/graphql-schema";
|
||||
|
||||
export const ReviewerFragment = gql`
|
||||
fragment ReviewerFields on Question {
|
||||
reviewer {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const REVIEWERS_QUERY = gql`
|
||||
query ReviwersQuery {
|
||||
reviewers {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
email
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
type Props = {
|
||||
reviewer?: User
|
||||
}
|
||||
|
||||
export const ReviewerSelect: FC<Props> = () => {
|
||||
const { question, hooks: { register } } = useFormProvider()
|
||||
const { loading, data } = useQuery<Query>(REVIEWERS_QUERY);
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
const reviewers = data?.reviewers.nodes
|
||||
|
||||
return (
|
||||
<select
|
||||
ref={register}
|
||||
className="w-full rounded p-1 border-gray-400 border shadow-sm"
|
||||
name="reviewerUserId"
|
||||
defaultValue={question?.reviewer?.id}
|
||||
>
|
||||
{(question?.status === undefined || question?.status === QuestionStatus.Draft) && <option />}
|
||||
{reviewers?.map((review, index) => (
|
||||
<option key={index} value={review?.id}>
|
||||
{review?.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
import React, { FC, useState } from "react";
|
||||
import { useQuery, gql } from "@apollo/client";
|
||||
|
||||
import { Query } from "../../../../../__generated__/graphql-schema";
|
||||
import { useFormProvider } from '../../FormContext'
|
||||
|
||||
type Props = {
|
||||
subjectId?: string
|
||||
}
|
||||
|
||||
export const SubjectFragment = gql`
|
||||
fragment SubjectFields on Question {
|
||||
subject {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const SUBJECTS_QUERY = gql`
|
||||
query SubjectQuery {
|
||||
subjects {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
axis {
|
||||
name
|
||||
}
|
||||
category {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const SubjectSelect: FC<Props> = () => {
|
||||
const { question, hooks: { register } } = useFormProvider()
|
||||
const [selectedId, setSelectedId] = useState(question?.subject?.id);
|
||||
|
||||
const { loading, data } = useQuery<Query>(SUBJECTS_QUERY);
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
const subjects = data?.subjects.nodes
|
||||
|
||||
const selectedSubject = data?.subjects.nodes?.find((subject) => subject?.id === selectedId);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div>
|
||||
<h2>Assunto</h2>
|
||||
<select
|
||||
ref={register}
|
||||
className="w-full rounded p-1 border-gray-400 border shadow-sm"
|
||||
name="subjectId"
|
||||
defaultValue={question?.subject?.id ?? ""}
|
||||
onChange={(e) => setSelectedId(e.target.value)}
|
||||
>
|
||||
<option value="" />
|
||||
{subjects?.map((subject) => (
|
||||
<option
|
||||
key={`${subject?.name}-${subject?.id}`}
|
||||
value={subject?.id}
|
||||
>
|
||||
{subject?.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<span className="mt-4">
|
||||
Eixo de Formação
|
||||
<input
|
||||
className="block rounded p-1 w-full border-gray-400 border shadow-sm"
|
||||
disabled
|
||||
value={selectedSubject?.axis.name}
|
||||
/>
|
||||
</span>
|
||||
<span className="mt-4">
|
||||
Categoria
|
||||
<input
|
||||
className="block rounded p-1 w-full border-gray-400 border shadow-sm"
|
||||
disabled
|
||||
value={selectedSubject?.category.name}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./FeaturesFormStep";
|
||||
4
app/javascript/pages/question/Form/steps/index.ts
Normal file
4
app/javascript/pages/question/Form/steps/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from "./EnunciationFormStep";
|
||||
export * from "./AnswerFormStep";
|
||||
export * from "./DistractoresFormStep";
|
||||
export * from "./FeaturesFromStep";
|
||||
Reference in New Issue
Block a user