Added form submission
This commit is contained in:
parent
46389bad32
commit
4b84cb15e8
3
.gitignore
vendored
3
.gitignore
vendored
@ -22,4 +22,5 @@ npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
*.local*
|
||||
*.local*
|
||||
*.gen*
|
13
codegen.yml
Normal file
13
codegen.yml
Normal file
@ -0,0 +1,13 @@
|
||||
overwrite: true
|
||||
schema: 'src/apollo/typeDefs.gql'
|
||||
documents: null
|
||||
generates:
|
||||
src/apollo/typeDefs.gen.ts:
|
||||
config:
|
||||
useIndexSignature: true
|
||||
wrapFieldDefinitions: true
|
||||
enumsAsTypes: true
|
||||
declarationKind: 'interface'
|
||||
|
||||
plugins:
|
||||
- 'typescript'
|
@ -18,7 +18,8 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build"
|
||||
"build": "react-scripts build",
|
||||
"codegen": "graphql-codegen --config codegen.yml"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
@ -34,5 +35,9 @@
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/cli": "^1.17.10",
|
||||
"@graphql-codegen/typescript": "^1.17.10"
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,27 @@ const LOGIN = gql`
|
||||
const FORM = gql`
|
||||
query Form($id: Int!) {
|
||||
form(id: $id) {
|
||||
author {
|
||||
email
|
||||
id
|
||||
name
|
||||
}
|
||||
dateCreated
|
||||
id
|
||||
questions {
|
||||
number
|
||||
... on ChoisesQuestion {
|
||||
title
|
||||
type
|
||||
variants {
|
||||
text
|
||||
}
|
||||
}
|
||||
... on InputQuestion {
|
||||
number
|
||||
title
|
||||
}
|
||||
}
|
||||
title
|
||||
}
|
||||
}
|
||||
@ -27,4 +47,12 @@ const USER = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export { LOGIN, FORM, USER }
|
||||
const FORMSUBMIT = gql`
|
||||
mutation FormSubmit($formId: Int!, $answers: String!) {
|
||||
formSubmit(formId: $formId, answers: $answers) {
|
||||
success
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export { LOGIN, FORM, USER, FORMSUBMIT }
|
||||
|
84
src/apollo/typeDefs.gql
Normal file
84
src/apollo/typeDefs.gql
Normal file
@ -0,0 +1,84 @@
|
||||
type Query {
|
||||
form(id: Int!): Form
|
||||
forms: [Form!]!
|
||||
user(id: Int): User
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createForm(title: String!, questions: String!): serverAnswer
|
||||
formSubmit(formId: Int!, answers: String!): serverAnswer
|
||||
login(email: String!): serverAnswer
|
||||
register(name: String!, email: String!): serverAnswer
|
||||
}
|
||||
|
||||
type Form {
|
||||
author: User
|
||||
dateCreated: String!
|
||||
id: Int!
|
||||
questions: [Question!]!
|
||||
submissions: [FormSubmission!]
|
||||
title: String!
|
||||
}
|
||||
|
||||
interface Question {
|
||||
number: Int!
|
||||
title: String!
|
||||
}
|
||||
|
||||
type ChoisesQuestion implements Question {
|
||||
number: Int!
|
||||
title: String!
|
||||
type: ChoiseType!
|
||||
variants: [Variant!]!
|
||||
}
|
||||
|
||||
type Variant {
|
||||
text: String!
|
||||
}
|
||||
|
||||
type InputQuestion implements Question {
|
||||
number: Int!
|
||||
title: String!
|
||||
}
|
||||
|
||||
type FormSubmission {
|
||||
answers: [Answer!]!
|
||||
date: String!
|
||||
id: Int!
|
||||
}
|
||||
|
||||
interface Answer {
|
||||
type: AnswerType!
|
||||
}
|
||||
|
||||
type InputAnswer implements Answer {
|
||||
type: AnswerType!
|
||||
userInput: String
|
||||
}
|
||||
|
||||
type ChoiseAnswer implements Answer {
|
||||
type: AnswerType!
|
||||
userChoise: Int!
|
||||
}
|
||||
|
||||
enum ChoiseType {
|
||||
CHECK
|
||||
CHOOSE
|
||||
SELECT
|
||||
}
|
||||
|
||||
enum AnswerType {
|
||||
CHOISE
|
||||
INPUT
|
||||
}
|
||||
|
||||
type User {
|
||||
email: String!
|
||||
forms: [Form!]
|
||||
id: Int!
|
||||
name: String!
|
||||
}
|
||||
|
||||
type serverAnswer {
|
||||
success: Boolean!
|
||||
}
|
@ -6,36 +6,10 @@ import client from '../apollo'
|
||||
import Context from '../context'
|
||||
import { useUser } from '../hooks'
|
||||
import Authorize from './Authorize'
|
||||
import DoForm from './DoForm'
|
||||
import Login from './Login'
|
||||
import UserPage from './UserPage'
|
||||
|
||||
// const TestComponent: React.FC = () => {
|
||||
// const { loading, error, data } = useQuery(FORM, {
|
||||
// variables: {
|
||||
// id: 1
|
||||
// }
|
||||
// })
|
||||
|
||||
// const { user } = useContext(Context)
|
||||
|
||||
// const [doLogin] = useMutation(LOGIN)
|
||||
|
||||
// if (loading) return <p>Loading...</p>
|
||||
// if (error) return <p>Error :(</p>
|
||||
|
||||
// return (
|
||||
// <div>
|
||||
// <button
|
||||
// onClick={() => doLogin({ variables: { email: 'test@test.test' } })}
|
||||
// >
|
||||
// Click!
|
||||
// </button>
|
||||
// {user.id}
|
||||
// {data.form.id}
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
|
||||
const App: React.FC = () => {
|
||||
const userContext = useUser()
|
||||
|
||||
@ -48,6 +22,7 @@ const App: React.FC = () => {
|
||||
<Route path="/login" component={Login} />
|
||||
<Route path="/authorize" component={Authorize} />
|
||||
<Route path="/user" component={UserPage} />
|
||||
<Route path="/form/:id" component={DoForm} />
|
||||
</Switch>
|
||||
</Router>
|
||||
</Context.Provider>
|
||||
|
36
src/components/DoForm/Lists.tsx
Normal file
36
src/components/DoForm/Lists.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import React from 'react'
|
||||
|
||||
interface IProps {
|
||||
variants: { text: string }[]
|
||||
name: string
|
||||
type: 'CHECK' | 'CHOOSE'
|
||||
onChange: (num: number) => void
|
||||
}
|
||||
|
||||
const Lists: React.FC<IProps> = ({ variants, name, type, onChange }) => {
|
||||
const inputType =
|
||||
(type === 'CHECK' && 'check') || (type === 'CHOOSE' && 'radio') || undefined
|
||||
|
||||
return (
|
||||
<div>
|
||||
{variants.map((el, index) => (
|
||||
<label key={index}>
|
||||
<input
|
||||
onChange={(e) => {
|
||||
const selectValue = variants.findIndex(
|
||||
(val) => val.text === e.currentTarget.value
|
||||
)
|
||||
onChange(selectValue)
|
||||
}}
|
||||
type={inputType}
|
||||
name={name}
|
||||
value={el.text}
|
||||
/>
|
||||
{el.text}
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Lists
|
28
src/components/DoForm/hooks.ts
Normal file
28
src/components/DoForm/hooks.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { useState } from 'react'
|
||||
import { ChoiseAnswer, InputAnswer } from '../../apollo/typeDefs.gen'
|
||||
|
||||
export const useForm = (initialValue?: (InputAnswer | ChoiseAnswer)[]) => {
|
||||
console.log(initialValue, 'Inside hook')
|
||||
|
||||
const [answers, setAnswer] = useState<(InputAnswer | ChoiseAnswer)[]>(
|
||||
initialValue || []
|
||||
)
|
||||
|
||||
const answerChange = (num: number) => {
|
||||
return (value: number | string) => {
|
||||
setAnswer((prev) => {
|
||||
return prev.map((el, index) => {
|
||||
if (index === num) {
|
||||
if (el.__typename === 'ChoiseAnswer' && typeof value === 'number')
|
||||
return { ...el, userChoise: value }
|
||||
if (el.__typename === 'InputAnswer' && typeof value === 'string')
|
||||
return { ...el, userInput: value }
|
||||
}
|
||||
return el
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return [answers, answerChange]
|
||||
}
|
171
src/components/DoForm/index.tsx
Normal file
171
src/components/DoForm/index.tsx
Normal file
@ -0,0 +1,171 @@
|
||||
import { useMutation, useQuery } from '@apollo/client'
|
||||
import React, { FormEvent, useEffect, useState } from 'react'
|
||||
import { Redirect, useParams } from 'react-router-dom'
|
||||
import { FORM, FORMSUBMIT } from '../../apollo'
|
||||
import {
|
||||
ChoiseAnswer,
|
||||
ChoisesQuestion,
|
||||
Form,
|
||||
InputAnswer,
|
||||
InputQuestion,
|
||||
QueryFormArgs,
|
||||
} from '../../apollo/typeDefs.gen'
|
||||
import Lists from './Lists'
|
||||
|
||||
interface IFormQuery {
|
||||
form: Form
|
||||
}
|
||||
|
||||
const DoForm: React.FC = () => {
|
||||
const { id: idString } = useParams<{ id: string }>()
|
||||
|
||||
const id = parseInt(idString)
|
||||
|
||||
const { data, error, loading } = useQuery<IFormQuery, QueryFormArgs>(FORM, {
|
||||
variables: { id },
|
||||
skip: isNaN(id),
|
||||
})
|
||||
|
||||
const [
|
||||
doFormSubmit,
|
||||
{ error: submitError, data: submitData, loading: submitLoading },
|
||||
] = useMutation(FORMSUBMIT)
|
||||
|
||||
const [answers, setAnswer] = useState<(InputAnswer | ChoiseAnswer)[]>([])
|
||||
|
||||
const getInitialState = (data: IFormQuery) => {
|
||||
if (data && data.form) {
|
||||
return data.form.questions.flatMap<InputAnswer | ChoiseAnswer>(
|
||||
(el: InputQuestion | ChoisesQuestion) => {
|
||||
if (el.__typename === 'ChoisesQuestion')
|
||||
return [
|
||||
{ __typename: 'ChoiseAnswer', type: 'CHOISE', userChoise: -1 },
|
||||
]
|
||||
if (el.__typename === 'InputQuestion')
|
||||
return [{ __typename: 'InputAnswer', type: 'INPUT', userInput: '' }]
|
||||
return []
|
||||
}
|
||||
)
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
const initialState = getInitialState(data)
|
||||
setAnswer(initialState)
|
||||
}
|
||||
}, [data])
|
||||
|
||||
useEffect(() => console.log(answers), [answers])
|
||||
|
||||
if (isNaN(id)) return <Redirect to="/" />
|
||||
|
||||
if (loading) return <div>Loading...</div>
|
||||
if (error) return <div>{error.message}</div>
|
||||
|
||||
const { form } = data!
|
||||
|
||||
const handleSubmit = (e: FormEvent) => {
|
||||
e.preventDefault()
|
||||
console.log('Submited form:', answers)
|
||||
|
||||
answers.forEach((el) => {
|
||||
delete el.__typename
|
||||
})
|
||||
|
||||
const submitAnswers = JSON.stringify(answers)
|
||||
|
||||
console.log('Filtered answers: ', submitAnswers)
|
||||
|
||||
doFormSubmit({
|
||||
variables: {
|
||||
formId: id,
|
||||
answers: submitAnswers,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const answerChange = (num: number) => {
|
||||
return (value: number | string) => {
|
||||
setAnswer((prev) => {
|
||||
return prev.map((el, index) => {
|
||||
if (index === num) {
|
||||
if (el.__typename === 'ChoiseAnswer' && typeof value === 'number')
|
||||
return { ...el, userChoise: value }
|
||||
if (el.__typename === 'InputAnswer' && typeof value === 'string')
|
||||
return { ...el, userInput: value }
|
||||
}
|
||||
return el
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{form.title}</h1>
|
||||
<p>{form.dateCreated}</p>
|
||||
<h3>{form.author?.name || 'No author'}</h3>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<ul>
|
||||
{form.questions.map((el: InputQuestion | ChoisesQuestion) => {
|
||||
if (el.__typename === 'InputQuestion')
|
||||
return (
|
||||
<li key={el.number}>
|
||||
<label>
|
||||
{el.title}
|
||||
<input
|
||||
onChange={(e) =>
|
||||
answerChange(el.number)(e.currentTarget.value)
|
||||
}
|
||||
type="text"
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
)
|
||||
if (el.__typename === 'ChoisesQuestion')
|
||||
return (
|
||||
<li key={el.number}>
|
||||
<label>
|
||||
{el.title}
|
||||
{el.type === 'SELECT' ? (
|
||||
<select
|
||||
onChange={(e) => {
|
||||
const selectValue = el.variants.findIndex(
|
||||
(val) => val.text === e.currentTarget.value
|
||||
)
|
||||
answerChange(el.number)(selectValue)
|
||||
}}
|
||||
name={el.title}
|
||||
>
|
||||
{el.variants.map((option, index) => (
|
||||
<option key={index}>{option.text}</option>
|
||||
))}
|
||||
</select>
|
||||
) : (
|
||||
<Lists
|
||||
variants={el.variants}
|
||||
onChange={answerChange(el.number)}
|
||||
name={el.title}
|
||||
type={el.type}
|
||||
/>
|
||||
)}
|
||||
</label>
|
||||
</li>
|
||||
)
|
||||
return <li>Unknown question type</li>
|
||||
})}
|
||||
</ul>
|
||||
{submitLoading ? <p>Uploading...</p> : <input type="submit" />}
|
||||
</form>
|
||||
{submitError && <p>{submitError.message}</p>}
|
||||
{submitData && submitData.formSubmit && submitData.formSubmit.success && (
|
||||
<p>Successfully uploaded</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DoForm
|
@ -8,15 +8,15 @@ const UserPage: React.FC = () => {
|
||||
|
||||
if (error) return <p>{error.message}</p>
|
||||
|
||||
console.log(Object.entries(data.user))
|
||||
const { name, email, id } = data.user
|
||||
|
||||
const user = Object.entries(data.user).map((el, index) => (
|
||||
<li key={index}>
|
||||
{el[0]}: {el[1]}
|
||||
</li>
|
||||
))
|
||||
|
||||
return <ul>{user}</ul>
|
||||
return (
|
||||
<div>
|
||||
<h1>Username: {name}</h1>
|
||||
<h3>Email: {email}</h3>
|
||||
<p>User ID: {id}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default UserPage
|
||||
|
Loading…
x
Reference in New Issue
Block a user