App.tsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import React, { useState } from 'react';
  2. import { Container, Fade, Button, Form, FormGroup, Input, Label, Jumbotron } from 'reactstrap';
  3. import { Link } from 'react-router-dom';
  4. import PersonalInformation, { FormData } from './PersonalInformation';
  5. import DrawCanvas, { CanvasData } from './DrawCanvas';
  6. type SectionComponentProps = {
  7. changePage: () => void
  8. }
  9. export const ClosureNotice = () => (
  10. <Container style={{
  11. height: '100vh',
  12. display: 'flex',
  13. flexDirection: 'column',
  14. justifyContent: 'center'
  15. }}>
  16. <Jumbotron>
  17. <h2>Voices of Merseyside</h2>
  18. <p>
  19. Thank you for your interest in the Voices of Merseyside project. Unfortunately, the survey phase of
  20. this study has now been closed as the target number of responses has now been met.
  21. </p>
  22. <p>
  23. I am incredibly grateful to everyone who has taken an interest in this project.
  24. </p>
  25. </Jumbotron>
  26. </Container>
  27. );
  28. const Introduction: React.FunctionComponent<SectionComponentProps> = ({ changePage }) => (
  29. <Fade tag="section" id="vom-intro">
  30. <h2 className="mb-4">Voices of Merseyside</h2>
  31. <div className="mb-4">
  32. <h4>About</h4>
  33. <p className="">
  34. Voices of Merseyside seeks to explore the rich and diverse accents, dialects, and
  35. identities of Merseyside. We invite you to help us draw a truly representative map of
  36. the linguistic landscape of Merseyside as experienced by you, the Voices of
  37. Merseyside.
  38. </p>
  39. </div>
  40. <div className="mb-3">
  41. <h4>Online Mapping</h4>
  42. <p>
  43. Residents of Merseyside are asked to draw a digital map of
  44. where they believe different accents in Merseyside can be found. Then, residents will
  45. be asked to provide information about these accents which will allow the researcher
  46. to monitor language attitudes.
  47. </p>
  48. </div>
  49. <div className="mb-3"
  50. >
  51. <h4>The researcher</h4>
  52. <p className="text-left">
  53. Voices of Merseyside is an MA thesis project created by Tanya Parry, a fellow Merseysider now living in Amsterdam. If you wish to get in touch, please email: <a className="text-decoration-none" href="mailto:voicesofmerseyside@gmail.com"> voicesofmerseyside@gmail.com</a>
  54. </p>
  55. </div>
  56. <div className="mb-3">
  57. <p className="text-muted font-italic">
  58. For more information about the voices of Merseyside project, please click this <Link to="/more">link</Link>
  59. </p>
  60. </div>
  61. <Button type="button" outline onClick={() => changePage()} color="info">Let's start!</Button>
  62. </Fade>
  63. );
  64. const TermsAndConditions: React.FunctionComponent<SectionComponentProps> = ({ changePage }) => {
  65. const [ agreement, setAgreement ] = useState(false);
  66. return (
  67. <Fade tag="section" id="vom-terms">
  68. <Form>
  69. <h2 className="mb-4">Terms of agreement</h2>
  70. <div className="mb-4">
  71. <p className="">
  72. I understand that the responses I give are anonymous (unless I have agreed to be contacted by the
  73. researcher – an option given at the end of the survey). Therefore, it is not possible to retract my
  74. participation once the map and accompanying responses have been submitted.
  75. </p>
  76. <p className="">
  77. I am aware that this study is part of an MA thesis for the Universiteit van Amsterdam, currently
  78. being undertaken by the researcher, Tanya Parry. I understand that my responses will be stored and
  79. used by the researcher and her team. However, I allow for my responses (which are anonymous
  80. unless I choose to be contacted by the researcher) to be shared with other academics, if the
  81. researcher so chooses.
  82. </p>
  83. <p>
  84. To read the terms and conditions specified by the Universiteit van Amsterdam, please click the following <Link to="/terms">link</Link>
  85. </p>
  86. </div>
  87. <FormGroup className="mb-4" check>
  88. <Input type="checkbox" id="vom-terms-check" checked={agreement} onChange={ e => setAgreement(e.target.checked)}/>
  89. <Label for="vom-terms-check" check>I give my permission to participate in this research project.</Label>
  90. </FormGroup>
  91. <Button type="button" disabled={!agreement} color={agreement ? 'info' : 'secondary'} onClick={ () => changePage() }>Next</Button>
  92. </Form>
  93. </Fade>
  94. );
  95. }
  96. const FinishSection: React.FunctionComponent = () => {
  97. return (
  98. <Fade tag="section" id="vom-finish">
  99. <h2>Thank you!</h2>
  100. </Fade>
  101. )
  102. };
  103. type AnswersData = {
  104. personalInformation: FormData,
  105. canvas: CanvasData[],
  106. canvasSize: {
  107. width: number,
  108. height: number,
  109. },
  110. email: string,
  111. };
  112. const PAGES = [0, 1, 2, 3, 4];
  113. const App = () => {
  114. const [ currentPage, setCurrentPage ] = useState(0);
  115. const [ exit, setExit ] = useState(false);
  116. const [ answers, saveAnswers ] = useState<AnswersData>({
  117. personalInformation: {
  118. age: '',
  119. gender: '',
  120. genderCustom: '',
  121. levelEducation: [],
  122. birthPlace: '',
  123. currentPlace: '',
  124. nonNative: '',
  125. },
  126. canvas: [],
  127. canvasSize: {
  128. width: 0,
  129. height: 0
  130. },
  131. email: ''
  132. });
  133. const changePage = () => {
  134. setCurrentPage(currentPage + 1);
  135. window.scrollTo(0, 0);
  136. }
  137. const uploadData = (result: AnswersData) => {
  138. const _data = {
  139. ...result,
  140. canvas: result.canvas.map(data => ({
  141. form: data.form,
  142. path: data.path.exportJSON()
  143. })),
  144. canvasSize: result.canvasSize,
  145. };
  146. const postRequest = new XMLHttpRequest();
  147. postRequest.open('POST', '/backend/', true);
  148. postRequest.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
  149. postRequest.send(JSON.stringify(_data));
  150. }
  151. const sections = [
  152. Introduction({
  153. changePage: changePage
  154. }),
  155. TermsAndConditions({
  156. changePage: changePage
  157. }),
  158. PersonalInformation({
  159. closeApp: () => setExit(true),
  160. saveData: ({ personalInformation, email }: {
  161. personalInformation: FormData,
  162. email: string
  163. }) => {
  164. saveAnswers({
  165. ...answers,
  166. personalInformation: personalInformation,
  167. email,
  168. });
  169. changePage()
  170. }
  171. }),
  172. DrawCanvas({
  173. saveData: (data) => {
  174. uploadData({
  175. ...answers,
  176. canvas: data.data,
  177. canvasSize: data.canvas
  178. })
  179. changePage()
  180. }
  181. }),
  182. FinishSection({})
  183. ];
  184. return (
  185. <>
  186. <div id="vom-progress-bar">
  187. {
  188. PAGES.map(page => (
  189. <div key={page} className={`vom-progress-page ${page <= currentPage ? 'progress-done' : ''}`}></div>
  190. ))
  191. }
  192. </div>
  193. <div className="App">
  194. <Container>
  195. {
  196. !exit ? (
  197. sections[currentPage]
  198. ) : (
  199. <Fade in={exit} tag="section">
  200. <h4>
  201. Thank you for your interest, but permission from your parent/guardian is required before you are able to participate.
  202. </h4>
  203. </Fade>
  204. )
  205. }
  206. </Container>
  207. </div>
  208. </>
  209. )
  210. };
  211. export default App;