Files
koodiklinikka.fi/components/membership/MembershipInfoForm.tsx
Olavi Haapala f4474523ad Chore/convert to typescript (#60)
* Convert to using TypeScript

Next.js v.9 allows converting an existing project to TS by simply renaming the files as TS files

* Fix type errors: Type 'string' is not assignable to type 'number'.

* Add mention about missing typings for javascript-time-ago

* Add GA_INITIALIZED to window

* Fix type error in feed transformers

* Model MembershipInfoForm state and props with TS

* Silence the typescript warning in MembershipInfoForm

* Add threshold to Fader props

* Fix Warning: Each child in a list should have a unique "key" prop.

Sponsors don't have ids – name is probably unique and can be used as a key

* Allow key as prop for SponsorLink

* Add type of the props for SponsorLink
2019-11-07 07:28:02 +02:00

211 lines
5.1 KiB
TypeScript

import request from "axios";
import React from "react";
import classSet from "classnames";
import api from "../api";
import Loader from "../Loader";
type Props = {
onSignupSuccess: () => void;
};
type State = {
error: boolean;
errors: string[];
fields: {
name: string;
email: string;
handle: string;
address: string;
postcode: string;
city: string;
};
sending: boolean;
pristineFields: string[];
};
const fieldNameTranslations = {
address: { fi: "Osoite" },
city: { fi: "Paikkakunta" },
email: { fi: "Sähköpostiosoite" },
handle: { fi: "Slack-käyttäjätunnus " },
name: { fi: "Koko nimi " },
postcode: { fi: "Postinumero" },
};
const mailValidateRe = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
function validateEmail(email) {
return mailValidateRe.test(email);
}
function getUserInfo(state) {
return state.fields;
}
export default class MembershipInfoForm extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.setState({
fields: {
address: "",
city: "",
email: "",
handle: "",
name: "",
postcode: "",
},
sending: false,
pristineFields: Object.keys(this.state.fields),
});
}
onSubmit = async () => {
this.setState({
sending: true,
error: null,
});
try {
await request.post(api("membership"), {
userInfo: getUserInfo(this.state),
});
this.setState({ sending: false });
this.props.onSignupSuccess();
} catch (err) {
this.setState({ error: err, sending: false });
}
};
onChange = e => {
const name = e.target.name;
if (e.target.value === this.state[name]) {
return;
}
this.setState({
fields: {
...this.state.fields,
[name]: e.target.value,
},
pristineFields: this.state.pristineFields.filter(
fieldName => fieldName !== name
),
errors: [],
});
};
getDataErrors = () => {
const foundErrors = [];
Object.keys(this.state.fields).forEach(fieldName => {
if (!this.state[fieldName]) {
foundErrors.push({ field: fieldName, type: "missing" });
}
});
if (this.state.fields.email && !validateEmail(this.state.fields.email)) {
foundErrors.push({ field: "email", type: "invalid" });
}
return foundErrors;
};
render() {
const inputErrors = this.getDataErrors();
const formClasses = classSet({
form: true,
"membership-form": true,
"has-error": inputErrors.length !== 0 || this.state.error,
sending: this.state.sending,
});
function getErrorMessage(err) {
let feedbackText;
if (err.type === "missing") {
feedbackText = `${fieldNameTranslations[err.field].fi} on pakollinen.`;
} else if (err.type === "invalid") {
feedbackText = `${fieldNameTranslations[err.field].fi} on virheellinen.`;
}
return (
<div key={err.field} className="form--message">
{feedbackText}
</div>
);
}
/* generate error messages */
const visibleErrors = inputErrors.filter(
error => this.state.pristineFields.indexOf(error.field) === -1
);
const fieldsWithErrors = visibleErrors.map(({ field }) => field);
const inputFields = Object.keys(this.state.fields).map(fieldName => {
const inputClasses = classSet({
input: true,
"has-error": fieldsWithErrors.includes(fieldName),
half: fieldName === "city" || fieldName === "postcode",
left: fieldName === "postcode",
});
function showsErrorFor(field) {
if (fieldName === "city") {
return false;
}
return (
field === fieldName || (fieldName === "postcode" && field === "city")
);
}
return (
<span key={fieldName}>
<input
className={inputClasses}
type={fieldName === "email" ? "email" : "text"}
name={fieldName}
placeholder={fieldNameTranslations[fieldName].fi}
value={this.state[fieldName]}
onChange={this.onChange}
/>
{visibleErrors
.filter(({ field }) => showsErrorFor(field))
.map(getErrorMessage)}
</span>
);
});
if (this.state.sending) {
return (
<div className="membership-form__loader">
<Loader />
</div>
);
}
return (
<div>
<h3>Liity jäseneksi</h3>
<form className={formClasses}>
{inputFields}
{this.state.error && (
<div className="form--message">
Jotain meni pieleen! Ota yhteyttä info@koodiklinikka.fi
</div>
)}
<br />
<button
type="button"
disabled={inputErrors.length !== 0}
className="btn btn__submit"
onClick={this.onSubmit}
>
Liity jäseneksi
</button>
</form>
</div>
);
}
}