Add translation functionality on admin signup pages
This commit is contained in:
@@ -1,49 +1,72 @@
|
||||
import React from "react";
|
||||
import Checkbox from "@components/Widgets/Checkbox/Checkbox";
|
||||
import { SignupFormQuestion } from "@models/Signup";
|
||||
import { Lang } from "src/i18n";
|
||||
import {
|
||||
Question, InputProps, optionTypes, SignupQuestionError,
|
||||
InputProps, optionTypes, SignupQuestionError,
|
||||
} from "./common";
|
||||
|
||||
interface OptionsWidgetProps {
|
||||
inputProps: InputProps;
|
||||
onChange: (value: Question[]) => void;
|
||||
onChange: (value: SignupFormQuestion[]) => void;
|
||||
}
|
||||
|
||||
class OptionsWidget extends React.Component<OptionsWidgetProps> {
|
||||
handleListOptionsChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
handleListOptionsChange = (questions: SignupFormQuestion[], index: number, lang: Lang): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val = event.target.value;
|
||||
const lst = val.split(";").map((p) => p.trimLeft());
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].options = lst;
|
||||
|
||||
if (lang === "fi") {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].options = {
|
||||
...questions[index].options,
|
||||
enumNames_fi: lst,
|
||||
enum: lst,
|
||||
};
|
||||
}
|
||||
if (lang === "en") {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].options = {
|
||||
...questions[index].options,
|
||||
enumNames_en: lst,
|
||||
};
|
||||
}
|
||||
onChange(questions);
|
||||
};
|
||||
|
||||
handleTextOptionsChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
handleInfoTextOptionsChange = (questions: SignupFormQuestion[], index: number, lang: Lang): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val = event.target.value;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].options = val as unknown as string[]; // TODO: Check type
|
||||
|
||||
if (lang === "fi") {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].description_fi = val;
|
||||
}
|
||||
if (lang === "en") {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].description_en = val;
|
||||
}
|
||||
onChange(questions);
|
||||
};
|
||||
|
||||
handleIntegerOptionsChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
handleIntegerOptionsChange = (questions: SignupFormQuestion[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val = event.target.value;
|
||||
if (val !== "") {
|
||||
const lst = val.split(";").map((p) => p.trimLeft());
|
||||
// Ignore everything else but the two first values
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].options = lst.splice(0, 2);
|
||||
questions[index].options.enum = lst.splice(0, 2);
|
||||
} else {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].options = [];
|
||||
questions[index].options.enum = [];
|
||||
}
|
||||
|
||||
onChange(questions);
|
||||
};
|
||||
|
||||
handleRequiredChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
handleRequiredChange = (questions: SignupFormQuestion[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val: boolean = event.target.checked;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
@@ -67,7 +90,7 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
|
||||
render(): JSX.Element {
|
||||
const { inputProps } = this.props;
|
||||
const {
|
||||
type, value, questions, index,
|
||||
value, type, questions, index,
|
||||
} = inputProps;
|
||||
if (!optionTypes.includes(type)) {
|
||||
throw new SignupQuestionError(`Question widget type "${type}" not in types array.`);
|
||||
@@ -82,25 +105,29 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Write something informative"
|
||||
value={questions[index].options}
|
||||
onChange={this.handleTextOptionsChange(questions, index)}
|
||||
placeholder="Write something informative in Finnish"
|
||||
value={questions[index].description_fi}
|
||||
onChange={this.handleInfoTextOptionsChange(questions, index, "fi")}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Write something informative in English"
|
||||
value={questions[index].description_en}
|
||||
onChange={this.handleInfoTextOptionsChange(questions, index, "en")}
|
||||
required
|
||||
/>
|
||||
{this.requiredField()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (type === "integer") {
|
||||
const lst = value as string[];
|
||||
const joinedValue = lst.join(";");
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Minimum;Maximum"
|
||||
value={joinedValue}
|
||||
value={value.enum.join(";")}
|
||||
onChange={this.handleIntegerOptionsChange(questions, index)}
|
||||
/>
|
||||
{this.requiredField()}
|
||||
@@ -109,15 +136,20 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
|
||||
}
|
||||
|
||||
if (type === "radiobutton") {
|
||||
const lst = value as string[];
|
||||
const joinedValue = lst.join(";");
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Kyllä;ei;ehkä"
|
||||
value={value.enumNames_fi.join(";")}
|
||||
onChange={this.handleListOptionsChange(questions, index, "fi")}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Yes;no;maybe"
|
||||
value={joinedValue}
|
||||
onChange={this.handleListOptionsChange(questions, index)}
|
||||
value={value.enumNames_en.join(";")}
|
||||
onChange={this.handleListOptionsChange(questions, index, "en")}
|
||||
required
|
||||
/>
|
||||
</>
|
||||
@@ -125,15 +157,20 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
|
||||
}
|
||||
|
||||
if (type === "checkbox") {
|
||||
const lst = value as string[];
|
||||
const joinedValue = lst.join(";");
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="A;B;C"
|
||||
value={joinedValue}
|
||||
onChange={this.handleListOptionsChange(questions, index)}
|
||||
placeholder="Yksi;Kaksi;Kolme"
|
||||
value={value.enumNames_fi.join(";")}
|
||||
onChange={this.handleListOptionsChange(questions, index, "fi")}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="One;Two;Three"
|
||||
value={value.enumNames_en.join(";")}
|
||||
onChange={this.handleListOptionsChange(questions, index, "en")}
|
||||
required
|
||||
/>
|
||||
{this.requiredField()}
|
||||
|
||||
@@ -2,7 +2,8 @@ import React, { ReactNode } from "react";
|
||||
import styled from "styled-components";
|
||||
import { Draggable } from "react-beautiful-dnd";
|
||||
import colors from "@theme/colors";
|
||||
import { Question, InputProps } from "./common";
|
||||
import { SignupFormQuestion } from "@models/Signup";
|
||||
import { Lang } from "src/i18n";
|
||||
import OptionsWidget from "./OptionsWidget";
|
||||
import TypeWidget from "./TypeWidget";
|
||||
import QuestionElement from "./Question";
|
||||
@@ -16,26 +17,28 @@ const WidgetRow = styled.div`
|
||||
`;
|
||||
|
||||
interface QuestionListProps {
|
||||
questions: Question[];
|
||||
questions: SignupFormQuestion[];
|
||||
innerRef: React.Ref<HTMLDivElement>;
|
||||
placeholder: ReactNode;
|
||||
onChange: (value: Question[]) => void;
|
||||
onChange: (value: SignupFormQuestion[]) => void;
|
||||
}
|
||||
|
||||
class QuestionList extends React.Component<QuestionListProps> {
|
||||
renderTextWidget = ({ questions, value, index }: InputProps): JSX.Element => (
|
||||
<input type="text" value={value} onChange={this.handleNameInputChange(questions, index)} />
|
||||
);
|
||||
|
||||
handleNameInputChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
handleNameInputChange = (questions: SignupFormQuestion[], index: number, lang: Lang): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val = event.target.value;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].name = val;
|
||||
if (lang === "fi") {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].title_fi = val;
|
||||
}
|
||||
if (lang === "en") {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].title_en = val;
|
||||
}
|
||||
onChange(questions);
|
||||
};
|
||||
|
||||
handleElementRemove = (questions: Question[], index: number) => (): void => {
|
||||
handleElementRemove = (questions: SignupFormQuestion[], index: number) => (): void => {
|
||||
const { onChange } = this.props;
|
||||
const newQuestions = [...questions];
|
||||
newQuestions.splice(index, 1);
|
||||
@@ -45,11 +48,6 @@ class QuestionList extends React.Component<QuestionListProps> {
|
||||
renderQuestions(): JSX.Element[] {
|
||||
const { questions, onChange } = this.props;
|
||||
return questions.map((q, index) => {
|
||||
const nameWidgetProps = {
|
||||
value: q.name, type: "text", questions, index,
|
||||
};
|
||||
const nameWidget = this.renderTextWidget(nameWidgetProps);
|
||||
|
||||
const dataProps = {
|
||||
value: q.options, type: q.type, questions, index,
|
||||
};
|
||||
@@ -66,7 +64,8 @@ class QuestionList extends React.Component<QuestionListProps> {
|
||||
<QuestionElement
|
||||
onClick={this.handleElementRemove(questions, index)}
|
||||
>
|
||||
{nameWidget}
|
||||
<input type="text" value={q.title_fi} onChange={this.handleNameInputChange(questions, index, "fi")} />
|
||||
<input type="text" value={q.title_en} onChange={this.handleNameInputChange(questions, index, "en")} />
|
||||
{typeSelectWidget}
|
||||
{optionsWidget}
|
||||
</QuestionElement>
|
||||
|
||||
@@ -4,8 +4,8 @@ import shortid from "shortid";
|
||||
import { DragDropContext, Droppable } from "react-beautiful-dnd";
|
||||
import colors from "@theme/colors";
|
||||
import AddIcon from "@components/AddIcon";
|
||||
import { SignupFormQuestion } from "@models/Signup";
|
||||
import QuestionList from "./QuestionList";
|
||||
import { Question } from "./common";
|
||||
|
||||
const Widget = styled.div`
|
||||
& > button {
|
||||
@@ -40,24 +40,25 @@ interface SignupQuestionsWidgetProps {
|
||||
}
|
||||
|
||||
const SignupQuestionsWidget: React.FC<SignupQuestionsWidgetProps> = ({ value, onFocus, onChange }) => {
|
||||
const onValueChange = (questions: Question[]) => {
|
||||
const onValueChange = (questions: SignupFormQuestion[]) => {
|
||||
const newValue = JSON.stringify(questions);
|
||||
onChange(newValue);
|
||||
};
|
||||
|
||||
const handleNewRowClick = (questions) => () => {
|
||||
const newRow: Question = {
|
||||
const newRow: SignupFormQuestion = {
|
||||
id: shortid.generate(),
|
||||
name: `Question #${questions.length + 1}`,
|
||||
options: [],
|
||||
title_fi: `Question #${questions.length + 1}`,
|
||||
title_en: `Question #${questions.length + 1}`,
|
||||
options: undefined,
|
||||
type: "text",
|
||||
};
|
||||
const newQuestions: Question[] = questions.concat([newRow]);
|
||||
const newQuestions: SignupFormQuestion[] = questions.concat([newRow]);
|
||||
|
||||
onValueChange(newQuestions);
|
||||
};
|
||||
|
||||
const handleDragEnd = (questions: Question[]) => (result) => {
|
||||
const handleDragEnd = (questions: SignupFormQuestion[]) => (result) => {
|
||||
const srcIndex = result.source.index;
|
||||
const dstIndex = result.destination.index;
|
||||
const srcCopy = { ...questions[srcIndex] };
|
||||
@@ -66,7 +67,7 @@ const SignupQuestionsWidget: React.FC<SignupQuestionsWidgetProps> = ({ value, on
|
||||
|
||||
onValueChange(questions);
|
||||
};
|
||||
const questions = JSON.parse(value) as Question[];
|
||||
const questions: SignupFormQuestion[] = JSON.parse(value);
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
import React from "react";
|
||||
import { Question, InputProps, optionTypes } from "./common";
|
||||
import { SignupFormQuestion } from "@models/Signup";
|
||||
import { InputProps, optionTypes } from "./common";
|
||||
|
||||
interface TypeWidgetProps {
|
||||
inputProps: InputProps;
|
||||
onChange: (value: Question[]) => void;
|
||||
onChange: (value: SignupFormQuestion[]) => void;
|
||||
}
|
||||
|
||||
class TypeWidget extends React.Component<TypeWidgetProps> {
|
||||
handleTypeChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLSelectElement> => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val = event.target.value as Question["type"];
|
||||
const TypeWidget = ({ onChange, inputProps }: TypeWidgetProps): JSX.Element => {
|
||||
const handleTypeChange = (questions: SignupFormQuestion[], index: number): React.ChangeEventHandler<HTMLSelectElement> => (event) => {
|
||||
const val = event.target.value as SignupFormQuestion["type"];
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
questions[index].type = val;
|
||||
onChange(questions);
|
||||
};
|
||||
|
||||
render(): JSX.Element {
|
||||
const { inputProps } = this.props;
|
||||
const { type, questions, index } = inputProps;
|
||||
const options = optionTypes.map((t) => (
|
||||
<option key={t} value={t}>{t}</option>
|
||||
));
|
||||
return (
|
||||
<select onChange={this.handleTypeChange(questions, index)} value={type} name="type">
|
||||
{options}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
}
|
||||
const { questions, type, index } = inputProps;
|
||||
const options = optionTypes.map((t) => (
|
||||
<option key={t} value={t}>{t}</option>
|
||||
));
|
||||
return (
|
||||
<select onChange={handleTypeChange(questions, index)} value={type} name="type">
|
||||
{options}
|
||||
</select>
|
||||
);
|
||||
};
|
||||
|
||||
export default TypeWidget;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { SignupFormQuestion } from "@models/Signup";
|
||||
|
||||
export interface Question {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -10,8 +12,8 @@ export interface Question {
|
||||
|
||||
export interface InputProps {
|
||||
index: number;
|
||||
value: string | string[];
|
||||
questions: Question[];
|
||||
value: SignupFormQuestion["options"];
|
||||
questions: SignupFormQuestion[];
|
||||
type: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ const SignupEmailPage: NextPage = () => {
|
||||
|
||||
// TODO: ATM we filter 'info' questions from table here. Maybe remove them from answer JSON altogether?
|
||||
const questions = signupForm ? signupForm.questions.filter((q) => q.type !== "info").map((q) => ({
|
||||
title: q.name,
|
||||
title: q.title_fi,
|
||||
id: q.id,
|
||||
})) : [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user