Added form submission

This commit is contained in:
Dmitriy Shishkov 2020-10-14 21:00:25 +05:00
parent 46389bad32
commit 4b84cb15e8
No known key found for this signature in database
GPG Key ID: D76D70029F55183E
10 changed files with 379 additions and 38 deletions

3
.gitignore vendored
View File

@ -22,4 +22,5 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.local*
*.local*
*.gen*

13
codegen.yml Normal file
View 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'

View File

@ -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"
}
}

View File

@ -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
View 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!
}

View File

@ -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>

View 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

View 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]
}

View 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

View File

@ -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