Skip to content

Commit 3f0d7c3

Browse files
committed
ticket
1 parent ba46115 commit 3f0d7c3

File tree

4 files changed

+378
-248
lines changed

4 files changed

+378
-248
lines changed

.env.local.example

+2
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ SG_API=
55
66

77
GOOGLE_FORM_ID=
8+
9+
EMAIL_SECRET=

components/RegistrationForm.js

+246
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
import { useState } from 'react'
2+
3+
import axios from 'axios'
4+
import { setCookie } from 'cookies-next'
5+
6+
import {
7+
useShortAnswerInput,
8+
useLongAnswerInput,
9+
useDropdownInput,
10+
useRadioInput,
11+
useGoogleForm,
12+
GoogleFormProvider,
13+
} from 'react-google-forms-hooks'
14+
15+
import form from '../GoogleForm.json'
16+
17+
export default function RegistrationForm({ setCurrentUser, setStateRegistration }) {
18+
const methods = useGoogleForm({ form })
19+
const [formState, setFormState] = useState('default')
20+
const [errorMsg, setErrorMsg] = useState('')
21+
22+
const onSubmit = async (data) => {
23+
setFormState('loading')
24+
if (!getEmailId() || !getNameId()) {
25+
setFormState('error')
26+
setErrorMsg('Form error. Contact the administrator.')
27+
return false
28+
}
29+
await methods.submitToGoogleForms(data)
30+
axios
31+
.post('/api/confirm', { email: data[getEmailId()], name: data[getNameId()] })
32+
.then((res) => {
33+
if (res.status !== 200) {
34+
setFormState('error')
35+
setErrorMsg(res?.error || 'Error! Please try again.')
36+
} else {
37+
setFormState('default')
38+
setCookie('user', data[getEmailId()], {
39+
maxAge: 60 * 60 * 24 * 180,
40+
SameSite: true,
41+
})
42+
setCurrentUser(data[getEmailId()])
43+
setStateRegistration('ticket')
44+
}
45+
})
46+
.catch((err) => {
47+
setFormState('error')
48+
setErrorMsg(err?.message ?? 'Error! Please try again.')
49+
})
50+
}
51+
52+
return (
53+
<>
54+
<GoogleFormProvider {...methods}>
55+
<form onSubmit={methods.handleSubmit(onSubmit)}>
56+
<Questions />
57+
<button
58+
type="submit"
59+
className="uppercase text-white py-2 px-4 mb-4 block mx-auto rounded-md bg-primary-600 disabled:bg-gray-400"
60+
disabled={formState === 'loading'}
61+
>
62+
{formState === 'loading' ? <>Loading</> : <>Register</>}
63+
</button>
64+
{formState === 'error' ? (
65+
<p className="text-center text-red-600">{errorMsg}</p>
66+
) : (
67+
''
68+
)}
69+
</form>
70+
</GoogleFormProvider>
71+
</>
72+
)
73+
}
74+
75+
const ShortAnswerInput = ({ id }) => {
76+
const { register, label, required, description } = useShortAnswerInput(id)
77+
78+
return (
79+
<>
80+
<label>
81+
<div className="block mb-2 font-bold">{label}</div>
82+
<div>{description}</div>
83+
<input
84+
className="p-4 w-full text-md bg-gray-100 rounded-lg placeholder:text-gray-700 focus:outline-none focus:bg-gray-200"
85+
type={label.toLocaleLowerCase() === 'email' ? 'email' : 'text'}
86+
{...register()}
87+
required={required}
88+
/>
89+
</label>
90+
</>
91+
)
92+
}
93+
94+
const LongAnswerInput = ({ id }) => {
95+
const { register, label, required, description } = useLongAnswerInput(id)
96+
97+
return (
98+
<>
99+
<label>
100+
<div className="block mb-2 font-bold">{label}</div>
101+
<div>{description}</div>
102+
<textarea
103+
className="p-4 text-gray-700 border-0 w-full text-md bg-gray-100 rounded-lg placeholder:text-gray-700 focus:outline-none focus:bg-gray-200"
104+
{...register()}
105+
required={required}
106+
/>
107+
</label>
108+
</>
109+
)
110+
}
111+
112+
const RadioInput = ({ id }) => {
113+
const { options, customOption, error, label, description, required } = useRadioInput(id)
114+
115+
return (
116+
<>
117+
<div className="block mb-2 font-bold">{label}</div>
118+
<div className="text-gray-600 text-sm">{description}</div>
119+
{options.map((o) => (
120+
<div key={o.id} className="flex space-x-2">
121+
<input type="radio" id={o.id} {...o.registerOption()} required={required} />
122+
<label htmlFor={o.id}>{o.label}</label>
123+
</div>
124+
))}
125+
{customOption && (
126+
<div>
127+
<div className="flex space-x-2">
128+
<input type="radio" id={customOption.id} {...customOption.registerOption()} />
129+
<label htmlFor={customOption.id}>Your option</label>
130+
</div>
131+
<input
132+
className="p-4 block border-0 w-full text-md bg-gray-100 rounded-lg placeholder:text-gray-700 focus:outline-none focus:bg-gray-200"
133+
type="text"
134+
{...customOption.registerCustomInput()}
135+
/>
136+
</div>
137+
)}
138+
<div>{error && 'This field is required'}</div>
139+
</>
140+
)
141+
}
142+
143+
const DropdownInput = ({ id }) => {
144+
const { register, options, required, label, description } = useDropdownInput(id)
145+
146+
return (
147+
<div className="w-full">
148+
<label className="block mb-2 font-bold">{label}</label>
149+
<div>{description}</div>
150+
<div
151+
className="appearance-none text-white inline-flex uppercase select-none font-thin relative whitespace-nowrap h-14 w-full outline-none overflow-hidden bg-white"
152+
style={{
153+
minWidth: '160px',
154+
borderRadius: '6px',
155+
}}
156+
>
157+
<select
158+
className="appearance-none border border-black border-solid cursor-default text-black w-full p-4 rounded-lg font-bold bg-white"
159+
style={{
160+
fontSize: '14px',
161+
fontFamily: 'inherit',
162+
wordSpacing: 'normal',
163+
transition: 'border-color 0.2s ease, background-color 0.2s ease',
164+
}}
165+
{...register()}
166+
required={required}
167+
>
168+
{options.map((o) => {
169+
return (
170+
<option className="bg-black text-white" key={o.label} value={o.label}>
171+
{o.label}
172+
</option>
173+
)
174+
})}
175+
</select>
176+
<div
177+
className="flex h-full right-0 pointer-events-none items-center absolute"
178+
style={{
179+
width: '30px',
180+
transition: 'border 0.2s ease 0s',
181+
}}
182+
>
183+
<svg
184+
viewBox="0 0 24 24"
185+
width="18"
186+
height="18"
187+
stroke="black"
188+
strokeWidth="1.5"
189+
strokeLinecap="round"
190+
strokeLinejoin="round"
191+
fill="none"
192+
shapeRendering="geometricPrecision"
193+
>
194+
<path d="M6 9l6 6 6-6" />
195+
</svg>
196+
</div>
197+
</div>
198+
</div>
199+
)
200+
}
201+
202+
const Questions = () => {
203+
return (
204+
<div>
205+
{form.fields.map((field) => {
206+
const { id } = field
207+
208+
let questionInput = null
209+
switch (field.type) {
210+
case 'RADIO':
211+
questionInput = <RadioInput id={id} />
212+
break
213+
case 'SHORT_ANSWER':
214+
questionInput = <ShortAnswerInput id={id} />
215+
break
216+
case 'LONG_ANSWER':
217+
questionInput = <LongAnswerInput id={id} />
218+
break
219+
case 'DROPDOWN':
220+
questionInput = <DropdownInput id={id} />
221+
break
222+
}
223+
224+
if (!questionInput) {
225+
return null
226+
}
227+
228+
return (
229+
<div key={id} className="my-6">
230+
{questionInput}
231+
</div>
232+
)
233+
})}
234+
</div>
235+
)
236+
}
237+
238+
const getEmailId = () => {
239+
return form?.fields?.filter((res) => res.label.toLocaleLowerCase() === 'email')?.[0]?.id
240+
}
241+
242+
const getNameId = () => {
243+
return form?.fields?.filter(
244+
(res) => res.label.toLocaleLowerCase() === 'first name'
245+
)?.[0]?.id
246+
}

components/Ticket.js

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import Image from 'next/image'
2+
import Logo from '../public/logo.svg'
3+
4+
function Ticket({ email }) {
5+
return (
6+
<div className="text-white mb-6 w-full sm:w-2/3">
7+
Ticket for {email}{' '}
8+
<div className="relative">
9+
<div className="hidden lg:flex">
10+
<svg
11+
width="100%"
12+
height="100%"
13+
viewBox="0 0 650 330"
14+
fill="none"
15+
xmlns="http://www.w3.org/2000/svg"
16+
>
17+
<path
18+
fillRule="evenodd"
19+
clipRule="evenodd"
20+
d="M20 0C8.95431 0 0 8.95431 0 20V138C14.9117 138 27 150.088 27 165C27 179.912 14.9117 192 0 192V310C0 321.046 8.9543 330 20 330H630C641.046 330 650 321.046 650 310V192C635.088 192 623 179.912 623 165C623 150.088 635.088 138 650 138V20C650 8.95431 641.046 0 630 0H20Z"
21+
fill="currentColor"
22+
/>
23+
<path
24+
fillRule="evenodd"
25+
clipRule="evenodd"
26+
d="M21 5C12.1634 5 5 12.1634 5 21V133.388C20.2981 135.789 32 149.028 32 165C32 180.972 20.2981 194.211 5 196.612V309C5 317.837 12.1634 325 21 325H629C637.837 325 645 317.837 645 309V196.612C629.702 194.211 618 180.972 618 165C618 149.028 629.702 135.789 645 133.388V21C645 12.1634 637.837 5 629 5H21Z"
27+
fill="#e7edf0"
28+
/>
29+
{/* <path d="M512 5V326" stroke="#444444" strokeDasharray="6 6" /> */}
30+
</svg>
31+
</div>
32+
<div className="lg:hidden flex">
33+
<svg
34+
width="100%"
35+
height="100%"
36+
viewBox="0 0 330 560"
37+
fill="none"
38+
xmlns="http://www.w3.org/2000/svg"
39+
>
40+
<path
41+
fillRule="evenodd"
42+
clipRule="evenodd"
43+
d="M2.9193e-06 540C3.40212e-06 551.046 8.95431 560 20 560L138 560C138 545.088 150.088 533 165 533C179.912 533 192 545.088 192 560L310 560C321.046 560 330 551.046 330 540L330 20C330 8.95427 321.046 -1.40334e-05 310 -1.35505e-05L192 -8.39259e-06C192 14.9117 179.912 27 165 27C150.088 27 138 14.9117 138 -6.03217e-06L20 -8.74228e-07C8.95428 -3.91405e-07 -2.41646e-05 8.95428 -2.36041e-05 20L2.9193e-06 540Z"
44+
fill="currentColor"
45+
/>
46+
<path
47+
fillRule="evenodd"
48+
clipRule="evenodd"
49+
d="M5 539C5 547.837 12.1634 555 21 555L133.388 555C135.789 539.702 149.028 528 165 528C180.972 528 194.211 539.702 196.612 555L309 555C317.837 555 325 547.837 325 539L325 21C325 12.1634 317.837 4.99999 309 4.99999L196.612 4.99999C194.211 20.2981 180.972 32 165 32C149.028 32 135.789 20.2982 133.388 4.99999L21 5C12.1634 5 4.99998 12.1635 4.99998 21L5 539Z"
50+
fill="#e7edf0"
51+
/>
52+
{/* <path d="M326 446H5" stroke="#444444" strokeDasharray="6 6" /> */}
53+
</svg>
54+
</div>
55+
<div className="absolute top-[10%] left-0 right-0 text-center w-full lg:left-[10%] lg:right-auto lg:w-[10%]">
56+
<Image
57+
src={Logo}
58+
alt="Open Components Ecosystem"
59+
width="120"
60+
height="176"
61+
className="w-12 lg:w-full"
62+
/>
63+
</div>
64+
<div className="absolute top-[50%] sm:top-[40%] lg:top-[18%] left-0 lg:left-[26%] right-0 lg:right-auto text-center lg:text-left text-xl md:text-4xl lg:text-4xl xl:text-5xl 2xl:text-6xl font-bold text-text-800">
65+
OCE Hackathon 2023
66+
</div>
67+
<div className="absolute top-[60%] lg:top-[45%] left-0 right-0 text-center">
68+
<div className="uppercase text-base md:text-lg my-6 text-primary-600">
69+
<b>February&nbsp;13&nbsp;-&nbsp;17,&nbsp;2023</b> |&nbsp;Learnathon
70+
<br />
71+
<b>February&nbsp;20&nbsp;-&nbsp;24,&nbsp;2023</b> |&nbsp;Hackathon
72+
</div>
73+
</div>
74+
<div className="absolute bottom-[10%] left-0 right-0 text-center text-xl lg:text-2xl text-text-800">
75+
{email}
76+
</div>
77+
</div>
78+
</div>
79+
)
80+
}
81+
82+
export default Ticket

0 commit comments

Comments
 (0)