move move frontend to progress-test

This commit is contained in:
João Geonizeli
2022-07-21 21:16:59 -03:00
parent f8d5d08447
commit 386050d4ad
129 changed files with 159374 additions and 39 deletions

View File

@@ -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>
)
};

View File

@@ -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>
</>
)
};

View File

@@ -0,0 +1 @@
export * from './ReviewMessages'

View 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>
);
};

View File

@@ -0,0 +1,2 @@
export * from "./ViewMode";
export * from "./ReviewMessages";