· 6 years ago · Sep 04, 2019, 06:22 AM
1import React from "react";
2import qs from "query-string";
3import { connect } from "react-redux";
4import { bindActionCreators } from "redux";
5import { Button, Row, Col, Form } from "react-bootstrap";
6import { fetchData } from '../actions/fetchActions';
7import {api, service} from '../constants/actionConstants';
8import ProgressBar from "react-bootstrap/ProgressBar";
9
10import loadingImage from "../images/loading.svg";
11import flamencoDone from "../images/flamenco-done.svg";
12
13import '../css/RegisterRetriever.css';
14import moment from 'moment';
15import 'moment/locale/nb';
16import Modal from "react-bootstrap/Modal";
17
18interface Props {
19 fetchData?: any,
20 loading?: boolean,
21 map?: any,
22 relations: any,
23 subGroups?: any,
24 location?: any,
25 asso?: any,
26 retriever?: any,
27 phoneNr?: string,
28 fullName?: string | undefined,
29 address?: string | undefined,
30 town?: string | undefined,
31 zip?: string,
32 showValidateAddressAlert?: boolean,
33}
34
35interface State {
36 validRelation: boolean,
37 totalSteps: number,
38 currentStep: number,
39 tof?: boolean,
40 [key: string]: any,
41 team?: string | undefined,
42 selectedTeam?: any,
43 existsIn1881Text: string,
44 phoneNr?: string,
45 fullName?: string | undefined,
46 address?: string | undefined,
47 town?: string | undefined,
48 zip?: string,
49 norkartAddress?: any,
50 showValidateAddressAlert?: boolean,
51}
52
53class RegisterRetriever extends React.PureComponent<Props, State> {
54
55 constructor(props: any) {
56 super(props);
57
58 this.state = {
59 validRelation: false,
60 totalSteps: 3,
61 currentStep: 1,
62 isRegistered: false,
63 tof: false,
64 selectedTeam: null,
65 existsIn1881Text: 'Endre informasjonen dersom noe ikke stemmer:',
66 phoneNr: '',
67 fullName: '',
68 address: '',
69 zip: '',
70 town: '',
71 norkartAddress: null,
72 searchPhoneNr: false,
73 showValidateAddressAlert: false,
74 };
75
76 this.handleChange = this.handleChange.bind(this);
77 this.changeStep = this.changeStep.bind(this);
78 this.handleRadioChange = this.handleRadioChange.bind(this);
79 this.search1881 = this.search1881.bind(this);
80 }
81
82 componentDidMount() {
83 var queryAssoString = qs.parse(this.props.location.search).associationId;
84 var queryRetriverString = qs.parse(this.props.location.search).retrieverId;
85 if (queryAssoString != null && queryRetriverString) {
86 var assoId = parseInt(queryAssoString as string);
87 var retrieverId = parseInt(queryRetriverString as string);
88 this.props.fetchData(api.azureApi + service.association + "/" + assoId + "/assoSubGroups", "subGroups");
89 this.props.fetchData(api.azureApi + service.association + "/association/" + assoId, "asso");
90 this.props.fetchData(api.azureApi + service.association + "/retriever/" + retrieverId, "retriever");
91 this.props.fetchData(api.azureApi + service.association + "/retrieverGroupRelation/getAll/retriever=" + retrieverId, "relations");
92 }
93 }
94
95 componentDidUpdate(prevProps: any) {
96 if (this.props.relations !== prevProps.relations) {
97 if (this.props.relations[0].asso_sub_group_id !== null) {
98 this.setState({
99 isRegistered: true
100 })
101 }
102 }
103
104 if (this.props.relations && this.props.asso) {
105 this.setState({
106 validRelation: this.props.relations[0].association_id === this.props.asso.id
107 })
108 }
109
110 if (this.props.retriever !== prevProps.retriever && this.props.retriever.status !== 500) {
111 const phoneNrString = this.props.retriever.phoneNr.toString();
112 this.setState({
113 phoneNr: phoneNrString
114 });
115 }
116 }
117
118 handleChange = (event: any) => {
119 const target = event.target;
120 const value = target.type === "checkbox" ? target.checked : target.value;
121 const name = target.name;
122
123 this.setState({
124 [name]: value
125 });
126
127 if (target.name === "phoneNr" && target.value.length !== 8) {
128 this.setState({ searchPhoneNr: false })
129 }
130
131 if (target.name === "zip" && target.value.length === 4) {
132 fetch("https://api.bring.com/shippingguide/api/postalCode.json?clientUrl=http://www.hentepant.no/&pnr=" + target.value)
133 .then(response => response.json())
134 .then(body => this.setState({
135 town: body.result
136 }))
137 }
138 };
139
140 changeStep = (direction: any) => {
141 if (direction === "next") {
142 if (this.state.currentStep === 3) this.updateRetriever();
143 this.setState({
144 currentStep: this.state.currentStep + 1
145 });
146 } else if (direction === "prev") {
147 this.setState({
148 currentStep: this.state.currentStep - 1
149 })
150 }
151
152 window.scrollTo(0, 0);
153 };
154
155 handleRadioChange(group: any) {
156 this.setState({
157 selectedTeam: group
158 })
159 }
160
161 search1881() {
162 this.setState({
163 searchPhoneNr: true
164 });
165
166 fetch(api.azureApi + service.association + "/retrieverAddress/getByPhoneNumber/" + this.state.phoneNr)
167 .then(response => response.json())
168 .then(data => {
169 const response = data.contacts[0];
170 const address = response.geography.address;
171 this.setState({
172 existsIn1881Text: "Endre informasjonen dersom noe ikke stemmer:",
173 fullName: response.name,
174 address: address.street + " " + address.houseNumber + (address.entrance ? address.entrance : ""),
175 zip: address.postCode,
176 town: address.postArea.toUpperCase()
177 })
178 }).catch(error => {
179 console.log("Does not exist in 1881: " + error);
180 this.setState({
181 existsIn1881Text: "Fyll ut din informasjon:",
182 fullName: '',
183 address: '',
184 zip: '',
185 town: ''
186 });
187 })
188 }
189
190 handleErrors(response: any) {
191 if (!response.ok) throw Error(response);
192 return response;
193 }
194
195 // TODO: Change service to retrieverregistration
196 updateRetriever = () => {
197 console.log("Updating retriever...");
198 const url = api.azureApi + service.association + "/retriever/edit/" + this.props.retriever.id;
199 const { phoneNr, fullName } = this.state;
200
201 const data = {
202 phoneNr: parseInt(phoneNr!),
203 name: fullName,
204 retrievalCount: 0,
205 active: true
206 };
207
208 console.table(data);
209
210 fetch(url, {
211 method: 'PUT',
212 body: JSON.stringify(data),
213 headers: { 'Content-Type': 'application/json' }
214 }).then(response => response.json())
215 .catch(error => console.table(error))
216 .then(response => {
217 console.table(response);
218 console.log("Updated retriever!");
219 this.updateGroupRelations();
220 this.createRetrieverAddress();
221 });
222 };
223
224 updateGroupRelations = () => {
225 console.log("Updating new group relations...");
226 const regUrl = api.azureApi + service.retrieverReg + "/retriever";
227
228 fetch(regUrl + "/addgroup/" + this.state.selectedTeam.id, {
229 method: 'PUT',
230 body: JSON.stringify(this.props.retriever),
231 headers: { 'Content-Type': 'application/json' }
232 }).then(response => this.handleErrors(response))
233 .then(() => { console.log("Successfully updated the group relation!") })
234 .catch(error => console.log("Could not update the group relation: " + JSON.stringify(error)));
235 };
236
237 createRetrieverAddress = () => {
238 console.log("Creating retriever address...");
239
240 fetch(api.azureApi + service.retrieverReg + "/retrieverAddress/post/" + this.props.retriever.id, {
241 method: 'POST',
242 body: JSON.stringify(this.state.norkartAddress),
243 headers: { 'Content-Type': 'application/json' }
244 }).then(response => response.json())
245 .catch(error => console.log("Could not post address to the database... " + error))
246 .then(() => console.log("Successfully posted address to the database!"))
247 };
248
249 validateAddress = () => {
250 console.log("Searching for given address in Norkart API");
251 const { address, zip, town } = this.state;
252 const fullAddress = address + " " + zip + " " + town;
253 const url = "https://www.webatlas.no/WAAPI-FritekstSok/search/kommunecustom?Targets=gateadresse&Query=" + fullAddress + "&api_key=" + api.norKartApiKey;
254
255 fetch(url, { headers: { 'Content-Type': 'application/json' }})
256 .then(response => response.json())
257 .then(body => {
258 const result = body.SearchResults[0].Source;
259 const resultAddress = result.GateNavn + " " + result.AdresseHusNummer + result.AdresseBokstav;
260
261 if (resultAddress.toUpperCase() === address!.toUpperCase() && result.PostNummer === zip && result.PostSted.toUpperCase() === town!.toUpperCase()) {
262 console.log("Norkart found the correct address!");
263 this.changeStep("next");
264 } else {
265 console.log("Not a valid address");
266 this.setState({ showValidateAddressAlert: true });
267 }
268
269 this.setState({ norkartAddress: body });
270 });
271 };
272
273 render() {
274 let closeValidateAddress = () => this.setState({ showValidateAddressAlert: false });
275
276 return this.props.loading ? (
277 <Row>
278 <Col className={"d-flex justify-content-center"}>
279 <img src={loadingImage} alt={"Loading registration form..."}/>
280 </Col>
281 </Row>
282 ) : (
283 this.props.subGroups && this.state.validRelation && this.props.asso && !this.props.asso.error ? (
284 <React.Fragment>
285 <Row id={"register-retriever"}>
286 <Col lg={{span: 8, offset: 2}}
287 md={{span: 8, offset: 2}}
288 sm={{span: 12}}
289 xs={{span: 12}}>
290
291 {/* Don't show the registration form if the user is already registered. */}
292 { this.state.isRegistered ? (
293 <AlreadyRegistered asso={this.props.asso}
294 relations={this.props.relations}/>
295 ) : (
296 <div>
297 { this.state.currentStep !== 4 &&
298 <Row>
299 <Col lg={{span: 8, offset: 2}}
300 md={{span: 8, offset: 2}}
301 sm={{span: 10, offset: 1}}
302 xs={{span: 10, offset: 1}}>
303 <p className={"progress-header"}>Steg {this.state.currentStep} / {this.state.totalSteps}</p>
304 <ProgressBar now={(this.state.currentStep / this.state.totalSteps) * 100}/>
305 </Col>
306 </Row>
307 }
308 <br/><br/>
309 <Row>
310 <Col lg={{span: 8, offset: 2}}
311 md={{span: 8, offset: 2}}
312 sm={{span: 10, offset: 1}}
313 xs={{span: 10, offset: 1}}>
314 { this.state.currentStep !== 4 ? (
315 <h2 className="primary-color center">Registrer ny henter</h2>
316 ) : (
317 <h2 className="primary-color center">Ferdig!</h2>
318 )}
319 </Col>
320 </Row>
321
322 {(() => {
323 switch (this.state.currentStep) {
324 case 1:
325 return <Step1 changeStep={this.changeStep}
326 handleChange={this.handleChange}
327 association={this.props.asso}
328 tof={this.state.tof}/>;
329 case 2:
330 return <Step2 validateAddress={this.validateAddress}
331 changeStep={this.changeStep}
332 handleChange={this.handleChange}
333 search1881={this.search1881}
334 phoneNr={this.state.phoneNr}
335 searchPhoneNr={this.state.searchPhoneNr}
336 existsIn1881Text={this.state.existsIn1881Text}
337 fullName={this.state.fullName}
338 address={this.state.address}
339 town={this.state.town}
340 zip={this.state.zip}
341 norkartAddress={this.state.norkartAddress}
342 showValidateAddressAlert={this.state.showValidateAddressAlert}
343 onHide={closeValidateAddress}
344 createAddress={this.createRetrieverAddress} />;
345 case 3:
346 return <Step3 changeStep={this.changeStep}
347 handleChange={this.handleChange}
348 handleRadioChange={this.handleRadioChange}
349 selectedTeam={this.state.selectedTeam}
350 subGroups={this.props.subGroups}/>;
351 case 4:
352 return <Step4 changeStep={this.changeStep}
353 association={this.props.asso}/>;
354 default:
355 }
356 return <p>Kan ikke laste inn siden.</p>;
357 })()}
358 </div>
359 )}
360 </Col>
361 </Row>
362 </React.Fragment>
363 ) : (
364 <div>
365 <h2 className="primary">Du har ikke tilgang på denne siden!</h2>
366 </div>
367 )
368 )
369 }
370}
371
372const AlreadyRegistered = (props: any) => {
373
374 return (
375 <div>
376 <div className={"center"}>
377 <h2 className={"primary-color"}>Du er allerede registrert!</h2>
378 <p>Supert! Alt er i skjønneste orden.</p>
379 <p>Du er satt bakerst i hentekøen hos <b>{props.asso.name}</b>, og er registrert på følgende lag:</p>
380 </div>
381 <Row className={"justify-content-xs-center justify-content-sm-center justify-content-md-center justify-content-lg-center justify-content-xl-center"}>
382 <Col xs={"auto"} sm={"auto"} md={"auto"} lg={"auto"} xl={"auto"}>
383 <div className={"d-flex justify-content-center"} id={"registered-subgroups-container"}>
384 <ul>
385 { props.relations.map((r:any) =>
386 <li>{r.asso_sub_group_id.title}</li>
387 )}
388 </ul>
389 </div>
390 </Col>
391 </Row>
392 <br/><br/>
393 <p className={"center"}>Kontakt din foreningsansvarlig dersom noe skulle være feil.</p>
394 </div>
395 )
396};
397
398function Step1(props: any) {
399 return(
400 <Form>
401 <Form.Group as={Row} className={"formTermsOfService"}>
402 <Col lg={{span: 8, offset: 2}}
403 md={{span: 8, offset: 2}}
404 sm={{span: 12}}
405 xs={{span: 12}}>
406 <Row>
407 <Col className={"formTermsOfService"}>
408 <Form.Check id={"tof"}
409 name="tof"
410 type="checkbox"
411 checked={props.tof}
412 onChange={props.handleChange}
413 label={"Jeg samtykker at HentePant og registrerte foreninger kan bruke min informasjon til å kontakte meg."}
414 />
415 </Col>
416 </Row>
417 </Col>
418 </Form.Group>
419 <br/>
420 { props.tof === true &&
421 <Row>
422 <Col lg={{span: 8, offset: 2}}
423 md={{span: 8, offset: 2}}
424 sm={{span: 10, offset: 1}}
425 xs={{span: 10, offset: 1}}
426 className={"d-flex justify-content-center"}>
427 <Button block onClick={() => props.changeStep("next")}>Neste</Button>
428 </Col>
429 </Row>
430 }
431 </Form>
432 )
433}
434
435function Step2(props: any) {
436
437 return (
438 <Form>
439 <Form.Group as={Row} controlId="formGridName">
440 <Col xl={{span: 4}}
441 lg={{span: 6, offset: 3}}
442 md={{span: 8, offset: 2}}
443 sm={{span: 10, offset: 1}}
444 xs={{span: 10, offset: 1}}>
445 <p>Bekreft telefonnummer du ønsker å bruke:</p>
446 <Form.Control name="phoneNr" type="text" placeholder="Telefonnummer..." value={props.phoneNr} onChange={props.handleChange} />
447
448 { props.phoneNr && props.phoneNr.length === 8 &&
449 <div className={"d-flex justify-content-center component-margin-top"}>
450 <Button block
451 variant={"primary"}
452 onClick={props.search1881}>OK</Button>
453 </div>
454 }
455 </Col>
456 </Form.Group>
457 { props.searchPhoneNr &&
458 <div>
459 <Form.Group as={Row} controlId="formGridName">
460 <Col lg={{span: 6, offset: 3}}
461 md={{span: 8, offset: 2}}
462 sm={{span: 10, offset: 1}}
463 xs={{span: 10, offset: 1}}>
464 <p>{ props.existsIn1881Text }</p>
465 <Form.Control name="fullName" type="text" placeholder="Fullt navn..." value={props.fullName} onChange={props.handleChange} />
466 </Col>
467 </Form.Group>
468 <Form.Group as={Row} controlId="formGridAddress">
469 <Col lg={{span: 6, offset: 3}}
470 md={{span: 8, offset: 2}}
471 sm={{span: 10, offset: 1}}
472 xs={{span: 10, offset: 1}}>
473 <Form.Control name="address" type="text" placeholder="Gatenavn..." value={props.address} onChange={props.handleChange} />
474 </Col>
475 </Form.Group>
476 <Form.Group as={Row} controlId="formGridArea">
477 <Col lg={{span: 3, offset: 3}}
478 md={{span: 4, offset: 2}}
479 sm={{span: 5, offset: 1}}
480 xs={{span: 5, offset: 1}}>
481 <Form.Control name="zip" type="text" placeholder="Postnummer..." value={props.zip} onChange={props.handleChange} />
482 </Col>
483 <Col lg={{span: 3}}
484 md={{span: 4}}
485 sm={{span: 5}}
486 xs={{span: 5}}>
487 <Form.Control name="town" type="text" disabled value={props.town}/>
488 </Col>
489 </Form.Group>
490 <Row>
491 <Col lg={{span: 3, offset: 3}}
492 md={{span: 4, offset: 2}}
493 sm={{span: 5, offset: 1}}
494 xs={{span: 5, offset: 1}}>
495 <div className={"d-flex justify-content-end"}>
496 <Button block
497 variant={"secondary"}
498 onClick={() => props.changeStep("prev")}>Gå tilbake</Button>
499 </div>
500 </Col>
501 <Col lg={{span: 3}}
502 md={{span: 4}}
503 sm={{span: 5}}
504 xs={{span: 5}}>
505 { props.fullName.length > 4 && props.address.length > 5 && props.zip.length === 4 && props.town !== "Ugyldig postnummer" &&
506 <Button block onClick={() => props.validateAddress()}>Neste</Button>
507 }
508 </Col>
509 </Row>
510 </div>
511 }
512
513 { props.norkartAddress &&
514 <ValidateAddress show={props.showValidateAddressAlert}
515 onHide={props.onHide}
516 address={props.norkartAddress}
517 changeStep={props.changeStep}/>
518 }
519 </Form>
520 )
521}
522
523const ValidateAddress = (props: any) => {
524 const address = props.address.SearchResults[0].Source;
525
526 return(
527 <Modal {...props} size={"sm"} id={"validate-address-modal"}>
528 <Modal.Header><Modal.Title>Stemmer adressen?</Modal.Title></Modal.Header>
529 <Modal.Body>
530 <p>{address.GateNavn} {address.AdresseHusNummer}{address.AdresseBokstav}, {address.PostNummer} {address.KommuneNavn}</p>
531 <Row>
532 <Col xs={{ span: 4, offset: 1 }} className={"no-padding"}>
533 <Button variant={"danger"} block onClick={props.onHide}>Nei</Button>
534 </Col>
535 <Col xs={{ span: 4, offset: 2 }} className={"no-padding"}>
536 <Button block onClick={() => props.changeStep("next")}>Ja</Button>
537 </Col>
538 </Row>
539 </Modal.Body>
540 </Modal>
541 )
542};
543
544function Step3 (props: any) {
545
546 return (
547 <Form>
548 <Form.Group as={Row} controlId="formGridTeam">
549 <Col lg={{span: 6, offset: 3}}
550 md={{span: 8, offset: 2}}
551 sm={{span: 10, offset: 1}}
552 xs={{span: 12}}>
553
554 <p className={"center"}>Velg hvilke lag du vil hente for:</p>
555
556 <Row>
557 <Col lg={{span: 4, offset: 4}}
558 md={{span: 6, offset: 3}}
559 sm={{span: 10, offset: 1}}
560 xs={{span: 10, offset: 1}}>
561 { props.subGroups.map((group: any) =>
562 <Form.Check type={"radio"}
563 id={group.title}
564 label={group.title}
565 key={group.id.toString()}
566 name={"subgroup"}
567 checked={group === props.selectedTeam}
568 onChange={() => props.handleRadioChange(group)}
569 />
570 )}
571 </Col>
572 </Row>
573 <br/>
574 <Row>
575 <Col lg={{span: 6}}
576 md={{span: 6}}
577 sm={{span: 6}}
578 xs={{span: 6}}>
579 <div className={"d-flex justify-content-end"}>
580 <Button block
581 variant={"secondary"}
582 onClick={() => props.changeStep("prev")}>Gå tilbake</Button>
583 </div>
584 </Col>
585 <Col lg={{span: 6}}
586 md={{span: 6}}
587 sm={{span: 6}}
588 xs={{span: 6}}>
589 { props.selectedTeam &&
590 <Button block onClick={() => props.changeStep("next")}>Registrer!</Button>
591 }
592 </Col>
593 </Row>
594 </Col>
595 </Form.Group>
596
597 </Form>
598 )
599}
600
601function Step4(props: any) {
602 return (
603 <div>
604 <Row>
605 <Col lg={{span: 4, offset: 4}}
606 md={{span: 6, offset: 3}}
607 sm={{span: 8, offset: 2}}
608 xs={{span: 8, offset: 2}}>
609 <p>Du er nå registrert som henter for {props.association.name}. Neste gang det er din tur å hente pant vil du få en forespørsel på SMS.</p>
610
611 <br/><br/>
612
613 <div className={"d-flex justify-content-center"}>
614 <img src={flamencoDone} alt={"Registration done!"}/>
615 </div>
616 </Col>
617 </Row>
618 </div>
619 )
620}
621
622
623const mapStateToProps = ({ fetchData } : {fetchData: any}) => {
624 return {
625 ...fetchData
626 };
627};
628
629const mapDispatchToProps = (dispatch: any) => {
630 return {
631 ...bindActionCreators({ fetchData }, dispatch)
632 };
633
634};
635
636export default connect(
637 mapStateToProps,
638 mapDispatchToProps
639)(RegisterRetriever);