Implement signup page using jsonschema form
This commit is contained in:
Generated
+37
-17
@@ -3041,6 +3041,7 @@
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-js": "^2.4.0",
|
||||
"regenerator-runtime": "^0.11.0"
|
||||
@@ -11669,8 +11670,7 @@
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
||||
},
|
||||
"lodash-id": {
|
||||
"version": "0.14.0",
|
||||
@@ -11708,11 +11708,6 @@
|
||||
"integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.topath": {
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz",
|
||||
"integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak="
|
||||
},
|
||||
"lodash.unescape": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
|
||||
@@ -15001,27 +14996,51 @@
|
||||
"integrity": "sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA=="
|
||||
},
|
||||
"react-jsonschema-form": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.5.0.tgz",
|
||||
"integrity": "sha512-SsldN37+5dDLRAGmwNO6cKb9AH2zhgkhIST9+UVaBqQ/KONl4jj1KFerXiEySGGDFBe81CjMGapZV5Ydrdp4pg==",
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.8.0.tgz",
|
||||
"integrity": "sha512-3rZZ1tCG+vtXRXEdX751t5L1c1TUGk1pvSY/4LzBrUo5FlwulnwJpkosE4BqwSRxvfMckP8YoHFQV4KjzaHGgQ==",
|
||||
"requires": {
|
||||
"@babel/runtime-corejs2": "^7.4.5",
|
||||
"ajv": "^6.7.0",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"core-js": "^2.5.7",
|
||||
"lodash.topath": "^4.5.2",
|
||||
"prop-types": "^15.5.8"
|
||||
"lodash": "^4.17.15",
|
||||
"prop-types": "^15.5.8",
|
||||
"react-is": "^16.8.4",
|
||||
"react-lifecycles-compat": "^3.0.4",
|
||||
"shortid": "^2.2.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime-corejs2": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.7.2.tgz",
|
||||
"integrity": "sha512-GfVnHchOBvIMsweQ13l4jd9lT4brkevnavnVOej5g2y7PpTRY+R4pcQlCjWMZoUla5rMLFzaS/Ll2s59cB1TqQ==",
|
||||
"requires": {
|
||||
"core-js": "^2.6.5",
|
||||
"regenerator-runtime": "^0.13.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "2.6.10",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz",
|
||||
"integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
|
||||
"integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
|
||||
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
|
||||
"requires": {
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.3",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -15310,7 +15329,8 @@
|
||||
"regenerator-runtime": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
|
||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
|
||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
|
||||
"dev": true
|
||||
},
|
||||
"regenerator-transform": {
|
||||
"version": "0.10.1",
|
||||
|
||||
+2
-2
@@ -120,11 +120,11 @@
|
||||
"query-string": "6.5.0",
|
||||
"react-beautiful-dnd": "10.1.1",
|
||||
"react-helmet": "5.2.1",
|
||||
"react-jsonschema-form": "1.5.0",
|
||||
"react-jsonschema-form": "^1.8.0",
|
||||
"react-router": "4.3.1",
|
||||
"react-router-dom": "4.3.1",
|
||||
"react-router-hash-link": "1.2.1",
|
||||
"shortid": "2.2.14"
|
||||
},
|
||||
"postcss": {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import axios from "axios";
|
||||
import { getAuthHeader } from "../auth";
|
||||
import * as qs from "query-string";
|
||||
const url = `${process.env.API_URL}/signup/`;
|
||||
import { Question } from "../components/SignupQuestionsWidget";
|
||||
|
||||
export interface Signup {
|
||||
id?: number;
|
||||
time: string;
|
||||
signupForm_id: number;
|
||||
answer: string;
|
||||
}
|
||||
|
||||
export async function createSignup(data: Signup): Promise<Signup> {
|
||||
try {
|
||||
const resp = await axios.post(url, data, {
|
||||
headers: {
|
||||
"Authorization": getAuthHeader(),
|
||||
},
|
||||
});
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,26 @@
|
||||
.sign-up-page {
|
||||
display: flex;
|
||||
|
||||
fieldset {
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 1rem 0;
|
||||
|
||||
.form-group > label {
|
||||
margin: 0.5rem 0;
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
display: block;
|
||||
}
|
||||
|
||||
input[type="radio"], input[type="checkbox"] {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.question {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
@@ -9,6 +29,10 @@
|
||||
input {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import * as React from "react";
|
||||
import Helmet from "react-helmet";
|
||||
import Form from "react-jsonschema-form";
|
||||
import "./SignUpPage.scss";
|
||||
import { getForm, SignupForm } from "../../models/SignupForm";
|
||||
import { createSignup, Signup } from "../../models/Signup";
|
||||
import PageSection from "../../components/PageSection";
|
||||
import { ColorEnum } from "../../components/ColorDiv/ColorDiv";
|
||||
import { Question, optionTypes } from "../../components/SignupQuestionsWidget";
|
||||
@@ -16,36 +18,22 @@ export interface SignUpPageProps {
|
||||
|
||||
export interface SignUpPageState {
|
||||
signUpForm: SignupForm | null;
|
||||
formData: any;
|
||||
statusMessage: string;
|
||||
error: string;
|
||||
}
|
||||
|
||||
class SignUpPage extends React.Component<SignUpPageProps, SignUpPageState> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { staticContext } = props;
|
||||
this.state = {
|
||||
signUpForm: null,
|
||||
formData: {},
|
||||
statusMessage: "",
|
||||
error: "",
|
||||
};
|
||||
|
||||
if (staticContext) {
|
||||
/* The static context is an object that manages promises when
|
||||
rendering on the server. If staticContext exists, that means
|
||||
we have to store all promises in it. Otherwise, operate
|
||||
normally. See server/index.ts. */
|
||||
if (staticContext.resolutions.getSignUpForm) {
|
||||
const signUpForm = staticContext.resolutions.getSignUpForm as SignupForm;
|
||||
this.state = {
|
||||
signUpForm,
|
||||
};
|
||||
} else {
|
||||
this.state = {
|
||||
signUpForm: null,
|
||||
};
|
||||
const promiseSignUpForm = this.fetchSignUp();
|
||||
staticContext.promises.getSignUpForm = promiseSignUpForm;
|
||||
}
|
||||
} else {
|
||||
this.state = {
|
||||
signUpForm: null,
|
||||
};
|
||||
this.fetchSignUp();
|
||||
}
|
||||
this.fetchSignUp();
|
||||
}
|
||||
|
||||
fetchSignUp = (): Promise<SignupForm> => {
|
||||
@@ -60,56 +48,163 @@ class SignUpPage extends React.Component<SignUpPageProps, SignUpPageState> {
|
||||
return formPromise;
|
||||
}
|
||||
|
||||
renderAnswerInput = (question: Question) => {
|
||||
if (!optionTypes.includes(question.type)) {
|
||||
throw new Error("Unknown question type");
|
||||
questionToSchemaProp = (question: Question): {} => {
|
||||
let obj;
|
||||
if (question.type === "text") {
|
||||
obj = {
|
||||
type: "string",
|
||||
title: question.name,
|
||||
default: "",
|
||||
};
|
||||
}
|
||||
else if (question.type === "radiobutton") {
|
||||
obj = {
|
||||
type: "string",
|
||||
title: question.name,
|
||||
enum: question.options,
|
||||
};
|
||||
}
|
||||
else if (question.type === "checkbox") {
|
||||
obj = {
|
||||
type: "array",
|
||||
title: question.name,
|
||||
items: {
|
||||
type: "string",
|
||||
enum: question.options,
|
||||
},
|
||||
uniqueItems: true,
|
||||
};
|
||||
}
|
||||
else {
|
||||
throw new Error(`No mapping to schema prop for question type ${question.type}`);
|
||||
}
|
||||
|
||||
if (question.type === "text") {
|
||||
return <input type="text" name={question.id} />;
|
||||
return {
|
||||
[question.id]: obj,
|
||||
}
|
||||
if (question.type === "radiobutton") {
|
||||
const { options } = question;
|
||||
return options.map((opt, index) => (
|
||||
<span key={index}>
|
||||
<input type="radio" name={`${question.id}`} />
|
||||
{opt}
|
||||
</span>
|
||||
));
|
||||
}
|
||||
|
||||
questionToUISchemaProp = (question: Question): {} => {
|
||||
let obj = {};
|
||||
if (question.type == "checkbox") {
|
||||
obj = {
|
||||
"ui:widget": "checkboxes",
|
||||
};
|
||||
}
|
||||
if (question.type === "checkbox") {
|
||||
const { options } = question;
|
||||
return options.map((opt, index) => {
|
||||
const optSlug = opt.replace(/\s/g, "_").toLocaleLowerCase();
|
||||
return (
|
||||
<span key={index}>
|
||||
<input type="checkbox" name={`${question.id}-${optSlug}`} value={opt} /> {opt}<br />
|
||||
</span>
|
||||
)
|
||||
else if (question.type == "radiobutton") {
|
||||
obj = {
|
||||
"ui:widget": "radio",
|
||||
}
|
||||
}
|
||||
// else {
|
||||
// throw new Error(`No mapping to UI schema prop for question type ${question.type}`);
|
||||
// }
|
||||
return {
|
||||
[question.id]: obj,
|
||||
};
|
||||
}
|
||||
|
||||
buildSchema = () => {
|
||||
const { error, formData, signUpForm } = this.state;
|
||||
const questions: Question[] = JSON.parse(signUpForm.questions);
|
||||
const schemaPropsArray = questions.map(this.questionToSchemaProp);
|
||||
let schemaProps = {};
|
||||
schemaPropsArray.forEach((schemaProp) => {
|
||||
schemaProps = {
|
||||
...schemaProps,
|
||||
...schemaProp,
|
||||
};
|
||||
});
|
||||
|
||||
const schema = {
|
||||
title: signUpForm.id ? signUpForm.title : "Loading...",
|
||||
type: "object",
|
||||
required: [],
|
||||
properties: schemaProps,
|
||||
};
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
buildUISchema = () => {
|
||||
const { error, formData, signUpForm } = this.state;
|
||||
const questions: Question[] = JSON.parse(signUpForm.questions);
|
||||
const uiSchemaPropsArray = questions.map(this.questionToUISchemaProp);
|
||||
let uiSchemaProps = {};
|
||||
uiSchemaPropsArray.forEach((uiSchemaProp) => {
|
||||
uiSchemaProps = {
|
||||
...uiSchemaProps,
|
||||
...uiSchemaProp,
|
||||
};
|
||||
});
|
||||
|
||||
const uiSchema = {
|
||||
...uiSchemaProps,
|
||||
};
|
||||
return uiSchema;
|
||||
}
|
||||
|
||||
onSubmit = async (data) => {
|
||||
const { signUpForm } = this.state;
|
||||
console.log("submitted, data:");
|
||||
console.log(data);
|
||||
|
||||
try {
|
||||
const payload: Signup = {
|
||||
time: new Date().toISOString(),
|
||||
signupForm_id: signUpForm.id,
|
||||
answer: JSON.stringify(data.formData),
|
||||
};
|
||||
// payload.questions = JSON.stringify(payload.questions);
|
||||
|
||||
if (payload.id === undefined) {
|
||||
const resp = await createSignup(payload);
|
||||
this.setState({
|
||||
formData: resp,
|
||||
statusMessage: "Sign-up submitted successfully",
|
||||
});
|
||||
}
|
||||
else {
|
||||
debugger;
|
||||
// const resp = await updateSignup(payload);
|
||||
// this.setState({
|
||||
// formData: resp,
|
||||
// statusMessage: "Sign-up submission updated successfully.",
|
||||
// });
|
||||
}
|
||||
} catch (err) {
|
||||
this.setState({
|
||||
error: String(err),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderQuestion = (question: Question) => {
|
||||
return (
|
||||
<div className="question">
|
||||
{question.name}
|
||||
{this.renderAnswerInput(question)}
|
||||
</div>
|
||||
);
|
||||
onChange = (data) => {
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
renderForm() {
|
||||
const { signUpForm } = this.state;
|
||||
const questions: Question[] = JSON.parse(signUpForm.questions);
|
||||
const content = questions.map(this.renderQuestion);
|
||||
const { signUpForm, formData } = this.state;
|
||||
// const questions: Question[] = JSON.parse(signUpForm.questions);
|
||||
// const content = questions.map(this.renderQuestion);
|
||||
const schema = this.buildSchema();
|
||||
const uiSchema = this.buildUISchema();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Title: {signUpForm.title}</h1>
|
||||
<div>
|
||||
{/* <form onSubmit={this.handleSubmit}>
|
||||
{content}
|
||||
</div>
|
||||
<button type="submit">Submit</button>
|
||||
</form> */}
|
||||
<Form
|
||||
schema={schema}
|
||||
uiSchema={uiSchema}
|
||||
formData={formData}
|
||||
idPrefix="rjsf"
|
||||
onChange={this.onChange}
|
||||
onSubmit={this.onSubmit}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user