move move frontend to progress-test
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
import { ApolloQueryResult, gql, OperationVariables } from "@apollo/client";
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
DocumentRemoveIcon
|
||||
} from '@heroicons/react/outline';
|
||||
import React, { FC } from "react";
|
||||
import { Card } from "../../../../components";
|
||||
import { Query, Question, ReviewMessage, ReviewMessageFeedbackType } from "../../../../__generated__/graphql-schema";
|
||||
import { ReviewMessageForm, ReviewMessageFormFragments } from "./ReviewMessagesForm";
|
||||
|
||||
|
||||
const feedbackIcon = {
|
||||
[ReviewMessageFeedbackType.Answer]: null,
|
||||
[ReviewMessageFeedbackType.Approve]: <CheckCircleIcon className="w-5 text-green-800" />,
|
||||
[ReviewMessageFeedbackType.RequestChanges]: <DocumentRemoveIcon className="w-5 text-red-800" />,
|
||||
};
|
||||
|
||||
const ReviewMessageTitle: FC<{
|
||||
feedback: ReviewMessage
|
||||
}> = ({ feedback }) => (
|
||||
<p className="flex">
|
||||
{feedback.user.name}{' '} - {' '}
|
||||
<span className="text-gray-700 pr-2">
|
||||
{new Date(feedback.createdAt).toLocaleString()}
|
||||
</span>
|
||||
{feedbackIcon[feedback.feedbackType]}
|
||||
</p>
|
||||
)
|
||||
|
||||
export const ReviewMessagesFragments = gql`
|
||||
${ReviewMessageFormFragments}
|
||||
fragment ReviewMessages_question on Question {
|
||||
id
|
||||
...ReviewMessageForm_question
|
||||
user {
|
||||
id
|
||||
}
|
||||
reviewMessages {
|
||||
nodes {
|
||||
id
|
||||
feedbackType
|
||||
text
|
||||
user {
|
||||
name
|
||||
avatarUrl
|
||||
}
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const ReviewMessages: FC<{
|
||||
question: Question
|
||||
refetch: (variables?: Partial<OperationVariables> | undefined) => Promise<ApolloQueryResult<Query>>
|
||||
}> = ({ question, refetch }) => {
|
||||
const reviewMessages = question.reviewMessages.nodes
|
||||
const hasFeebacks = !!reviewMessages.length
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card className="mb-3" title="Histórico de Pareceres">
|
||||
{hasFeebacks
|
||||
? reviewMessages.map((item) => (
|
||||
<div key={item.id}>
|
||||
<ReviewMessageTitle feedback={item} />
|
||||
<p className="p-2">
|
||||
{item.text}
|
||||
</p>
|
||||
</div>
|
||||
))
|
||||
: 'Essa questão não tem nenhum parecer ainda.'}
|
||||
</Card>
|
||||
<ReviewMessageForm question={question} refetch={refetch} />
|
||||
</div>
|
||||
)
|
||||
};
|
||||
@@ -0,0 +1,139 @@
|
||||
import { ApolloQueryResult, gql, OperationVariables, useMutation } from "@apollo/client";
|
||||
import React, { FC, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Prompt, useHistory } from "react-router";
|
||||
import { Button, Card } from "../../../../components";
|
||||
import { useCurrentUser } from "../../../../contexts";
|
||||
import { NodeId } from "../../../../utils/graphql";
|
||||
import { Mutation, Query, Question, ReviewMessageFeedbackType } from "../../../../__generated__/graphql-schema";
|
||||
|
||||
export const REVIEW_FEEDBACK = [
|
||||
{
|
||||
label: "Aprovada",
|
||||
description: "O revisor sugere que as observações enviadas no parecer sejam consideradas.",
|
||||
value: ReviewMessageFeedbackType.Approve,
|
||||
},
|
||||
{
|
||||
label: "Pendente de Alterações",
|
||||
description: "O autor deve efetuar as alterações solicitadas no parecer e reenviar a questão ao revisor.",
|
||||
value: ReviewMessageFeedbackType.RequestChanges,
|
||||
},
|
||||
];
|
||||
|
||||
export const ReviewMessageFormFragments = gql`
|
||||
fragment ReviewMessageForm_question on Question {
|
||||
id
|
||||
status
|
||||
user {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const CREATE_REVIEW_MESSAGE_MUTATION = gql`
|
||||
mutation($questionId: ID!, $feedbackType: ReviewMessageFeedbackType!, $text: String!) {
|
||||
createReviewMessage(
|
||||
input: {
|
||||
message: {
|
||||
questionId: $questionId
|
||||
feedbackType: $feedbackType
|
||||
text: $text
|
||||
}
|
||||
}
|
||||
) {
|
||||
reviewMessage {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const ReviewMessageForm: FC<{
|
||||
question: Question
|
||||
refetch: (variables?: Partial<OperationVariables> | undefined) => Promise<ApolloQueryResult<Query>>
|
||||
}> = ({ question, refetch }) => {
|
||||
const [isChangesSaved, setIsChangesSaved] = useState(true)
|
||||
const { register, handleSubmit } = useForm()
|
||||
const history = useHistory();
|
||||
const { user } = useCurrentUser()
|
||||
|
||||
const [createReviewMessage] = useMutation<Mutation['createReviewMessage']>(CREATE_REVIEW_MESSAGE_MUTATION)
|
||||
|
||||
const hasFeebacks = !!question.reviewMessages.nodes.length
|
||||
const questionIsFromCurrentUser = user?.id === question.user.id
|
||||
|
||||
const handleTextChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
if (e.target.value !== '') {
|
||||
setIsChangesSaved(false)
|
||||
} else {
|
||||
setIsChangesSaved(true)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmitClick = () => {
|
||||
setIsChangesSaved(true)
|
||||
}
|
||||
|
||||
const formSubmit = async (inputs: {
|
||||
feedbackType: ReviewMessageFeedbackType
|
||||
text: string
|
||||
}) => {
|
||||
await createReviewMessage({
|
||||
variables: {
|
||||
text: inputs.text,
|
||||
feedbackType: questionIsFromCurrentUser ? ReviewMessageFeedbackType.Answer : inputs.feedbackType,
|
||||
questionId: NodeId.decode(question.id).id,
|
||||
},
|
||||
});
|
||||
|
||||
await refetch()
|
||||
|
||||
history.push('/questions')
|
||||
};
|
||||
|
||||
if (!hasFeebacks && questionIsFromCurrentUser) return null
|
||||
|
||||
return (
|
||||
<>
|
||||
<Prompt
|
||||
when={!isChangesSaved}
|
||||
message='O parecer ainda não foi enviado, deseja continuar?'
|
||||
/>
|
||||
<Card title="Parecer" className="max-w-screen-md mx-auto">
|
||||
<form onSubmit={handleSubmit(formSubmit)}>
|
||||
<textarea
|
||||
onChange={(e) => handleTextChange(e)}
|
||||
className="w-full h-32 p-2 border-solid border-2 border-gray-700 rounded-md"
|
||||
ref={register}
|
||||
name="text"
|
||||
/>
|
||||
{!questionIsFromCurrentUser && REVIEW_FEEDBACK.map((item, index) => (
|
||||
<div key={index} className="flex mb-2">
|
||||
<input
|
||||
type="radio"
|
||||
id={item.value}
|
||||
name="feedbackType"
|
||||
ref={register({ required: true })}
|
||||
value={item.value}
|
||||
className="my-auto"
|
||||
defaultChecked={index === 0}
|
||||
/>
|
||||
<label
|
||||
htmlFor={item.value}
|
||||
className="flex flex-col pl-2 w-full"
|
||||
>
|
||||
{item.label}
|
||||
<p className="text-gray-700 text-sm">{item.description}</p>
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
<div className="justify-end flex">
|
||||
<Button type="primary" htmlType="submit" className="mt-4" onClick={handleSubmitClick}>
|
||||
{questionIsFromCurrentUser ? 'Responder Parecer' : 'Enviar Parecer'}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './ReviewMessages'
|
||||
171
app/javascript/pages/question/shared/ViewMode.tsx
Normal file
171
app/javascript/pages/question/shared/ViewMode.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
import React, { FC } from "react";
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
import { Card } from "../../../components";
|
||||
import { Question } from "../../../__generated__/graphql-schema";
|
||||
import { loadWIRISplugin } from "../../../utils/plugins";
|
||||
import { BLOOM_TAXONOMY, DIFFICULTY } from "../../../utils/types";
|
||||
|
||||
export const ViewModeFragments = gql`
|
||||
fragment QuestionReadOnlyFields on Question {
|
||||
intention
|
||||
instruction
|
||||
support
|
||||
body
|
||||
alternatives {
|
||||
correct
|
||||
text
|
||||
}
|
||||
explanation
|
||||
references
|
||||
authorship
|
||||
authorshipYear
|
||||
difficulty
|
||||
checkType
|
||||
bloomTaxonomy
|
||||
subject {
|
||||
name
|
||||
axis {
|
||||
name
|
||||
}
|
||||
category {
|
||||
name
|
||||
}
|
||||
}
|
||||
status
|
||||
reviewer {
|
||||
id
|
||||
name
|
||||
}
|
||||
updatedAt
|
||||
}
|
||||
`
|
||||
|
||||
type Props = {
|
||||
questionData?: Question
|
||||
}
|
||||
|
||||
export const ViewMode: FC<Props> = ({ questionData: question }) => {
|
||||
if (!question) return null;
|
||||
|
||||
const { alternatives } = question;
|
||||
|
||||
const correctAlternative = alternatives?.find(
|
||||
(alternative) => alternative.correct === true,
|
||||
);
|
||||
|
||||
const incorrectAnswers = alternatives?.filter(
|
||||
(alternative) => alternative.correct === false,
|
||||
);
|
||||
|
||||
function formatDate(stringDate: string) {
|
||||
return new Date(stringDate).toLocaleDateString();
|
||||
}
|
||||
|
||||
const { instruction, support, body } = question;
|
||||
|
||||
const difficulty = DIFFICULTY.find((item) => question.difficulty === item.value)?.label
|
||||
const bloomTaxonomy = BLOOM_TAXONOMY.find((item) => question.bloomTaxonomy === item.value)?.label
|
||||
|
||||
loadWIRISplugin()
|
||||
|
||||
return (
|
||||
<div className="max-w-screen-lg">
|
||||
<Card className="mb-3" title="Características">
|
||||
<div className="grid grid-cols-2">
|
||||
<div>
|
||||
<span className="text-gray-700">Grau de Dificuldade: </span>
|
||||
{difficulty ?? ''}
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-700">Habilidade Cognitiva: </span>
|
||||
{bloomTaxonomy ?? ''}
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-700">Ano: </span>
|
||||
{question.authorshipYear}
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-700">Autoria: </span>
|
||||
{question.authorship === "UNIFESO" ? "Própria" : `Terceiro - ${question.authorship}`}
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-700">Atualizada em: </span>
|
||||
{formatDate(question.updatedAt)}
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-700">Assunto: </span>
|
||||
{question.subject?.name}
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-700">Categoria: </span>
|
||||
{question.subject?.category?.name}
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-700">Revisor: </span>
|
||||
{question.reviewer?.name}
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-700">Eixo de Formação: </span>
|
||||
{question.subject?.axis?.name}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
{!!question.intention?.length && (
|
||||
<Card className="mb-3" title="Intenção">
|
||||
<div className="ck-content" dangerouslySetInnerHTML={{ __html: question.intention }} />
|
||||
</Card>
|
||||
)}
|
||||
{instruction && (
|
||||
<Card className="mb-3" title="Instrução">
|
||||
<div className="ck-content" dangerouslySetInnerHTML={{ __html: instruction }} />
|
||||
</Card>
|
||||
)}
|
||||
{support && (
|
||||
<Card className="mb-3" title="Suporte">
|
||||
<div className="ck-content" dangerouslySetInnerHTML={{ __html: support }} />
|
||||
</Card>
|
||||
)}
|
||||
{body && (
|
||||
<Card className="mb-3" title="Enunciado">
|
||||
<div className="ck-content" dangerouslySetInnerHTML={{ __html: body }} />
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Card className="mb-3" title="Resposta Correta">
|
||||
<div className="ck-content" dangerouslySetInnerHTML={{ __html: correctAlternative?.text ?? '' }} />
|
||||
|
||||
<div className="flex flex-col w-full border border-gray-300 rounded p-4 mt-4 shadow-sm">
|
||||
<div>
|
||||
<h2 className="text-base font-medium mb-3">Explicação</h2>
|
||||
<div
|
||||
className="ck-content ml-2"
|
||||
dangerouslySetInnerHTML={{ __html: question.explanation ?? '' }}
|
||||
/>
|
||||
</div>
|
||||
<div className="bg-gray-400 w-full my-3" style={{ height: "1px" }} />
|
||||
<div>
|
||||
<h2 className="text-base font-medium mb-3">Referências</h2>
|
||||
<div
|
||||
className="ck-content ml-2"
|
||||
dangerouslySetInnerHTML={{ __html: question.references ?? '' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="mb-3" title="Distratores">
|
||||
{incorrectAnswers?.map(({ text }, index) => (
|
||||
<div key={`question-alternative-${index}`}>
|
||||
{index !== 0 && (
|
||||
<div
|
||||
className="bg-gray-400 w-full my-3"
|
||||
style={{ height: "1px" }}
|
||||
/>
|
||||
)}
|
||||
<div className="ck-content" dangerouslySetInnerHTML={{ __html: text ?? '' }} />
|
||||
</div>
|
||||
))}
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
2
app/javascript/pages/question/shared/index.ts
Normal file
2
app/javascript/pages/question/shared/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./ViewMode";
|
||||
export * from "./ReviewMessages";
|
||||
Reference in New Issue
Block a user