Added styles for entrie form view: form submission and list submissions components. Some minor code refactors
BIN
public/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
public/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
public/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 10 KiB |
9
public/browserconfig.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<browserconfig>
|
||||||
|
<msapplication>
|
||||||
|
<tile>
|
||||||
|
<square150x150logo src="/mstile-150x150.png" />
|
||||||
|
<TileColor>#363645</TileColor>
|
||||||
|
</tile>
|
||||||
|
</msapplication>
|
||||||
|
</browserconfig>
|
BIN
public/favicon-16x16.png
Normal file
After Width: | Height: | Size: 959 B |
BIN
public/favicon-32x32.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
@ -2,15 +2,37 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="QuestionForm is an open source alternative to Google Forms"
|
content="QuestionForm is an open source alternative to Google Forms"
|
||||||
/>
|
/>
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
<link
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="%PUBLIC_URL%/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="%PUBLIC_URL%/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="%PUBLIC_URL%/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/site.webmanifest" />
|
||||||
|
<link
|
||||||
|
rel="mask-icon"
|
||||||
|
href="%PUBLIC_URL%/safari-pinned-tab.svg"
|
||||||
|
color="#6df577"
|
||||||
|
/>
|
||||||
|
<meta name="msapplication-TileColor" content="#6df577" />
|
||||||
|
<meta name="theme-color" content="#363645" />
|
||||||
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/apple-touch-icon.png" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="/index.css" />
|
<link rel="stylesheet" href="/index.css" />
|
||||||
<title>QuestionForm</title>
|
<title>QuestionForm</title>
|
||||||
|
@ -8,18 +8,18 @@
|
|||||||
"type": "image/x-icon"
|
"type": "image/x-icon"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "logo192.png",
|
"src": "android-chrome-192x192.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "192x192"
|
"sizes": "192x192"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "logo512.png",
|
"src": "android-chrome-512x512.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "512x512"
|
"sizes": "512x512"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"start_url": ".",
|
"start_url": "/",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"theme_color": "#000000",
|
"theme_color": "#6df577",
|
||||||
"background_color": "#ffffff"
|
"background_color": "#363645"
|
||||||
}
|
}
|
||||||
|
BIN
public/mstile-150x150.png
Normal file
After Width: | Height: | Size: 11 KiB |
34
public/safari-pinned-tab.svg
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
<metadata>
|
||||||
|
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||||
|
</metadata>
|
||||||
|
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M2265 5108 c-44 -5 -134 -20 -200 -33 -514 -104 -948 -336 -1315
|
||||||
|
-705 -370 -371 -602 -803 -705 -1315 -34 -172 -45 -289 -45 -495 0 -206 11
|
||||||
|
-323 45 -495 103 -512 335 -946 705 -1315 356 -357 770 -584 1263 -694 338
|
||||||
|
-76 753 -76 1097 0 261 58 590 191 802 325 164 104 314 225 458 369 357 356
|
||||||
|
584 770 694 1263 56 251 73 597 41 857 -95 768 -529 1449 -1191 1868 -304 192
|
||||||
|
-708 331 -1076 371 -115 13 -459 12 -573 -1z m690 -902 c173 -37 291 -78 450
|
||||||
|
-156 311 -154 568 -407 724 -715 137 -272 201 -616 172 -925 -39 -400 -186
|
||||||
|
-725 -448 -987 l-82 -83 32 -23 c126 -93 243 -139 367 -142 l85 -2 3 -148 3
|
||||||
|
-148 -103 6 c-216 13 -391 84 -559 228 l-56 48 -29 -20 c-61 -42 -245 -129
|
||||||
|
-344 -164 -57 -19 -160 -48 -229 -62 -117 -25 -144 -27 -371 -27 -229 -1 -253
|
||||||
|
1 -371 26 -547 118 -982 448 -1219 925 -230 464 -222 1033 21 1503 226 437
|
||||||
|
648 751 1155 860 163 34 219 39 454 35 195 -3 241 -7 345 -29z"/>
|
||||||
|
<path d="M2420 3939 c-634 -61 -1129 -503 -1251 -1119 -26 -133 -35 -315 -20
|
||||||
|
-436 12 -94 35 -212 44 -220 2 -2 28 4 58 15 80 27 224 59 341 76 136 19 434
|
||||||
|
19 585 0 436 -56 820 -243 1257 -614 l100 -84 77 81 c175 185 296 418 346 670
|
||||||
|
24 122 24 394 -1 517 -56 282 -178 513 -374 709 -238 237 -534 372 -889 406
|
||||||
|
-120 11 -149 11 -273 -1z"/>
|
||||||
|
<path d="M1715 1973 c-105 -8 -202 -27 -289 -54 -53 -16 -96 -32 -96 -35 0
|
||||||
|
-12 36 -73 72 -122 175 -237 424 -415 717 -512 160 -54 277 -72 451 -73 251
|
||||||
|
-1 449 48 648 159 l64 36 -34 29 c-425 370 -784 533 -1248 569 -130 10 -199
|
||||||
|
10 -285 3z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
19
public/site.webmanifest
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"short_name": "",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/android-chrome-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/android-chrome-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"theme_color": "#363645",
|
||||||
|
"background_color": "#363645",
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import styles from './main.module.css'
|
||||||
|
|
||||||
interface IListsProps {
|
interface IListsProps {
|
||||||
variants: { text: string }[]
|
variants: { text: string }[]
|
||||||
name: string
|
name: string
|
||||||
@ -25,6 +27,7 @@ const Lists: React.FC<IListsProps> = ({ variants, name, type, onChange }) => {
|
|||||||
type={inputType}
|
type={inputType}
|
||||||
name={name}
|
name={name}
|
||||||
value={el.text}
|
value={el.text}
|
||||||
|
className={styles.selector}
|
||||||
/>
|
/>
|
||||||
{el.text}
|
{el.text}
|
||||||
</label>
|
</label>
|
||||||
|
3
src/components/FormLists/main.module.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.selector {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
@ -1,26 +1,73 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1.70667in" height="1.70667in" viewBox="0 0 512 512">
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
<path id="Selection" fill="#6df577" d="M 240.00,0.21
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
C 240.00,0.21 264.00,0.21 264.00,0.21
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
264.00,0.21 276.00,0.91 276.00,0.91
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
300.87,2.65 324.65,8.22 348.00,16.95
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
373.01,26.30 398.45,41.27 419.00,58.25
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
439.59,75.27 457.63,95.40 471.95,118.00
|
width="512"
|
||||||
534.76,217.17 522.88,344.95 443.09,431.00
|
height="512"
|
||||||
423.70,451.91 400.58,469.96 375.00,482.75
|
viewBox="0 0 135.46666 135.46666"
|
||||||
330.78,504.88 295.27,512.57 246.00,512.00
|
version="1.1"
|
||||||
246.00,512.00 236.00,511.09 236.00,511.09
|
id="svg8"
|
||||||
215.63,509.66 195.41,505.80 176.00,499.33
|
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
|
||||||
139.83,487.29 108.97,469.02 81.00,443.09
|
sodipodi:docname="logo.svg">
|
||||||
81.00,443.09 74.17,436.91 74.17,436.91
|
<defs
|
||||||
31.81,393.29 5.16,336.79 0.91,276.00
|
id="defs2" />
|
||||||
0.91,276.00 0.00,264.00 0.00,264.00
|
<sodipodi:namedview
|
||||||
0.00,264.00 0.00,248.00 0.00,248.00
|
id="base"
|
||||||
0.00,248.00 0.91,236.00 0.91,236.00
|
pagecolor="#ffffff"
|
||||||
2.95,206.83 10.08,177.63 22.31,151.00
|
bordercolor="#666666"
|
||||||
51.84,86.67 103.14,38.48 170.00,14.69
|
borderopacity="1.0"
|
||||||
186.39,8.86 203.75,4.61 221.00,2.28
|
inkscape:pageopacity="0.0"
|
||||||
221.00,2.28 240.00,0.21 240.00,0.21 Z" />
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="0.35"
|
||||||
|
inkscape:cx="574.79706"
|
||||||
|
inkscape:cy="560"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="text845"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="1366"
|
||||||
|
inkscape:window-height="744"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
borderlayer="false" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<circle
|
||||||
|
style="fill:#6df577;fill-opacity:1;stroke:#000000;stroke-width:0.184432;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
id="path18"
|
||||||
|
cx="67.73333"
|
||||||
|
cy="67.73333"
|
||||||
|
r="67.641113" />
|
||||||
|
<g
|
||||||
|
aria-label="Q"
|
||||||
|
id="text845"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:116.417px;line-height:1.25;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583">
|
||||||
|
<path
|
||||||
|
d="m 112.61208,104.23006 c -0.6985,0.11642 -1.397,0.11642 -1.74625,0.11642 -3.60893,0 -6.86861,-1.28059 -11.176035,-4.423848 9.313355,-7.916356 14.319295,-19.092388 14.319295,-32.363926 0,-25.495323 -19.55806,-44.354877 -45.984719,-44.354877 -26.659494,0 -46.566801,19.092388 -46.566801,44.587711 0,24.796821 20.605809,44.47129 46.450384,44.47129 9.546194,0 18.510303,-2.56117 25.844574,-7.45068 5.937267,5.47159 10.826782,7.33427 18.859552,7.45068 z M 35.07836,85.603341 c 4.65668,-1.862672 8.032773,-2.444757 13.504372,-2.444757 14.668542,0 25.728157,4.540263 38.184776,15.949129 -5.82085,3.608927 -11.525283,5.238767 -18.859554,5.238767 -8.498442,0 -16.414798,-2.67759 -22.817733,-7.567107 -4.65668,-3.49251 -7.799939,-7.101437 -10.011861,-11.176032 z M 31.469433,78.26907 c -0.931336,-4.307429 -1.280587,-6.519352 -1.280587,-9.779028 0,-21.187894 16.298379,-37.369857 37.602691,-37.369857 21.187894,0 37.486273,15.832712 37.486273,36.438521 0,10.244696 -3.84176,18.859554 -11.758116,26.659493 -4.074596,-3.49251 -7.450688,-6.170101 -10.943198,-8.731275 -9.662611,-6.868603 -20.489392,-10.244696 -32.713178,-10.244696 -6.635768,0 -11.758117,0.814919 -18.393885,3.026842 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:116.417px;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ffffff;stroke-width:0.264583"
|
||||||
|
id="path1581" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 3.8 KiB |
@ -7,6 +7,7 @@ import Lists from '../FormLists'
|
|||||||
import { useForm } from './hooks'
|
import { useForm } from './hooks'
|
||||||
import { QuestionT } from '../../types'
|
import { QuestionT } from '../../types'
|
||||||
import { RefetchQuestionsFT, IFormSubmitMutation } from './types'
|
import { RefetchQuestionsFT, IFormSubmitMutation } from './types'
|
||||||
|
import styles from './main.module.css'
|
||||||
|
|
||||||
interface IQuestionsFormProps {
|
interface IQuestionsFormProps {
|
||||||
formId: number
|
formId: number
|
||||||
@ -62,32 +63,40 @@ const QuestionsForm: React.FC<IQuestionsFormProps> = ({
|
|||||||
{questions.map((el: QuestionT) => {
|
{questions.map((el: QuestionT) => {
|
||||||
if (el.__typename === 'InputQuestion')
|
if (el.__typename === 'InputQuestion')
|
||||||
return (
|
return (
|
||||||
<li key={el.number}>
|
<li key={el.number} className={styles.question}>
|
||||||
<label>
|
<label
|
||||||
|
className={styles.questionTitle}
|
||||||
|
htmlFor={el.title.replace(' ', '_')}
|
||||||
|
>
|
||||||
{el.title}
|
{el.title}
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
|
className={styles.textInput}
|
||||||
|
placeholder="Input"
|
||||||
|
name={el.title.replace(' ', '_')}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
changeAnswer(el.number)(e.currentTarget.value)
|
changeAnswer(el.number)(e.currentTarget.value)
|
||||||
}
|
}
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
</label>
|
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
if (el.__typename === 'ChoisesQuestion')
|
if (el.__typename === 'ChoisesQuestion')
|
||||||
return (
|
return (
|
||||||
<li key={el.number}>
|
<li key={el.number} className={styles.question}>
|
||||||
<label>
|
<label className={styles.questionTitle} htmlFor={el.title}>
|
||||||
{el.title}
|
{el.title}
|
||||||
|
</label>
|
||||||
{el.type === 'SELECT' ? (
|
{el.type === 'SELECT' ? (
|
||||||
<select
|
<select
|
||||||
|
className={styles.select}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const selectValue = el.variants.findIndex(
|
const selectValue = el.variants.findIndex(
|
||||||
(val) => val.text === e.currentTarget.value
|
(val) => val.text === e.currentTarget.value
|
||||||
)
|
)
|
||||||
changeAnswer(el.number)(selectValue)
|
changeAnswer(el.number)(selectValue)
|
||||||
}}
|
}}
|
||||||
name={el.title}
|
name={el.title.replace(' ', '_')}
|
||||||
>
|
>
|
||||||
{el.variants.map((option, index) => (
|
{el.variants.map((option, index) => (
|
||||||
<option key={index}>{option.text}</option>
|
<option key={index}>{option.text}</option>
|
||||||
@ -97,17 +106,20 @@ const QuestionsForm: React.FC<IQuestionsFormProps> = ({
|
|||||||
<Lists
|
<Lists
|
||||||
variants={el.variants}
|
variants={el.variants}
|
||||||
onChange={changeAnswer(el.number)}
|
onChange={changeAnswer(el.number)}
|
||||||
name={el.title}
|
name={el.title.replace(' ', '_')}
|
||||||
type={el.type}
|
type={el.type}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</label>
|
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
return <li>Unknown question type</li>
|
return <li className={styles.question}>Unknown question type</li>
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
{submitLoading ? <p>Uploading...</p> : <input type="submit" />}
|
{submitLoading ? (
|
||||||
|
<p>Uploading...</p>
|
||||||
|
) : (
|
||||||
|
<input className={styles.button} type="submit" />
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
{submitError && <p>{submitError.message}</p>}
|
{submitError && <p>{submitError.message}</p>}
|
||||||
{submitData?.formSubmit.success && <p>Successfully uploaded</p>}
|
{submitData?.formSubmit.success && <p>Successfully uploaded</p>}
|
||||||
|
56
src/components/QuestionsForm/main.module.css
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
.question {
|
||||||
|
list-style: none;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.questionTitle {
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
display: block;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textInput {
|
||||||
|
height: 2.3rem;
|
||||||
|
border-radius: 100vh;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding: 0 0.7rem;
|
||||||
|
width: 100%;
|
||||||
|
border-bottom: 0.15rem var(--containerColor) solid;
|
||||||
|
transition: border 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textInput:focus {
|
||||||
|
border-bottom-width: 0rem;
|
||||||
|
border-top: 0.15rem var(--containerColor) solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select {
|
||||||
|
/* -webkit-appearance: none; */
|
||||||
|
/* -moz-appearance: none; */
|
||||||
|
/* appearance: none; */
|
||||||
|
padding: 0.5rem;
|
||||||
|
background: var(--accentColor);
|
||||||
|
border-radius: 20px;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
height: 2.3rem;
|
||||||
|
border-radius: 100vh;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding: 0 0.7rem;
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: var(--accentColor);
|
||||||
|
color: var(--onAccentFontColor);
|
||||||
|
box-shadow: 0 1px 6px 0 var(--accentShadowColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:active {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
25
src/components/SubmissionsList/empty.svg
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 32.055 32.055" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<path style="fill:#363645" d="M3.968,12.061C1.775,12.061,0,13.835,0,16.027c0,2.192,1.773,3.967,3.968,3.967c2.189,0,3.966-1.772,3.966-3.967
|
||||||
|
C7.934,13.835,6.157,12.061,3.968,12.061z M16.233,12.061c-2.188,0-3.968,1.773-3.968,3.965c0,2.192,1.778,3.967,3.968,3.967
|
||||||
|
s3.97-1.772,3.97-3.967C20.201,13.835,18.423,12.061,16.233,12.061z M28.09,12.061c-2.192,0-3.969,1.774-3.969,3.967
|
||||||
|
c0,2.19,1.774,3.965,3.969,3.965c2.188,0,3.965-1.772,3.965-3.965S30.278,12.061,28.09,12.061z" />
|
||||||
|
</g>
|
||||||
|
<g></g>
|
||||||
|
<g></g>
|
||||||
|
<g></g>
|
||||||
|
<g></g>
|
||||||
|
<g></g>
|
||||||
|
<g></g>
|
||||||
|
<g></g>
|
||||||
|
<g></g>
|
||||||
|
<g></g>
|
||||||
|
<g></g>
|
||||||
|
<g></g>
|
||||||
|
<g></g>
|
||||||
|
<g></g>
|
||||||
|
<g></g>
|
||||||
|
<g></g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 969 B |
@ -7,6 +7,9 @@ import {
|
|||||||
ChoisesQuestion,
|
ChoisesQuestion,
|
||||||
Question,
|
Question,
|
||||||
} from '../../apollo/typeDefs.gen'
|
} from '../../apollo/typeDefs.gen'
|
||||||
|
import emptyIcon from './empty.svg'
|
||||||
|
import styles from './main.module.css'
|
||||||
|
import { getDateCreated } from '../../utils'
|
||||||
|
|
||||||
interface ISubmissionListProps {
|
interface ISubmissionListProps {
|
||||||
submissions: FormSubmission[]
|
submissions: FormSubmission[]
|
||||||
@ -20,27 +23,31 @@ const SubmissionList: React.FC<ISubmissionListProps> = ({
|
|||||||
return submissions.length > 0 ? (
|
return submissions.length > 0 ? (
|
||||||
<ul>
|
<ul>
|
||||||
{submissions.map((submission, submissionIndex) => (
|
{submissions.map((submission, submissionIndex) => (
|
||||||
<li key={submissionIndex}>
|
<li className={styles.listItem} key={submissionIndex}>
|
||||||
<h2>
|
<h2 className={styles.itemHeader}>
|
||||||
User: {submission.user ? submission.user.name : 'No submitter'}
|
{`User ${
|
||||||
|
submission.user ? submission.user.name : 'No submitter'
|
||||||
|
} submitted on ${getDateCreated(submission.date)}:`}
|
||||||
</h2>
|
</h2>
|
||||||
<ul>
|
<ul>
|
||||||
{submission.answers.map(
|
{submission.answers.map(
|
||||||
(answer: InputAnswer | ChoiseAnswer, answerIndex) => (
|
(answer: InputAnswer | ChoiseAnswer, answerIndex) => (
|
||||||
<li key={answerIndex}>
|
<li key={answerIndex}>
|
||||||
<h3>{questions![answerIndex].title}</h3>
|
<div className={styles.question}>
|
||||||
|
<h3>{questions[answerIndex].title}</h3>
|
||||||
{answer.__typename === 'ChoiseAnswer' && (
|
{answer.__typename === 'ChoiseAnswer' && (
|
||||||
<h4>
|
<p>
|
||||||
{
|
{
|
||||||
(questions![answerIndex] as ChoisesQuestion).variants[
|
(questions[answerIndex] as ChoisesQuestion).variants[
|
||||||
answer.userChoise
|
answer.userChoise
|
||||||
].text
|
].text
|
||||||
}
|
}
|
||||||
</h4>
|
</p>
|
||||||
)}
|
)}
|
||||||
{answer.__typename === 'InputAnswer' && (
|
{answer.__typename === 'InputAnswer' && (
|
||||||
<h4>{answer.userInput}</h4>
|
<p>{answer.userInput}</p>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
@ -49,7 +56,12 @@ const SubmissionList: React.FC<ISubmissionListProps> = ({
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
) : (
|
||||||
<p>No submissions yet</p>
|
<div className={styles.emptyContainer}>
|
||||||
|
<div className={styles.emptyIconContainer}>
|
||||||
|
<img className={styles.emptyIcon} src={emptyIcon} alt="Empty" />
|
||||||
|
</div>
|
||||||
|
<h3>No submissions yet :(</h3>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
42
src/components/SubmissionsList/main.module.css
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
.listItem {
|
||||||
|
list-style-type: none;
|
||||||
|
border: 0.1rem var(--containerColor) solid;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 2.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemHeader {
|
||||||
|
text-decoration: underline wavy;
|
||||||
|
text-underline-offset: 0.5rem;
|
||||||
|
padding-bottom: 2.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emptyContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emptyIconContainer {
|
||||||
|
height: 3.5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emptyIcon {
|
||||||
|
height: 7rem;
|
||||||
|
margin-top: -1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (orientation: portrait) {
|
||||||
|
.question {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import {
|
|||||||
InputAnswer,
|
InputAnswer,
|
||||||
ChoiseAnswer,
|
ChoiseAnswer,
|
||||||
Form,
|
Form,
|
||||||
|
User,
|
||||||
} from './apollo/typeDefs.gen'
|
} from './apollo/typeDefs.gen'
|
||||||
|
|
||||||
export type QuestionT = InputQuestion | ChoisesQuestion
|
export type QuestionT = InputQuestion | ChoisesQuestion
|
||||||
@ -13,3 +14,9 @@ export type AnswerT = InputAnswer | ChoiseAnswer
|
|||||||
export interface IFormQuery {
|
export interface IFormQuery {
|
||||||
form: Form
|
form: Form
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type GetDateCreatedFT = (dateString: string) => string
|
||||||
|
|
||||||
|
export interface IUserQuery {
|
||||||
|
user: User
|
||||||
|
}
|
||||||
|
@ -9,7 +9,7 @@ import styles from './main.module.css'
|
|||||||
import QuestionsForm from '../../components/QuestionsForm'
|
import QuestionsForm from '../../components/QuestionsForm'
|
||||||
import { IFormQuery } from '../../types'
|
import { IFormQuery } from '../../types'
|
||||||
import { useId } from './hooks'
|
import { useId } from './hooks'
|
||||||
import { getDateCreated } from './utils'
|
import { getDateCreated } from '../../utils'
|
||||||
|
|
||||||
const DoForm: React.FC = () => {
|
const DoForm: React.FC = () => {
|
||||||
const id = useId()
|
const id = useId()
|
||||||
@ -44,7 +44,7 @@ const DoForm: React.FC = () => {
|
|||||||
<div className={styles.mainTop}></div>
|
<div className={styles.mainTop}></div>
|
||||||
{form.submissions ? (
|
{form.submissions ? (
|
||||||
<>
|
<>
|
||||||
<h1>Submissions</h1>
|
<h1 className={styles.mainHeader}>Submissions</h1>
|
||||||
<SubmissionList
|
<SubmissionList
|
||||||
submissions={form.submissions}
|
submissions={form.submissions}
|
||||||
questions={form.questions!}
|
questions={form.questions!}
|
||||||
@ -52,7 +52,7 @@ const DoForm: React.FC = () => {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<h1>Questions</h1>
|
<h1 className={styles.mainHeader}>Questions</h1>
|
||||||
<QuestionsForm
|
<QuestionsForm
|
||||||
formId={id}
|
formId={id}
|
||||||
questions={data!.form.questions!}
|
questions={data!.form.questions!}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
.header {
|
.header {
|
||||||
padding: 2.3rem;
|
padding: 2.3rem;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
grid-template:
|
grid-template:
|
||||||
'title title' auto
|
'title title' auto
|
||||||
'date author' auto / auto auto;
|
'date author' auto / auto auto;
|
||||||
@ -33,6 +34,8 @@
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
padding: 2.3rem;
|
padding: 2.3rem;
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mainTop {
|
.mainTop {
|
||||||
@ -46,3 +49,7 @@
|
|||||||
left: 0rem;
|
left: 0rem;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mainHeader {
|
||||||
|
padding-bottom: 2.3rem;
|
||||||
|
}
|
||||||
|
@ -1 +0,0 @@
|
|||||||
export type GetDateCreatedFT = (dateString: string) => string
|
|
@ -1,19 +1,22 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useQuery } from '@apollo/client'
|
import { useQuery } from '@apollo/client'
|
||||||
import { generateFromString } from 'generate-avatar'
|
import { generateFromString } from 'generate-avatar'
|
||||||
import { Redirect } from 'react-router-dom'
|
import { Redirect, useHistory } from 'react-router-dom'
|
||||||
|
|
||||||
import Card from '../../components/Card'
|
import Card from '../../components/Card'
|
||||||
import { USER } from '../../apollo'
|
import { USER } from '../../apollo'
|
||||||
import { QueryUserArgs, User } from '../../apollo/typeDefs.gen'
|
import { QueryUserArgs } from '../../apollo/typeDefs.gen'
|
||||||
import styles from './main.module.css'
|
import styles from './main.module.css'
|
||||||
|
import { IUserQuery } from '../../types'
|
||||||
interface IUserQuery {
|
import { logOut } from './utils'
|
||||||
user: User
|
|
||||||
}
|
|
||||||
|
|
||||||
const Home: React.FC = () => {
|
const Home: React.FC = () => {
|
||||||
let { data, error, loading } = useQuery<IUserQuery, QueryUserArgs>(USER)
|
let { data, error, loading, refetch } = useQuery<IUserQuery, QueryUserArgs>(
|
||||||
|
USER
|
||||||
|
)
|
||||||
|
|
||||||
|
const history = useHistory()
|
||||||
|
|
||||||
if (loading) return <p>Loading...</p>
|
if (loading) return <p>Loading...</p>
|
||||||
|
|
||||||
if (error?.message === 'Authorization required')
|
if (error?.message === 'Authorization required')
|
||||||
@ -35,6 +38,12 @@ const Home: React.FC = () => {
|
|||||||
alt="Userpic"
|
alt="Userpic"
|
||||||
/>
|
/>
|
||||||
<h1>{user.name}</h1>
|
<h1>{user.name}</h1>
|
||||||
|
<button
|
||||||
|
className={styles.button}
|
||||||
|
onClick={() => logOut(refetch, history)}
|
||||||
|
>
|
||||||
|
Log out
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -64,6 +64,24 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
height: 2.3rem;
|
||||||
|
border-radius: 100vh;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding: 0 0.7rem;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
background-color: var(--accentColor);
|
||||||
|
color: var(--onAccentFontColor);
|
||||||
|
box-shadow: 0 1px 6px 0 var(--accentShadowColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:active {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
@media (orientation: portrait) {
|
@media (orientation: portrait) {
|
||||||
.container {
|
.container {
|
||||||
grid-template-columns: auto;
|
grid-template-columns: auto;
|
||||||
|
11
src/views/Home/types.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { QueryUserArgs } from '../../apollo/typeDefs.gen'
|
||||||
|
import { ApolloQueryResult } from '@apollo/client'
|
||||||
|
import { IUserQuery } from '../../types'
|
||||||
|
import { History } from 'history'
|
||||||
|
|
||||||
|
export type LogOutFT = (
|
||||||
|
refetch: (
|
||||||
|
variables?: Partial<QueryUserArgs> | undefined
|
||||||
|
) => Promise<ApolloQueryResult<IUserQuery>>,
|
||||||
|
history: History
|
||||||
|
) => void
|
7
src/views/Home/utils.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { LogOutFT } from './types'
|
||||||
|
|
||||||
|
export const logOut: LogOutFT = (refetch, history) => {
|
||||||
|
localStorage.removeItem('token')
|
||||||
|
refetch()
|
||||||
|
history.push('/')
|
||||||
|
}
|
@ -50,13 +50,15 @@
|
|||||||
|
|
||||||
.input {
|
.input {
|
||||||
border-bottom: 0.15rem var(--containerColor) solid !important;
|
border-bottom: 0.15rem var(--containerColor) solid !important;
|
||||||
transition: border-bottom 0.1s;
|
transition: border 0.1s;
|
||||||
}
|
}
|
||||||
.input:focus {
|
.input:focus {
|
||||||
border-bottom-width: 0rem !important;
|
border-bottom-width: 0rem !important;
|
||||||
|
border-top: 0.15rem var(--containerColor) solid !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
|
cursor: pointer;
|
||||||
background-color: var(--accentColor);
|
background-color: var(--accentColor);
|
||||||
color: var(--onAccentFontColor);
|
color: var(--onAccentFontColor);
|
||||||
box-shadow: 0 1px 6px 0 var(--accentShadowColor);
|
box-shadow: 0 1px 6px 0 var(--accentShadowColor);
|
||||||
|