Compare commits
13 Commits
264d126414
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65a3180599 | ||
|
|
45b401eda3 | ||
|
|
5899d2ad1f | ||
| 1edd26bf1b | |||
|
|
ce7d38e95b | ||
|
|
230a59e999 | ||
|
|
147fb997d0 | ||
|
|
3d946d03ff | ||
|
|
a28b21f44e | ||
|
|
8e0111b8ea | ||
|
|
155bbddbd7 | ||
|
|
f8ff95d188 | ||
|
|
2a1883dd8e |
@@ -7,12 +7,14 @@ import { AssessmentRoutePaths, DashboardRoutePaths, QuestionRoutePaths } from ".
|
|||||||
import { RootState } from "../../services/store";
|
import { RootState } from "../../services/store";
|
||||||
import { turnOff } from "../../services/store/unsavedChanges";
|
import { turnOff } from "../../services/store/unsavedChanges";
|
||||||
import { Dialog } from '../Dialog';
|
import { Dialog } from '../Dialog';
|
||||||
|
import { useCurrentUser } from "../../contexts";
|
||||||
|
|
||||||
export const AppbarTabs = () => {
|
export const AppbarTabs = () => {
|
||||||
const unsavedChanges = useSelector((state: RootState) => state.unsavedChanges)
|
const unsavedChanges = useSelector((state: RootState) => state.unsavedChanges)
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
|
const { isOnlyTeacher } = useCurrentUser()
|
||||||
|
|
||||||
const [newPath, setNewPath] = useState<string>()
|
const [newPath, setNewPath] = useState<string>()
|
||||||
|
|
||||||
@@ -45,14 +47,16 @@ export const AppbarTabs = () => {
|
|||||||
tabel: 'Questões',
|
tabel: 'Questões',
|
||||||
pathname: QuestionRoutePaths.index,
|
pathname: QuestionRoutePaths.index,
|
||||||
isCurrent: location.pathname.includes('question'),
|
isCurrent: location.pathname.includes('question'),
|
||||||
},
|
}]
|
||||||
{
|
|
||||||
icon: <DocumentIcon className="w-6" />,
|
if (!isOnlyTeacher) {
|
||||||
tabel: 'Avaliações',
|
links.push({
|
||||||
pathname: AssessmentRoutePaths.index,
|
icon: <DocumentIcon className="w-6" />,
|
||||||
isCurrent: false,
|
tabel: 'Avaliações',
|
||||||
|
pathname: AssessmentRoutePaths.index,
|
||||||
|
isCurrent: false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Controller, useForm } from "react-hook-form";
|
|||||||
|
|
||||||
import { Axis, Query } from "../../__generated__/graphql-schema";
|
import { Axis, Query } from "../../__generated__/graphql-schema";
|
||||||
import { Button, Card, Input, Navigator } from '../../components';
|
import { Button, Card, Input, Navigator } from '../../components';
|
||||||
|
import { useHistory } from "react-router";
|
||||||
|
|
||||||
type NewAssessementForm = {
|
type NewAssessementForm = {
|
||||||
axisWeights: Record<string, any>
|
axisWeights: Record<string, any>
|
||||||
@@ -47,6 +48,8 @@ export const NewAssessement = () => {
|
|||||||
const notSelectedAxis: Axis[] = axes.filter((axis) => !subjectsIds.includes(axis.id))
|
const notSelectedAxis: Axis[] = axes.filter((axis) => !subjectsIds.includes(axis.id))
|
||||||
const selectedAxis: Axis[] = axes.filter((axis) => subjectsIds.includes(axis.id))
|
const selectedAxis: Axis[] = axes.filter((axis) => subjectsIds.includes(axis.id))
|
||||||
|
|
||||||
|
const navigate = useHistory()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Navigator home />
|
<Navigator home />
|
||||||
@@ -179,9 +182,14 @@ export const NewAssessement = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<Button type="primary" className="ml-auto mr-6 mt-6">
|
<div className="flex justify-end mr-6 gap-4">
|
||||||
Gerar
|
<Button type="primary" className="mt-6">
|
||||||
</Button>
|
Gerar Automaticamente
|
||||||
|
</Button>
|
||||||
|
<Button type="primary" className="mt-6" onClick={() => navigate.push('new-manual')}>
|
||||||
|
Gerar Manualmente
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,98 +1,84 @@
|
|||||||
import { gql, useQuery } from "@apollo/client";
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Navigator } from '../../components';
|
||||||
|
|
||||||
import { Axis, Query } from "../../__generated__/graphql-schema";
|
|
||||||
import { Button, Card, Input, Navigator } from '../../components';
|
|
||||||
import { SideBar } from "./components/SideBar";
|
|
||||||
import { QuestionCard } from "./components/QuestionCard";
|
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import { SelectedQuestionsSideBar } from "./components/SelectedQuestionsSideBar";
|
import { SelectedQuestionsSideBar } from "./components/SelectedQuestionsSideBar";
|
||||||
import { FiltersSideBar } from "./components/FiltersSideBar";
|
import { FiltersSideBar } from "./components/FiltersSideBar";
|
||||||
|
import { BottomBar } from "./components/BottomBar";
|
||||||
|
import { QuestionArea } from "./components/QuestionArea";
|
||||||
|
import { gql, useQuery } from "@apollo/client";
|
||||||
|
import { Query, Question } from "../../__generated__/graphql-schema";
|
||||||
|
|
||||||
type NewAssessementManualForm = {
|
const QuestionFragments = gql`
|
||||||
axisWeights: Record<string, any>
|
fragment QuestionFields on Question {
|
||||||
}
|
id
|
||||||
|
authorship
|
||||||
const NEW_ASSESSEMENT_DATA_QUERY = gql`
|
authorshipYear
|
||||||
query NewAssessementDataQuery {
|
bloomTaxonomy
|
||||||
axes {
|
body
|
||||||
nodes {
|
checkType
|
||||||
id
|
difficulty
|
||||||
|
status
|
||||||
|
subject {
|
||||||
|
axis {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
category {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const QUESTIONS_QUERY = gql`
|
||||||
|
${QuestionFragments}
|
||||||
|
query {
|
||||||
|
questions {
|
||||||
|
nodes {
|
||||||
|
...QuestionFields
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const NewAssessementManual = () => {
|
export const NewAssessementManual = () => {
|
||||||
const { data } = useQuery<Query>(NEW_ASSESSEMENT_DATA_QUERY)
|
const [questions, setQuestions] = useState<Question[]>([])
|
||||||
const axes = data?.axes.nodes
|
const [selectedQuestions, setSelectedQuestions] = useState<{id: string, label: string, removeHandler: Function}[]>([])
|
||||||
|
|
||||||
const [questions, setQuestions] = useState<{id: string, label: string, removeHandler: Function}[]>([])
|
useQuery<Query>(QUESTIONS_QUERY, {
|
||||||
|
onCompleted: (response) => {
|
||||||
const [subjectsIds, setSubjectsIds] = useState<string[]>([])
|
const { questions: questionConnection } = response
|
||||||
const { register, control, watch } = useForm<NewAssessementManualForm>({
|
setQuestions(questionConnection.nodes as Question[])
|
||||||
mode: 'onBlur'
|
},
|
||||||
|
fetchPolicy: "network-only"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const addAxisForm = useForm<{ axisId: string }>()
|
|
||||||
|
|
||||||
const handleAddAxis = (formData: { axisId: string }) => {
|
|
||||||
setSubjectsIds(prev => [...prev, formData.axisId])
|
|
||||||
addAxisForm.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleRemoveAxis = (axisId: string) => {
|
|
||||||
setSubjectsIds(prev => prev.filter((axis => axis !== axisId)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!axes?.length) return null
|
|
||||||
|
|
||||||
const notSelectedAxis: Axis[] = axes.filter((axis) => !subjectsIds.includes(axis.id))
|
|
||||||
const selectedAxis: Axis[] = axes.filter((axis) => subjectsIds.includes(axis.id))
|
|
||||||
|
|
||||||
const addQuestion = (label: string, removeHandler: Function) => {
|
const addQuestion = (label: string, removeHandler: Function) => {
|
||||||
const id: string = label.replace(/\s+/g, '')
|
const id: string = label.replace(/\s+/g, '_')
|
||||||
if (!questions.find(q => q.id === id)) {
|
if (!selectedQuestions.find(q => q.id === id)) {
|
||||||
setQuestions(q => [...q, { id, label, removeHandler }])
|
setSelectedQuestions(q => [...q, { id, label, removeHandler }])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeQuestion = (id: string) => {
|
const removeQuestion = (id: string) => {
|
||||||
setQuestions(q => q.filter(i => i.id !== id))
|
setSelectedQuestions(q => q.filter(i => i.id !== id))
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearSelectedQuestions = () => {
|
||||||
|
setSelectedQuestions([])
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Navigator home />
|
<Navigator home />
|
||||||
<div className="grid grid-cols-5 gap-4 mt-4 mx-4">
|
<BottomBar/>
|
||||||
|
<div className="grid grid-cols-5 gap-4 mt-4 mx-4 pb-20">
|
||||||
<FiltersSideBar/>
|
<FiltersSideBar/>
|
||||||
<div className="col-span-3 border-l-2 border-r-2 border-gray-300 px-6 pb-20"> {/*bg-blue-500*/}
|
<QuestionArea questions={questions}
|
||||||
<QuestionCard title="Question 1"
|
onAddQuestion={addQuestion} onRemoveQuestion={removeQuestion}/>
|
||||||
onAddQuestion={addQuestion}
|
<SelectedQuestionsSideBar
|
||||||
onRemoveQuestion={removeQuestion}/>
|
questions={selectedQuestions}
|
||||||
<QuestionCard title="Question 2"
|
onClearSelectedQuestions={clearSelectedQuestions}
|
||||||
onAddQuestion={addQuestion}
|
/>
|
||||||
onRemoveQuestion={removeQuestion}/>
|
|
||||||
<QuestionCard title="Question 3"
|
|
||||||
onAddQuestion={addQuestion}
|
|
||||||
onRemoveQuestion={removeQuestion}/>
|
|
||||||
<QuestionCard title="Question 4"
|
|
||||||
onAddQuestion={addQuestion}
|
|
||||||
onRemoveQuestion={removeQuestion}/>
|
|
||||||
<QuestionCard title="Question 5"
|
|
||||||
onAddQuestion={addQuestion}
|
|
||||||
onRemoveQuestion={removeQuestion}/>
|
|
||||||
<QuestionCard title="Question 6"
|
|
||||||
onAddQuestion={addQuestion}
|
|
||||||
onRemoveQuestion={removeQuestion}/>
|
|
||||||
<QuestionCard title="Question 7"
|
|
||||||
onAddQuestion={addQuestion}
|
|
||||||
onRemoveQuestion={removeQuestion}/>
|
|
||||||
</div>
|
|
||||||
<SelectedQuestionsSideBar questions={questions}/>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
21
app/javascript/pages/assessment/components/BottomBar.tsx
Normal file
21
app/javascript/pages/assessment/components/BottomBar.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import React, { FC, } from "react";
|
||||||
|
import { Button } from "../../../components";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BottomBar: FC<Props> = () => {
|
||||||
|
return (
|
||||||
|
<div className="fixed bottom-0 bg-white w-full h-16 flex items-center justify-end shadow-lg">
|
||||||
|
<div className="flex gap-6 mx-16">
|
||||||
|
<Button className="w-32">
|
||||||
|
Cancelar
|
||||||
|
</Button>
|
||||||
|
<Button type="primary" className="w-32">
|
||||||
|
Salvar
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,57 +1,83 @@
|
|||||||
import React, { FC, } from "react";
|
import React, { FC, useState, } from "react";
|
||||||
import { SideBar } from "./SideBar";
|
import { SideBar } from "./SideBar";
|
||||||
import { SelectedQuestionCard } from "./SelectedQuestionCard";
|
|
||||||
import { Button } from "../../../components";
|
import { Button } from "../../../components";
|
||||||
import { SelectFilterField } from "./SelectFilterField";
|
import { SelectFilterField } from "./SelectFilterField";
|
||||||
import { RangeFilterField } from "./RangeFilterField";
|
import { RangeFilterField } from "./RangeFilterField";
|
||||||
|
import { BLOOM_TAXONOMY, CHECK_TYPE, DIFFICULTY } from "../../../utils/types";
|
||||||
|
import { gql, useQuery } from "@apollo/client";
|
||||||
|
import { Axis, Category, Query, Subject } from "../../../__generated__/graphql-schema";
|
||||||
|
|
||||||
|
const FILTERS_QUERY = gql`
|
||||||
|
query {
|
||||||
|
categories {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
axes {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subjects {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
questions?: {
|
|
||||||
id: string, label: string, removeHandler: Function
|
|
||||||
}[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FiltersSideBar: FC<Props> = () => {
|
export const FiltersSideBar: FC<Props> = () => {
|
||||||
|
const [categories, setCategories] = useState<Category[]>([])
|
||||||
|
const [axis, setAxis] = useState<Axis[]>([])
|
||||||
|
const [subjects, setSubjects] = useState<Subject[]>([])
|
||||||
|
|
||||||
|
const difficulties = DIFFICULTY.map(item => ({id: item.value, label: item.label}))
|
||||||
|
const bloomTaxonomyTypes = BLOOM_TAXONOMY.map(item => ({id: item.value, label: item.label}))
|
||||||
|
const checkTypes = CHECK_TYPE.map(item => ({id: item.value, label: item.label}))
|
||||||
|
const authorshipTypes = [
|
||||||
|
{id: 1, label: 'Própria'},
|
||||||
|
{id: 2, label: 'Outro'},
|
||||||
|
]
|
||||||
|
|
||||||
|
useQuery<Query>(FILTERS_QUERY, {
|
||||||
|
onCompleted: (response) => {
|
||||||
|
const {
|
||||||
|
categories: categoriesConnection,
|
||||||
|
axes: axisConnection,
|
||||||
|
subjects: subjectConnection
|
||||||
|
} = response
|
||||||
|
setCategories(categoriesConnection.nodes as Category[])
|
||||||
|
setAxis(axisConnection.nodes as Axis[])
|
||||||
|
setSubjects(subjectConnection.nodes as Subject[])
|
||||||
|
},
|
||||||
|
fetchPolicy: "network-only"
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SideBar header="Filtros">
|
<SideBar header="Filtros">
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<form className="flex flex-col gap-4"
|
<form className="flex flex-col gap-4">
|
||||||
// onSubmit={addAxisForm.handleSubmit(handleAddAxis)}
|
<SelectFilterField label="Grau de Dificuldade:" options={difficulties}/>
|
||||||
>
|
<SelectFilterField label="Categoria:" options={
|
||||||
<SelectFilterField label="Grau de Dificuldade:" options={[
|
categories.map(item => ({id: item.id, label: item.name}))
|
||||||
{id: 1, label: 'Fácil'},
|
}/>
|
||||||
{id: 2, label: 'Média'},
|
<SelectFilterField label="Eixo de Formação:" options={
|
||||||
{id: 3, label: 'Difícil'}
|
axis.map(item => ({id: item.id, label: item.name}))
|
||||||
]}/>
|
}/>
|
||||||
<SelectFilterField label="Categoria:" options={[
|
<SelectFilterField label="Assunto:" options={
|
||||||
{id: 1, label: 'Conhecimentos Básicos'},
|
subjects.map(item => ({id: item.id, label: item.name}))
|
||||||
{id: 2, label: 'Redes e Sistemas Computacionais'},
|
}/>
|
||||||
{id: 3, label: 'Modelagem e Simulacao'}
|
<SelectFilterField label="Habilidade Cognitiva:" options={bloomTaxonomyTypes}/>
|
||||||
]}/>
|
<SelectFilterField label="Tipo:" options={checkTypes}/>
|
||||||
<SelectFilterField label="Eixo de Formação:" options={[
|
<SelectFilterField label="Autoria:" options={authorshipTypes}/>
|
||||||
{id: 1, label: 'Infraestrutura de Sistemas Computacionais'},
|
|
||||||
{id: 2, label: 'Sistemas de Software'},
|
|
||||||
{id: 3, label: 'Algoritmos de Alto Desempenho'}
|
|
||||||
]}/>
|
|
||||||
<SelectFilterField label="Assunto:" options={[
|
|
||||||
{id: 1, label: 'Cálculo'},
|
|
||||||
{id: 2, label: 'Pesquisa Operacional'},
|
|
||||||
{id: 3, label: 'Sistemas Digitais'}
|
|
||||||
]}/>
|
|
||||||
<SelectFilterField label="Habilidade Cognitiva:" options={[
|
|
||||||
{id: 1, label: 'Recordar'},
|
|
||||||
{id: 2, label: 'Compreender'},
|
|
||||||
{id: 3, label: 'Criar'}
|
|
||||||
]}/>
|
|
||||||
<SelectFilterField label="Tipo:" options={[
|
|
||||||
{id: 1, label: 'Resposta Multipla'},
|
|
||||||
{id: 2, label: 'Lacuna'},
|
|
||||||
{id: 3, label: 'Foco Negativo'}
|
|
||||||
]}/>
|
|
||||||
<SelectFilterField label="Autoria:" options={[
|
|
||||||
{id: 1, label: 'Própria'},
|
|
||||||
{id: 2, label: 'Outro'},
|
|
||||||
]}/>
|
|
||||||
<RangeFilterField label="Ano:"/>
|
<RangeFilterField label="Ano:"/>
|
||||||
<div className="w-full flex flex-col mt-2 gap-3">
|
<div className="w-full flex flex-col mt-2 gap-3">
|
||||||
<Button type="primary" htmlType="submit">
|
<Button type="primary" htmlType="submit">
|
||||||
@@ -61,7 +87,6 @@ export const FiltersSideBar: FC<Props> = () => {
|
|||||||
Limpar Filtro
|
Limpar Filtro
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</SideBar>
|
</SideBar>
|
||||||
|
|||||||
20
app/javascript/pages/assessment/components/QuestionArea.tsx
Normal file
20
app/javascript/pages/assessment/components/QuestionArea.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import React, { FC } from "react";
|
||||||
|
import { QuestionCard } from "./QuestionCard";
|
||||||
|
import { Question } from "../../../__generated__/graphql-schema";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
questions: Question[]
|
||||||
|
onAddQuestion: Function,
|
||||||
|
onRemoveQuestion: Function
|
||||||
|
}
|
||||||
|
|
||||||
|
export const QuestionArea: FC<Props> = ({ questions, onAddQuestion, onRemoveQuestion }) => {
|
||||||
|
return (
|
||||||
|
<div className="col-span-3 border-l-2 border-r-2 border-gray-300 px-6">
|
||||||
|
{questions.map(question =>
|
||||||
|
<QuestionCard key={question.id} question={question}
|
||||||
|
onAddQuestion={onAddQuestion}
|
||||||
|
onRemoveQuestion={onRemoveQuestion}/>)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,16 +1,24 @@
|
|||||||
import React, { FC, useState } from "react";
|
import React, { FC, useState } from "react";
|
||||||
import { Button, Card } from "../../../components";
|
import { Button, Card } from "../../../components";
|
||||||
|
import { QuestionCardField } from "./QuestionCardField";
|
||||||
|
import { Question } from "../../../__generated__/graphql-schema";
|
||||||
|
import { NodeId } from "../../../utils/graphql";
|
||||||
|
import { BLOOM_TAXONOMY, CHECK_TYPE, DIFFICULTY } from "../../../utils/types";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
question: Question
|
||||||
onAddQuestion: Function,
|
onAddQuestion: Function,
|
||||||
onRemoveQuestion: Function
|
onRemoveQuestion: Function
|
||||||
}
|
}
|
||||||
|
|
||||||
export const QuestionCard: FC<Props> = ({ title, onAddQuestion, onRemoveQuestion }) => {
|
export const QuestionCard: FC<Props> = ({ question, onAddQuestion, onRemoveQuestion }) => {
|
||||||
const [collapsed, setCollapsed] = useState(false)
|
const [collapsed, setCollapsed] = useState(false)
|
||||||
|
|
||||||
const questionId = title.replace(/\s+/g, '')
|
const title = `Questão ${NodeId.decode(question.id).id}`
|
||||||
|
const htmlId = title.replace(/\s+/g, '_')
|
||||||
|
const difficulty = DIFFICULTY.find(item => item.value === question.difficulty)?.label
|
||||||
|
const bloomTaxonomy = BLOOM_TAXONOMY.find(item => item.value === question.bloomTaxonomy)?.label
|
||||||
|
const checkType = CHECK_TYPE.find(item => item.value === question.checkType)?.label
|
||||||
|
|
||||||
const handleAddQuestion = () => {
|
const handleAddQuestion = () => {
|
||||||
setButtonState({
|
setButtonState({
|
||||||
@@ -23,7 +31,7 @@ export const QuestionCard: FC<Props> = ({ title, onAddQuestion, onRemoveQuestion
|
|||||||
setButtonState({
|
setButtonState({
|
||||||
bg: '', label: 'Adicionar', method: handleAddQuestion
|
bg: '', label: 'Adicionar', method: handleAddQuestion
|
||||||
})
|
})
|
||||||
onRemoveQuestion(questionId)
|
onRemoveQuestion(htmlId)
|
||||||
}
|
}
|
||||||
|
|
||||||
const [buttonState, setButtonState] = useState({
|
const [buttonState, setButtonState] = useState({
|
||||||
@@ -31,47 +39,21 @@ export const QuestionCard: FC<Props> = ({ title, onAddQuestion, onRemoveQuestion
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id={questionId}>
|
<div id={htmlId}>
|
||||||
<Card title={title} className="mb-5">
|
<Card title={title} className="mb-5">
|
||||||
<div>
|
<div>
|
||||||
{!collapsed && <div className="grid grid-cols-2 gap-2">
|
{!collapsed && <div className="grid grid-cols-2 gap-2">
|
||||||
<div>
|
<QuestionCardField label="Grau de Dificuldade" value={difficulty}/>
|
||||||
<span className="text-gray-700">Grau de Dificuldade: </span>
|
<QuestionCardField label="Categoria" value={question.subject?.category.name}/>
|
||||||
<span>Média</span>
|
<QuestionCardField label="Eixo de Formação" value={question.subject?.axis.name}/>
|
||||||
</div>
|
<QuestionCardField label="Assunto" value={question.subject?.name}/>
|
||||||
<div>
|
<QuestionCardField label="Habilidade Cognitiva" value={bloomTaxonomy}/>
|
||||||
<span className="text-gray-700">Categoria: </span>
|
<QuestionCardField label="Tipo" value={checkType}/>
|
||||||
<span>Modelagem</span>
|
<QuestionCardField label="Autoria" value={question.authorship} />
|
||||||
</div>
|
<QuestionCardField label="Ano" value={question.authorshipYear}/>
|
||||||
<div>
|
|
||||||
<span className="text-gray-700">Eixo de Formação: </span>
|
|
||||||
<span>Infra Sistemas</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="text-gray-700">Assunto: </span>
|
|
||||||
<span>Fisica</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="text-gray-700">Habilidade Cognitiva: </span>
|
|
||||||
<span>Compreender</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="text-gray-700">Tipo: </span>
|
|
||||||
<span>Resposta Multipla</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="text-gray-700">Autoria: </span>
|
|
||||||
<span>UNIFESO</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="text-gray-700">Ano: </span>
|
|
||||||
<span>2023</span>
|
|
||||||
</div>
|
|
||||||
<div className="col-span-2">
|
<div className="col-span-2">
|
||||||
<span className="text-gray-700">Enunciado:</span>
|
<span className="text-gray-700">Enunciado:</span>
|
||||||
<div>
|
<div dangerouslySetInnerHTML={{__html: question.body ?? ''}}></div>
|
||||||
ijodsjidsoifidfsiojsdfiojdsfiodfs ijdf iodsf iosd iojdf sijodsf iojdsf ioj sdfiojdf sioj dfsiojsdf iojdfs ijodsfijoidfsijodfsijdfsijo dsiofd ijosdfjiofdsidsfio
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
</div>}
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import React, { FC } from "react";
|
||||||
|
import { Maybe } from "../../../__generated__/graphql-schema";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
label: string,
|
||||||
|
value?: Maybe<string>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const QuestionCardField: FC<Props> = ({ label, value }) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-700">{`${label}: `}</span>
|
||||||
|
<span>{value ?? ''}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { FC, } from "react";
|
import React, { FC, } from "react";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
options: {id: number, label: string,}[]
|
options: {id: number | string, label: string,}[]
|
||||||
label: string
|
label: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,35 @@
|
|||||||
import React, { FC, } from "react";
|
import React, { FC, } from "react";
|
||||||
import { SideBar } from "./SideBar";
|
import { SideBar } from "./SideBar";
|
||||||
import { SelectedQuestionCard } from "./SelectedQuestionCard";
|
import { SelectedQuestionCard } from "./SelectedQuestionCard";
|
||||||
|
import { Button } from "../../../components";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
questions: {
|
questions: {
|
||||||
id: string, label: string, removeHandler: Function
|
id: string, label: string, removeHandler: Function
|
||||||
}[]
|
}[]
|
||||||
|
onClearSelectedQuestions: Function
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SelectedQuestionsSideBar: FC<Props> = ({ questions }) => {
|
export const SelectedQuestionsSideBar: FC<Props> = ({ questions, onClearSelectedQuestions }) => {
|
||||||
return (
|
return (
|
||||||
<SideBar header="Questões Selecionadas">
|
<SideBar header="Questões Selecionadas">
|
||||||
<div>
|
<div>
|
||||||
{questions.length ?
|
{questions.length ?
|
||||||
questions.map(q => <SelectedQuestionCard
|
<>
|
||||||
key={q.id} id={q.id} label={q.label}
|
<div>
|
||||||
onRemoveQuestion={q.removeHandler}/>) :
|
{questions.map(q => <SelectedQuestionCard
|
||||||
<h2 className="text-gray-700 mt-3">
|
key={q.id} id={q.id} label={q.label}
|
||||||
Nenhuma questão selecionada
|
onRemoveQuestion={q.removeHandler}/>)}
|
||||||
</h2>
|
</div>
|
||||||
|
<div className="flex justify-center mt-6">
|
||||||
|
<Button type="primary" onClick={() => onClearSelectedQuestions()}>
|
||||||
|
Limpar Seleção
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</> :
|
||||||
|
<h2 className="text-gray-700 mt-3">
|
||||||
|
Nenhuma questão selecionada
|
||||||
|
</h2>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</SideBar>
|
</SideBar>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { FaArrowLeft, FaArrowRight, FaAngleDown, FaAngleUp } from 'react-icons/f
|
|||||||
import { MdModeEdit } from 'react-icons/md';
|
import { MdModeEdit } from 'react-icons/md';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import { useLocalStorage } from 'usehooks-ts';
|
||||||
|
|
||||||
import { Question, QuestionStatus } from '../../../__generated__/graphql-schema'
|
import { Question, QuestionStatus } from '../../../__generated__/graphql-schema'
|
||||||
import { useCurrentUser } from '../../../contexts';
|
import { useCurrentUser } from '../../../contexts';
|
||||||
@@ -39,7 +40,7 @@ export const QuestionsListFragments = gql`
|
|||||||
export const QuestionsList: FC<Props> = ({ questions, title, pagination }) => {
|
export const QuestionsList: FC<Props> = ({ questions, title, pagination }) => {
|
||||||
const { user } = useCurrentUser()
|
const { user } = useCurrentUser()
|
||||||
const [pageCount, setPageCount] = useState(1)
|
const [pageCount, setPageCount] = useState(1)
|
||||||
const [collapsed, setCollapsed] = useState(false)
|
const [collapsed, setCollapsed] = useLocalStorage<boolean>('collapsed', false)
|
||||||
|
|
||||||
const formatDate = (stringDate: string) => new Date(stringDate).toLocaleDateString()
|
const formatDate = (stringDate: string) => new Date(stringDate).toLocaleDateString()
|
||||||
|
|
||||||
|
|||||||
23
app/models/assessment.rb
Normal file
23
app/models/assessment.rb
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: assessments
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# observations :text
|
||||||
|
# params :jsonb
|
||||||
|
# title :string
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# user_id :bigint not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_assessments_on_user_id (user_id)
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# fk_rails_... (user_id => users.id)
|
||||||
|
#
|
||||||
|
class Assessment < ApplicationRecord
|
||||||
|
belongs_to :user
|
||||||
|
end
|
||||||
13
app/policies/assessment_policy.rb
Normal file
13
app/policies/assessment_policy.rb
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
class AssessmentPolicy < ApplicationPolicy
|
||||||
|
class Scope < Scope
|
||||||
|
def resolve
|
||||||
|
scope.all
|
||||||
|
end
|
||||||
|
|
||||||
|
def index?
|
||||||
|
@roles.find do |role|
|
||||||
|
admin nde coordinator center_director pro_rector teacher
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
12
db/migrate/20230705145916_create_assessments.rb
Normal file
12
db/migrate/20230705145916_create_assessments.rb
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
class CreateAssessments < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
create_table :assessments do |t|
|
||||||
|
t.string :title
|
||||||
|
t.text :observations
|
||||||
|
t.jsonb :params
|
||||||
|
t.references :user, null: false, foreign_key: true
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
13
db/schema.rb
generated
13
db/schema.rb
generated
@@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.0].define(version: 2022_08_05_233401) do
|
ActiveRecord::Schema[7.0].define(version: 2023_07_05_145916) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
|
||||||
@@ -56,6 +56,16 @@ ActiveRecord::Schema[7.0].define(version: 2022_08_05_233401) do
|
|||||||
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
|
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "assessments", force: :cascade do |t|
|
||||||
|
t.string "title"
|
||||||
|
t.text "observations"
|
||||||
|
t.jsonb "params"
|
||||||
|
t.bigint "user_id", null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["user_id"], name: "index_assessments_on_user_id"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "axes", force: :cascade do |t|
|
create_table "axes", force: :cascade do |t|
|
||||||
t.string "name"
|
t.string "name"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
@@ -155,6 +165,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_08_05_233401) do
|
|||||||
|
|
||||||
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
||||||
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
||||||
|
add_foreign_key "assessments", "users"
|
||||||
add_foreign_key "questions", "subjects"
|
add_foreign_key "questions", "subjects"
|
||||||
add_foreign_key "questions", "users"
|
add_foreign_key "questions", "users"
|
||||||
add_foreign_key "review_messages", "questions"
|
add_foreign_key "review_messages", "questions"
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
"postcss": "^7.0.32",
|
"postcss": "^7.0.32",
|
||||||
"postcss-cli": "^7.1.1",
|
"postcss-cli": "^7.1.1",
|
||||||
"postcss-import": "^12.0.1",
|
"postcss-import": "^12.0.1",
|
||||||
"tailwindcss": "^1.5.1"
|
"tailwindcss": "^1.5.1",
|
||||||
|
"usehooks-ts": "2.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
spec/factories/assessments.rb
Normal file
28
spec/factories/assessments.rb
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: assessments
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# observations :text
|
||||||
|
# params :jsonb
|
||||||
|
# title :string
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# user_id :bigint not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_assessments_on_user_id (user_id)
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# fk_rails_... (user_id => users.id)
|
||||||
|
#
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :assessment do
|
||||||
|
title { "MyString" }
|
||||||
|
observations { "MyText" }
|
||||||
|
params { "" }
|
||||||
|
user { nil }
|
||||||
|
end
|
||||||
|
end
|
||||||
25
spec/models/assessment_spec.rb
Normal file
25
spec/models/assessment_spec.rb
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: assessments
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# observations :text
|
||||||
|
# params :jsonb
|
||||||
|
# title :string
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# user_id :bigint not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_assessments_on_user_id (user_id)
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# fk_rails_... (user_id => users.id)
|
||||||
|
#
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Assessment, type: :model do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user