Form.tsx 7.07 KB
Newer Older
v.mattfeld's avatar
v.mattfeld committed
1
import React, { FC, useCallback, useEffect, useState } from 'react';
v.mattfeld's avatar
v.mattfeld committed
2
import { Box, Button, Stack, Radio, Flex, Spacer } from "@chakra-ui/react";
Valerius's avatar
Valerius committed
3
import { useRouter } from 'next/router';
Valerius's avatar
Valerius committed
4
import { Formik } from 'formik';
Valerius's avatar
Valerius committed
5
6
import { CheckboxControl, CheckboxContainer, RadioGroupControl } from "formik-chakra-ui";
import * as Yup from 'yup'
Valerius's avatar
Valerius committed
7
import { useTranslation } from 'react-i18next';
v.mattfeld's avatar
v.mattfeld committed
8
import { useRecoilState } from 'recoil';
v.mattfeld's avatar
v.mattfeld committed
9
import { attentionCheckState, Check } from 'pages/annotate';
v.mattfeld's avatar
v.mattfeld committed
10
11
import { logger } from 'utils/logger';
import { fetcher } from 'utils/utils';
12
import { STUDY_SIZE } from 'db/db';
Valerius's avatar
Valerius committed
13
14
15
16
17

const validateSchema = Yup.object({
	sensitivity: Yup.string().required(),
	targetDemographic: Yup.array().min(1),
});
v.mattfeld's avatar
v.mattfeld committed
18

Valerius's avatar
Valerius committed
19
20
21
interface FormProps {
	pageNumber: number;
	uid: string;
v.mattfeld's avatar
v.mattfeld committed
22
23
24
	imageID: number | string;
	refetch: any;
	isCheck: boolean;
Valerius's avatar
Valerius committed
25
26
}

v.mattfeld's avatar
v.mattfeld committed
27

v.mattfeld's avatar
v.mattfeld committed
28
const Form: FC<FormProps> = ({ pageNumber, uid, imageID, refetch, isCheck }) => {
Valerius's avatar
Valerius committed
29
	const router = useRouter()
Valerius's avatar
Valerius committed
30
	const { t } = useTranslation()
v.mattfeld's avatar
v.mattfeld committed
31
32
33
	const attentionURL = `/api/dbAttention?uid=${uid}`

	const [shouldRefetch, setShouldRefetch] = useState(true)
Valerius's avatar
Valerius committed
34

v.mattfeld's avatar
v.mattfeld committed
35
	const [attentionCheck, setAttentionCheck] = useRecoilState(attentionCheckState)
v.mattfeld's avatar
v.mattfeld committed
36
37
38
	const { checks, currentPage } = attentionCheck
	const currentCheck = checks.find(check => check.page === currentPage) || { isQuestionOne: false }
	const { isQuestionOne } = currentCheck
v.mattfeld's avatar
v.mattfeld committed
39

Valerius's avatar
Valerius committed
40
	useEffect(() => {
v.mattfeld's avatar
v.mattfeld committed
41
42
43
		if (pageNumber > STUDY_SIZE)
			// redirect to done after STUDY_SIZE submits
			router.push(`/done?uid=${uid}`)
v.mattfeld's avatar
v.mattfeld committed
44
	}, [pageNumber, router, uid])
Valerius's avatar
Valerius committed
45

v.mattfeld's avatar
v.mattfeld committed
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
	useCallback(() => {
		const fn = async () => {
			if (!shouldRefetch) return

			const { data } = await fetcher(attentionURL)

			if (uid && attentionCheck.uid && data) {
				const { att_0, att_1, att_2 } = data
				const checkOne: Check = {
					...checks[0],
					pass: att_0,
				}
				const checkTwo: Check = {
					...checks[1],
					pass: att_1,
				}
				const checkThree: Check = {
					...checks[2],
					pass: att_2,
				}
				const newChecks = [checkOne, checkTwo, checkThree]
				setAttentionCheck({
					...attentionCheck,
					checks: newChecks,
				})
				setShouldRefetch(false)
			}
		}
		fn()
	}, [attentionCheck, attentionURL, checks, setAttentionCheck, shouldRefetch, uid])

Valerius's avatar
Valerius committed
77
	return (
Valerius's avatar
Valerius committed
78
79
80
		<Formik
			initialValues={{
				sensitivity: "",
Valerius's avatar
Valerius committed
81
				targetDemographic: [''],
Valerius's avatar
Valerius committed
82
83
84
			}}

			onSubmit={(values, { resetForm }) => {
v.mattfeld's avatar
v.mattfeld committed
85
86

				const fetchError = (error: any) => {
v.mattfeld's avatar
v.mattfeld committed
87
					logger.error({ error, values })
v.mattfeld's avatar
v.mattfeld committed
88
89
90
91
					alert(error)
					resetForm()
				}

Valerius's avatar
Valerius committed
92
93
				const myHeaders = new Headers();
				myHeaders.append("Content-Type", "application/json");
v.mattfeld's avatar
v.mattfeld committed
94

v.mattfeld's avatar
bug fix    
v.mattfeld committed
95
				let tms = values.targetDemographic.filter(x => x !== '')
v.mattfeld's avatar
v.mattfeld committed
96

v.mattfeld's avatar
v.mattfeld committed
97
				if (isCheck) {
v.mattfeld's avatar
v.mattfeld committed
98
					const pass = isQuestionOne ? values.sensitivity == '6' : tms.includes("Everybody")
v.mattfeld's avatar
v.mattfeld committed
99
100

					// update state
v.mattfeld's avatar
v.mattfeld committed
101
					const innerCheck = attentionCheck.checks.find(x => x.imageID === imageID)
v.mattfeld's avatar
v.mattfeld committed
102

v.mattfeld's avatar
v.mattfeld committed
103
					const newCheck: Check = {
v.mattfeld's avatar
v.mattfeld committed
104
						...innerCheck!,
v.mattfeld's avatar
v.mattfeld committed
105
106
107
						checked: true,
						pass,
					}
v.mattfeld's avatar
v.mattfeld committed
108

v.mattfeld's avatar
v.mattfeld committed
109
					// find index to update the checks
v.mattfeld's avatar
v.mattfeld committed
110
111
112
					const index = attentionCheck.checks.indexOf(innerCheck!)
					let currentChecks = [...attentionCheck.checks] // copy that is not read only
					currentChecks[index] = newCheck
v.mattfeld's avatar
v.mattfeld committed
113
114
					const updatedChecks = {
						...attentionCheck,
v.mattfeld's avatar
v.mattfeld committed
115
						checks: currentChecks,
v.mattfeld's avatar
v.mattfeld committed
116
						currentPage: pageNumber - 1,
v.mattfeld's avatar
v.mattfeld committed
117
					}
v.mattfeld's avatar
v.mattfeld committed
118
					setAttentionCheck(updatedChecks)
v.mattfeld's avatar
v.mattfeld committed
119

v.mattfeld's avatar
v.mattfeld committed
120
121
					const attentionPayload = updatedChecks
					const rawAttention = JSON.stringify({ ...attentionPayload, page: pageNumber - 1 })
v.mattfeld's avatar
v.mattfeld committed
122
					const attentionRequestOptions: RequestInit = {
v.mattfeld's avatar
v.mattfeld committed
123
124
						method: 'POST',
						headers: myHeaders,
v.mattfeld's avatar
v.mattfeld committed
125
						body: rawAttention,
v.mattfeld's avatar
v.mattfeld committed
126
127
						redirect: 'follow',

v.mattfeld's avatar
v.mattfeld committed
128
129
					}

v.mattfeld's avatar
v.mattfeld committed
130
					fetch(`/api/attention`, attentionRequestOptions)
v.mattfeld's avatar
v.mattfeld committed
131
132
133
134
135
136
137
138
139
140
						.then(response => {
							if (response.status === 400) {
								throw new Error('Attention Check Request Format Error');
							}
							response.text()
						})
						.then(() => {
							resetForm()
							refetch()
						})
v.mattfeld's avatar
v.mattfeld committed
141
						.catch(fetchError);
v.mattfeld's avatar
v.mattfeld committed
142
					setShouldRefetch(true)
v.mattfeld's avatar
v.mattfeld committed
143
144
					return
				}
145

146
				let tdv = {
v.mattfeld's avatar
bug fix    
v.mattfeld committed
147
148
149
150
151
152
					acquaintance: tms.includes("Aquaintance"),
					colleagues: tms.includes("Colleagues"),
					family: tms.includes("Family"),
					friends: tms.includes("Friends"),
					everybody: tms.includes("Everybody"),
					nobody: tms.includes("Nobody"),
153
154
				}

155
				let payload = {
156
					sensitivity: values.sensitivity,
157
					id: imageID,
158
					uid: uid,
Valerius Mattfeld's avatar
Valerius Mattfeld committed
159
					...tdv
160
161
162
163
				}

				const raw = JSON.stringify(payload)

v.mattfeld's avatar
v.mattfeld committed
164
				const requestOptions: RequestInit = {
Valerius's avatar
Valerius committed
165
166
167
					method: 'POST',
					headers: myHeaders,
					body: raw,
v.mattfeld's avatar
v.mattfeld committed
168
					redirect: 'follow',
Valerius's avatar
Valerius committed
169
170
				};

v.mattfeld's avatar
DB API    
v.mattfeld committed
171
				fetch(`/api/submit`, requestOptions)
172
173
174
175
176
177
178
					.then(response => {
						if (response.status === 400) {
							throw new Error(t('verficationError'));
						}
						response.text()
					})
					.then(() => {
v.mattfeld's avatar
v.mattfeld committed
179
180
181
182
						setAttentionCheck({
							...attentionCheck,
							currentPage: pageNumber - 1,
						})
183
						resetForm()
v.mattfeld's avatar
v.mattfeld committed
184
						refetch()
185
					})
v.mattfeld's avatar
v.mattfeld committed
186
					.catch(fetchError);
v.mattfeld's avatar
v.mattfeld committed
187
				setShouldRefetch(true)
Valerius's avatar
Valerius committed
188
189
190
			}}
			validationSchema={validateSchema}
		>
191
			{({ handleSubmit, handleReset, isSubmitting, values }) => (
Valerius's avatar
Valerius committed
192
193
194
195
				<Box
					as="form"
					px={5}
					onSubmit={handleSubmit as any}
Valerius's avatar
Valerius committed
196
				>
v.mattfeld's avatar
v.mattfeld committed
197
198
199
200
					<RadioGroupControl
						mt={4}
						name="sensitivity"
						label={
v.mattfeld's avatar
v.mattfeld committed
201
							isQuestionOne && isCheck ? t('checkOne') : t('questionOne')
v.mattfeld's avatar
v.mattfeld committed
202
						}>
Valerius's avatar
Valerius committed
203
						<Stack spacing="1">
v.mattfeld's avatar
v.mattfeld committed
204

Valerius's avatar
Valerius committed
205
							<Radio value="1">{t('a11')}</Radio>
v.mattfeld's avatar
v.mattfeld committed
206
							<Radio value="3">{t('a12')}</Radio>
v.mattfeld's avatar
v.mattfeld committed
207
							<Radio value="2">{t('a13')}</Radio>
v.mattfeld's avatar
v.mattfeld committed
208
							<Radio value="4">{t('a14')}</Radio>
v.mattfeld's avatar
v.mattfeld committed
209
210
							{/* TODO: insert seperator */}
							<Radio value="4">{t('a15')}</Radio>
Valerius's avatar
Valerius committed
211
212
						</Stack>
					</RadioGroupControl>
v.mattfeld's avatar
v.mattfeld committed
213
214
215
					<CheckboxContainer mt={4} name="targetDemographic" label={
						!isQuestionOne && isCheck ? t('checkTwo') : t('questionTwo')
					}>
Valerius's avatar
Valerius committed
216
						<Stack spacing="1">
v.mattfeld's avatar
v.mattfeld committed
217
218
219
							<CheckboxControl name="targetDemographic" value="Nobody">{t('a25')}</CheckboxControl>
							<hr />
							<CheckboxControl isDisabled={values.targetDemographic.indexOf('Nobody') !== -1} name="targetDemographic" value="Family">{t('a22')}</CheckboxControl>
220
221
222
							<CheckboxControl isDisabled={values.targetDemographic.indexOf('Nobody') !== -1} name="targetDemographic" value="Friends">{t('a21')}</CheckboxControl>
							<CheckboxControl isDisabled={values.targetDemographic.indexOf('Nobody') !== -1} name="targetDemographic" value="Aquaintance">{t('a24')}</CheckboxControl>
							<CheckboxControl isDisabled={values.targetDemographic.indexOf('Nobody') !== -1} name="targetDemographic" value="Colleagues">{t('a23')}</CheckboxControl>
Valerius's avatar
Valerius committed
223
							<hr />
224
							<CheckboxControl isDisabled={values.targetDemographic.indexOf('Nobody') !== -1} name="targetDemographic" value="Everybody">{t('a26')}</CheckboxControl>
Valerius's avatar
Valerius committed
225
226
						</Stack>
					</CheckboxContainer>
v.mattfeld's avatar
v.mattfeld committed
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
					<Flex my={8} >
						<Box>
							<Button
								size="md"
								rounded="full"
								color="white"
								bg="red.400"
								_hover={{ bg: 'red.300' }}
								type="submit"
								isLoading={isSubmitting}
							>
								{t('submit')}
							</Button>
						</Box>
						<Spacer />
						<Box>
							<Button mr='0' onClick={() => {
								handleReset()
								refetch()
							}}>
								Reset
							</Button>
						</Box>
					</Flex>
Valerius's avatar
Valerius committed
251
252
253
				</Box>
			)}
		</Formik>
Valerius's avatar
Valerius committed
254
255
256
257
	);
}

export default Form