diff --git a/src/components/Widgets/DatetimeWidget/DatetimeWidget.tsx b/src/components/Widgets/DatetimeWidget/DatetimeWidget.tsx index 9ace004..ee95f00 100644 --- a/src/components/Widgets/DatetimeWidget/DatetimeWidget.tsx +++ b/src/components/Widgets/DatetimeWidget/DatetimeWidget.tsx @@ -1,7 +1,7 @@ import React from "react"; import "./DatetimeWidget.scss"; -export interface DatetimeWidgetProps { +interface DatetimeWidgetProps { value: string; onChange: (value: string) => void; onFocus: () => void; @@ -9,42 +9,37 @@ export interface DatetimeWidgetProps { required: boolean; disabled: boolean; } -export interface DatetimeWidgetState { } -class DatetimeWidget extends React.Component { - render() { - const { value, onChange, onFocus, onBlur, required, disabled } = this.props; - - let date; - let time; - if (value && value.length !== 0) { - let rest; - [date, rest] = value.split("T"); - time = rest.slice(0, 5); - } - - const commonProps = { - onFocus, - onBlur, - required, - disabled, - }; - - return ( -
- onChange(`${event.target.value}T${time}`)} - value={date} - {...commonProps} /> - onChange(`${date}T${event.target.value}:00`)} - value={time} - {...commonProps} /> -
- ); +const DatetimeWidget: React.FC = ({ value, onChange, onFocus, onBlur, required, disabled }) => { + let date; + let time; + if (value && value.length !== 0) { + let rest; + [date, rest] = value.split("T"); + time = rest.slice(0, 5); } + + const commonProps = { + onFocus, + onBlur, + required, + disabled, + }; + + return ( +
+ onChange(`${event.target.value}T${time}`)} + value={date} + {...commonProps} /> + onChange(`${date}T${event.target.value}:00`)} + value={time} + {...commonProps} /> +
+ ); } export default DatetimeWidget; diff --git a/src/components/Widgets/SectionDividerWidget/SectionDividerWidget.tsx b/src/components/Widgets/SectionDividerWidget/SectionDividerWidget.tsx index 9662e9d..465d0e4 100644 --- a/src/components/Widgets/SectionDividerWidget/SectionDividerWidget.tsx +++ b/src/components/Widgets/SectionDividerWidget/SectionDividerWidget.tsx @@ -3,10 +3,9 @@ import "./SectionDividerWidget.scss"; import Icon from "../../Icon"; import { IconType } from "../../Icon/Icon"; -export interface SectionDividerWidgetProps { +interface SectionDividerWidgetProps { label: string; } -export interface SectionDividerWidgetState { } const getIconByLabel = (label: string) => { if (label === "Finnish") { @@ -19,16 +18,10 @@ const getIconByLabel = (label: string) => { return null; } -class SectionDividerWidget extends React.Component { - render() { - const { label } = this.props; - - return ( -

- {label} {getIconByLabel(label)} -

- ); - } -} +const SectionDividerWidget: React.FC = ({ label }) => ( +

+ {label} {getIconByLabel(label)} +

+); export default SectionDividerWidget; diff --git a/src/components/Widgets/SignupQuestionsWidget/SignupQuestionsWidget.tsx b/src/components/Widgets/SignupQuestionsWidget/SignupQuestionsWidget.tsx index 361dd81..c7d3173 100644 --- a/src/components/Widgets/SignupQuestionsWidget/SignupQuestionsWidget.tsx +++ b/src/components/Widgets/SignupQuestionsWidget/SignupQuestionsWidget.tsx @@ -6,23 +6,21 @@ import AddIcon from "@assets/img/add-icon.png"; import "./SignupQuestionsWidget.scss"; import QuestionList from "./QuestionList"; -export interface SignupQuestionsWidgetProps { +interface SignupQuestionsWidgetProps { value: string; onChange: (value: string) => void; onFocus: () => void; required: boolean; disabled: boolean; } -export interface SignupQuestionsWidgetState { } -class SignupQuestionsWidget extends React.Component { - onValueChange = (questions: Question[]) => { - const { onChange } = this.props; +const SignupQuestionsWidget: React.FC = ({ value, onFocus, onChange }) => { + const onValueChange = (questions: Question[]) => { const newValue = JSON.stringify(questions); onChange(newValue); } - handleNewRowClick = (questions) => () => { + const handleNewRowClick = (questions) => () => { const newRow: Question = { id: shortid.generate(), name: `Question #${questions.length + 1}`, @@ -31,47 +29,42 @@ class SignupQuestionsWidget extends React.Component (result) => { + const handleDragEnd = (questions: Question[]) => (result) => { const srcIndex = result.source.index; const dstIndex = result.destination.index; const srcCopy = { ...questions[srcIndex] }; questions.splice(srcIndex, 1); questions.splice(dstIndex, 0, srcCopy); - this.onValueChange(questions); + onValueChange(questions); } + const questions = JSON.parse(value) as Question[]; - render() { - const { value, onFocus } = this.props; - - const questions = JSON.parse(value) as Question[]; - - return ( -
- - - {(provided) => ( - - )} - - - -
- ); - } + return ( +
+ + + {(provided) => ( + + )} + + + +
+ ); } export default SignupQuestionsWidget; diff --git a/src/models/Feed.ts b/src/models/Feed.ts index a209d8d..c77a62b 100644 --- a/src/models/Feed.ts +++ b/src/models/Feed.ts @@ -1,11 +1,12 @@ import axios from "axios"; import { getAuthHeader } from "@utils/auth"; +import { Tag } from "./Tag"; const url = `${process.env.API_URL}/feed/`; export interface Post { id: number; - tags: number[]; + tags: Tag[]; visible: boolean; image: string; title_fi: string; diff --git a/src/pages/admin/AdminCreateCommon.tsx b/src/pages/admin/AdminCreateCommon.tsx index 3351d96..5d15354 100644 --- a/src/pages/admin/AdminCreateCommon.tsx +++ b/src/pages/admin/AdminCreateCommon.tsx @@ -1,7 +1,13 @@ +import React from "react"; import styled from "styled-components"; +import Form, { ISubmitEvent, IChangeEvent, ErrorSchema } from "react-jsonschema-form"; import { colors }from "@theme/colors"; +import { Event } from "@models/Event"; +import { Post } from "@models/Feed"; +import { SignupForm } from "@models/SignupForm"; +import { JobAd } from "@models/JobAd"; -const AdminCreateCommon = styled.main` +const Common = styled.main` width: 100%; fieldset { @@ -54,4 +60,65 @@ const AdminCreateCommon = styled.main` } `; +type FormTypes = Event | SignupForm | Post | JobAd; + +type AdminCreateCommonProps = { + title: string; + formData?: FormTypes; + schema: { + [name: string]: any; + }; + UISchema: { + [name: string]: any; + }; + onChange: (e: IChangeEvent, es?: ErrorSchema) => any; + onFocus: (id: string, value: string | number | boolean) => void; + onSubmit: (e: ISubmitEvent) => any; + statusMessage: string; + error: string; + widgets: { + [name: string]: any; + }; +} + +const AdminCreateCommon: React.FC = ({ + title, + formData, + schema, + UISchema, + onChange, + onFocus, + onSubmit, + statusMessage, + error, + widgets +}) => { + + const onError = (data: any) => { + console.error("error, data:"); + console.log(data); + } + + return ( + +

{title}

+ {statusMessage &&
{statusMessage}
} +
+ + {error && ( +
{error}
+ )} + + ) +} + export default AdminCreateCommon; diff --git a/src/pages/admin/EventCreatePage.tsx b/src/pages/admin/EventCreatePage.tsx index 9ba2d5d..8185174 100644 --- a/src/pages/admin/EventCreatePage.tsx +++ b/src/pages/admin/EventCreatePage.tsx @@ -1,6 +1,6 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import { Helmet } from "react-helmet"; -import Form from "react-jsonschema-form"; +import { RouteComponentProps } from "react-router-dom"; import AdminCreateCommon from "./AdminCreateCommon"; import { Tag, getTags } from "@models/Tag"; import { SignupForm, getForms } from "@models/SignupForm"; @@ -16,89 +16,184 @@ const widgets = { markdownEditor: MarkdownEditorWidget }; -export interface EventCreatePageProps { - history: { - push: (to: string) => void; +const buildSchema = (formData: Event, signupForms: SignupForm[], tags: Tag[]) => { + const date = new Date(), tomorrowDate = new Date(); + const currentDatetime = date.toISOString(); + tomorrowDate.setDate(tomorrowDate.getDate() + 1); + const tomorrowDatetime = tomorrowDate.toISOString(); + + const schema = { + title: formData?.id ? formData.title_fi : "New Event", + type: "object", + required: ["title_fi", "title_en", "tags", "location_fi", "location_en", "start_time", "end_time", "description_fi", "description_en", "content_fi", "content_en"], + properties: { + tags: { + type: "array", + title: "Event tags", + items: { + type: "number", + enum: tags.map(t => t.id), + enumNames: tags.map(t => t.name_fi), + }, + uniqueItems: true, + default: [], + }, + visible: { + type: "boolean", + title: "Visible", + default: true, + }, + start_time: { + type: "string", + title: "Start time", + default: currentDatetime, + }, + end_time: { + type: "string", + title: "End time", + default: tomorrowDatetime, + }, + signupForm: { + type: "array", + title: "Signup forms", + items: { + type: "number", + // TODO: A bug here, DB must have at least one SignupForm, otherwise cannot submit + enum: signupForms.map(form => form.id), + enumNames: signupForms.map(form => form.title_fi), + }, + uniqueItems: true, + }, + image: { + type: ["string", "null"], + format: formData?.image ? "uri-reference" : "data-url", + title: "Override tag icon with image", + default: undefined + }, + finnish_section_divider: { + title: "Finnish", + type: "string", + }, + title_fi: { + type: "string", + title: "Title", + default: "" + }, + description_fi: { + type: "string", + title: "Description", + default: "", + }, + content_fi: { + type: "string", + title: "Content", + default: "", + }, + location_fi: { + type: "string", + title: "Location", + default: "", + }, + english_section_divider: { + title: "English", + type: "string", + }, + title_en: { + type: "string", + title: "Title", + default: "" + }, + description_en: { + type: "string", + title: "Description", + default: "", + }, + content_en: { + type: "string", + title: "Content", + default: "", + }, + location_en: { + type: "string", + title: "Location", + default: "", + }, + } }; - match: { - params: { - id?: number; - }; - }; -} -export interface EventCreatePageState { - tags: Tag[]; - signupForm: SignupForm[]; - error?: string; - statusMessage?: string; - formData: Event; + return schema; } -class EventCreatePage extends React.Component { - constructor(props) { - super(props); - this.state = { - tags: [], - signupForm: [], - formData: {} as Event, - }; +const buildUISchema = () => { + const uiSchema = { + content_fi: { + "ui:widget": "markdownEditor", + }, + content_en: { + "ui:widget": "markdownEditor", + }, + start_time: { + "ui:widget": "datetime", + }, + end_time: { + "ui:widget": "datetime", + }, + image: { + "ui:options": { + accept: [".jpg", ".jpeg", ".png"] + } + }, + finnish_section_divider: { + "ui:widget": "section_divider", + "ui:options": { + label: false + }, + }, + english_section_divider: { + "ui:widget": "section_divider", + "ui:options": { + label: false + }, + }, + }; + return uiSchema; +} - this.fetchTags(); - this.fetchSignupForms(); +interface MatchParams { + id?: string; +} - const { id } = props.match.params; - if (id !== undefined) { - this.fetchInitialFormData(id); +type EventCreatePageProps = RouteComponentProps; + +const EventCreatePage: React.FC = ({ match: { params: { id } } }) => { + const [formData, setFormData] = useState(null); + const [tags, setTags] = useState([]); + const [signupForms, setSignupForms] = useState([]); + const [error, setError] = useState(null); + const [statusMessage, setStatusMessage] = useState(null); + + useEffect(() => { + + getTags() + .then(res => setTags(res)) + .catch(err => setError(err)) + + getForms(true) + .then(res => setSignupForms(res)) + .catch(err => setError(err)) + + const eventId = id && Number(id); + if (eventId !== undefined) { + getEvent(eventId, true) + .then(res => setFormData({ + ...res, + tags: (res.tags).map(inst => inst.id) as any, + signupForm: (res.signupForm).map(inst => inst.id) as any, + })) + .catch(err => setError(err)) } - } + }, [id]) - fetchInitialFormData = async (id) => { - try { - const data = await getEvent(id, true); - data.tags = (data.tags as any).map(inst => inst.id); - data.signupForm = (data.signupForm as any).map(inst => inst.id); - this.setState({ - formData: data, - }); - } catch (err) { - this.setState({ - error: String(err), - }); - } - } - - fetchTags = async () => { - try { - const tags = await getTags(); - this.setState({ - tags, - }); - return tags; - } catch (err) { - this.setState({ - error: String(err), - }); - throw err; - } - } - - fetchSignupForms = async () => { - try { - const signupForm = await getForms(true); - this.setState({ - signupForm - }) - return signupForm; - } catch (err) { - this.setState({ - error: String(err), - }); - throw err; - } - } - - onSubmit = async (data) => { - const { history } = this.props; + const onSubmit = async (data) => { try { const payload = data.formData; payload.signup_id = payload.signupForm; @@ -113,10 +208,8 @@ class EventCreatePage extends React.Component inst.id); resp.tags = data.formData.tags; resp.signupForm = data.formData.signupForm; - this.setState({ - formData: resp, - statusMessage: "Event created successfully", - }); + setStatusMessage("Event created successfully"); + setFormData(resp); } else { const resp = await updateEvent(payload); // TODO: Backend return old data because of Prefetch (used for filtering invisble signupForms from GET) @@ -125,213 +218,39 @@ class EventCreatePage extends React.Component inst.id); resp.tags = data.formData.tags; resp.signupForm = data.formData.signupForm; - this.setState({ - formData: resp, - statusMessage: "Event updated successfully", - }); + setStatusMessage("Event updated successfully"); + setFormData(resp); } } catch (err) { - this.setState({ - error: String(err), - }); + setError(err); } } - onError = (data) => { - console.error("error, data:"); - console.log(this.state.formData); - console.log(data); - } + const onChange = (data) => setFormData(data.formData); + const onFocus = () => setStatusMessage(null); + const title = formData?.id + ? `Edit Event "${formData.title_fi}"` + : "Create Event"; - onChange = (data) => { - this.setState({ - formData: data.formData, - }); - } - - onFocus = () => { - this.setState({ - statusMessage: null, - }); - } - - buildSchema = () => { - const { tags, signupForm, error } = this.state; - - const formData = this.state.formData as Event; - - const date = new Date(); - const currentDatetime = date.toISOString(); - const tomorrowDate = new Date(); - tomorrowDate.setDate(tomorrowDate.getDate() + 1); - const tomorrowDatetime = tomorrowDate.toISOString(); - - const schema = { - title: formData.id ? formData.title_fi : "New Event", - type: "object", - required: ["title_fi", "title_en", "tags", "location_fi", "location_en", "start_time", "end_time", "description_fi", "description_en", "content_fi", "content_en"], - properties: { - tags: { - type: "array", - title: "Event tags", - items: { - type: "number", - enum: tags.map(t => t.id), - enumNames: tags.map(t => t.name_fi), - }, - uniqueItems: true, - default: [], - }, - visible: { - type: "boolean", - title: "Visible", - default: true, - }, - start_time: { - type: "string", - title: "Start time", - default: currentDatetime, - }, - end_time: { - type: "string", - title: "End time", - default: tomorrowDatetime, - }, - signupForm: { - type: "array", - title: "Signup forms", - items: { - type: "number", - // TODO: A bug here, DB must have at least one SignupForm, otherwise cannot submit - enum: signupForm.map(form => form.id), - enumNames: signupForm.map(form => form.title_fi), - }, - uniqueItems: true, - }, - image: { - type: ["string", "null"], - format: formData.image ? "uri-reference" : "data-url", - title: "Override tag icon with image", - default: undefined - }, - finnish_section_divider: { - title: "Finnish", - type: "string", - }, - title_fi: { - type: "string", - title: "Title", - default: "" - }, - description_fi: { - type: "string", - title: "Description", - default: "", - }, - content_fi: { - type: "string", - title: "Content", - default: "", - }, - location_fi: { - type: "string", - title: "Location", - default: "", - }, - english_section_divider: { - title: "English", - type: "string", - }, - title_en: { - type: "string", - title: "Title", - default: "" - }, - description_en: { - type: "string", - title: "Description", - default: "", - }, - content_en: { - type: "string", - title: "Content", - default: "", - }, - location_en: { - type: "string", - title: "Location", - default: "", - }, - } - }; - return schema; - } - - buildUISchema = () => { - const uiSchema = { - content_fi: { - "ui:widget": "markdownEditor", - }, - content_en: { - "ui:widget": "markdownEditor", - }, - start_time: { - "ui:widget": "datetime", - }, - end_time: { - "ui:widget": "datetime", - }, - image: { - "ui:options": { - accept: [".jpg", ".jpeg", ".png"] - } - }, - finnish_section_divider: { - "ui:widget": "section_divider", - "ui:options": { - label: false - }, - }, - english_section_divider: { - "ui:widget": "section_divider", - "ui:options": { - label: false - }, - }, - }; - return uiSchema; - } - - render() { - const { error, statusMessage } = this.state; - const { formData } = this.state; - const schema = this.buildSchema(); - const uiSchema = this.buildUISchema(); - - const title = formData.id - ? `Edit Event "${formData.title_fi}"` - : "Create Event"; - - return ( - - - - -

{title}

- {statusMessage &&
{statusMessage}
} - - {error &&
{error}
} -
- ); - } + return ( + <> + + + + + + ); } export default EventCreatePage; diff --git a/src/pages/admin/FeedCreatePage.tsx b/src/pages/admin/FeedCreatePage.tsx index 48fda70..5fc580e 100644 --- a/src/pages/admin/FeedCreatePage.tsx +++ b/src/pages/admin/FeedCreatePage.tsx @@ -1,230 +1,209 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import { Helmet } from "react-helmet"; -import Form from "react-jsonschema-form"; import { RouteComponentProps } from "react-router-dom"; import AdminCreateCommon from "./AdminCreateCommon"; import { Tag, getTags } from "@models/Tag"; import { Post, createPost, getPost, updatePost } from "@models/Feed"; import DatetimeWidget from "@components/Widgets/DatetimeWidget/DatetimeWidget"; +import SectionDividerWidget from "@components/Widgets/SectionDividerWidget/SectionDividerWidget"; +import MarkdownEditorWidget from "@components/Widgets/MarkdownEditorWidget"; const widgets = { datetime: DatetimeWidget, + section_divider: SectionDividerWidget, + markdownEditor: MarkdownEditorWidget }; +const buildSchema = (formData: Post, tags: Tag[]) => { + const date = new Date(); + const currentDatetime = date.toISOString(); + + const schema = { + title: formData?.id ? formData.title_fi : "New Post", + type: "object", + required: ["title_fi", "title_en", "description_fi", "description_en", "content_fi", "content_en", "publish_time"], + properties: { + tags: { + type: "array", + title: "Post tags", + items: { + type: "number", + enum: tags.map(t => t.id), + enumNames: tags.map(t => t.name_fi), + }, + uniqueItems: true, + default: [], + }, + visible: { + type: "boolean", + title: "Visible", + default: true, + }, + publish_time: { + type: "string", + title: "Publish time", + default: currentDatetime, + }, + autohide_enabled: { + type: "boolean", + title: "Autohide enabled", + default: false, + }, + autohide: { + type: "string", + title: "Autohide time", + default: "", + }, + finnish_section_divider: { + title: "Finnish", + type: "string", + }, + title_fi: { + type: "string", + title: "Title", + default: "" + }, + description_fi: { + type: "string", + title: "Description", + default: "", + }, + content_fi: { + type: "string", + title: "Content", + default: "", + }, + english_section_divider: { + title: "English", + type: "string", + }, + title_en: { + type: "string", + title: "Title", + default: "" + }, + description_en: { + type: "string", + title: "Description", + default: "", + }, + content_en: { + type: "string", + title: "Content", + default: "", + }, + } + }; + return schema; +} + +const buildUISchema = (formData: Post) => { + const uiSchema = { + content_fi: { + "ui:widget": "markdownEditor", + }, + content_en: { + "ui:widget": "markdownEditor", + }, + publish_time: { + "ui:widget": "datetime", + }, + autohide: { + "ui:widget": formData && formData.autohide_enabled ? "datetime" : "hidden", + }, + finnish_section_divider: { + "ui:widget": "section_divider", + "ui:options": { + label: false + }, + }, + english_section_divider: { + "ui:widget": "section_divider", + "ui:options": { + label: false + }, + }, + }; + return uiSchema; +} + interface MatchParams { id?: string; } type FeedCreatePageProps = RouteComponentProps; -export interface FeedCreatePageState { - tags: Tag[]; - error?: string; - statusMessage?: string; - formData: Post; -} +const FeedCreatePage: React.FC = ({ match: { params: { id } } }) => { + const [formData, setFormData] = useState(null); + const [tags, setTags] = useState([]); + const [error, setError] = useState(null); + const [statusMessage, setStatusMessage] = useState(null); -class FeedCreatePage extends React.Component { - constructor(props) { - super(props); - this.state = { - tags: [], - formData: {} as Post, - }; + useEffect(() => { - this.fetchTags(); + getTags() + .then(res => setTags(res)) + .catch(err => setError(err)) - const {id} = props.match.params; - if (id !== undefined) { - this.fetchInitialFormData(id); + const feedId = id && Number(id); + if (feedId !== undefined) { + getPost(feedId) + // getPost(feedId, true) + .then(res => setFormData({ + ...res, + tags: (res.tags).map(inst => inst.id) as any, + })) + .catch(err => setError(err)) } - } - - fetchInitialFormData = async (id) => { - try { - const data = await getPost(id); - // data.tags = data.tag_id; - this.setState({ - formData: data, - }); - } catch (err) { - this.setState({ - error: String(err), - }); - } - } - - fetchTags = async () => { - const getTagsPromise = getTags(); - try { - const tags = await getTagsPromise; - this.setState({ - tags, - }); - return getTagsPromise; - } catch (err) { - this.setState({ - error: String(err), - }); - } - } - - onSubmit = async (data) => { - console.log("submitted, data:"); - console.log(data); + }, [id]) + const onSubmit = async (data) => { try { const payload = data.formData; payload.tag_id = payload.tags; payload.autohide = payload.autohide || new Date(); - if (payload.id === undefined) { const resp = await createPost(payload); // resp.tags = resp.tags; - this.setState({ - formData: resp, - statusMessage: "Post created successfully", - }); + setStatusMessage("Post created successfully"); + setFormData(resp); } else { const resp = await updatePost(payload); // resp.tags = resp.tag_id; - this.setState({ - formData: resp, - statusMessage: "Post updated successfully.", - }); + setStatusMessage("Post updated successfully"); + setFormData(resp); } } catch (err) { - this.setState({ - error: String(err), - }); + setError(err); } } - onError = (data) => { - console.error("error, data:"); - console.log(data); - } + const onChange = (data) => setFormData(data.formData); + const onFocus = () => setStatusMessage(null); - onChange = (data) => { - this.setState({ - formData: data.formData, - }); - } + const title = formData?.id + ? `Edit Post "${formData.title_fi}"` + : "Create Post"; - onFocus = () => { - this.setState({ - statusMessage: null, - }); - } - - buildSchema = () => { - const { tags, error, formData } = this.state; - - const date = new Date(); - const currentDatetime = date.toISOString(); - - const schema = { - title: formData.id ? formData.title_fi : "New Post", - type: "object", - required: ["title", "description", "content", "publish_time"], - properties: { - title: { - type: "string", - title: "Title", - default: "" - }, - tags: { - type: "array", - title: "Post tags", - items: { - type: "number", - enum: tags.map(t => t.id), - enumNames: tags.map(t => t.name_fi), - }, - uniqueItems: true, - default: [], - }, - description: { - type: "string", - title: "Description", - default: "", - }, - content: { - type: "string", - title: "Content", - default: "", - }, - publish_time: { - type: "string", - title: "Publish time", - default: currentDatetime, - }, - autohide_enabled: { - type: "boolean", - title: "Autohide enabled", - default: false, - }, - autohide: { - type: "string", - title: "Autohide time", - default: "", - }, - visible: { - type: "boolean", - title: "Visible", - default: true, - } - } - }; - return schema; - } - - buildUISchema = () => { - const { formData } = this.state; - const { autohide_enabled } = formData; - const uiSchema = { - content: { - "ui:widget": "textarea", - }, - publish_time: { - "ui:widget": "datetime", - }, - autohide: { - "ui:widget": autohide_enabled ? "datetime" : "hidden", - }, - }; - return uiSchema; - } - - render() { - const { error, formData, statusMessage } = this.state; - const schema = this.buildSchema(); - const uiSchema = this.buildUISchema(); - - const title = formData.id - ? `Edit Post "${formData.title_fi}"` - : "Create Post"; - - return ( - - - - -

{title}

- {statusMessage &&
{statusMessage}
} - - {error &&
{error}
} -
- ); - } + return ( + <> + + + + + + ); } export default FeedCreatePage; diff --git a/src/pages/admin/JobAdCreatePage.tsx b/src/pages/admin/JobAdCreatePage.tsx index 08d26b1..02bbbaa 100644 --- a/src/pages/admin/JobAdCreatePage.tsx +++ b/src/pages/admin/JobAdCreatePage.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useState } from "react"; import { Helmet } from "react-helmet"; -import Form from "react-jsonschema-form"; import { RouteComponentProps } from "react-router-dom"; import AdminCreateCommon from "./AdminCreateCommon"; import { JobAd, getJobAd, createJobAd, updateJobAd } from "@models/JobAd"; @@ -14,12 +13,12 @@ const widgets = { markdownEditor: MarkdownEditorWidget }; -const buildSchema = (title: string) => { +const buildSchema = (formData: JobAd) => { const monthFromNow = new Date(); monthFromNow.setDate(new Date().getDate() + 30); const schema = { - title, + title: formData?.id ? formData.title_fi : "New Job Ad", type: "object", required: ["title_fi", "title_en", "description_fi", "description_en", "content_fi", "content_en", "autohide_at", "autohide_enabled", "visible"], properties: { @@ -105,8 +104,6 @@ const buildUISchema = (formData: JobAd) => ({ }, }); - - interface MatchParams { id?: string; } @@ -114,21 +111,19 @@ interface MatchParams { type JobAdCreatePageProps = RouteComponentProps; const JobAdCreatePage: React.FC = ({ match: { params: { id } } }) => { - const [formData, setFormData] = useState(null); + const [error, setError] = useState(null); + const [statusMessage, setStatusMessage] = useState(null); useEffect(() => { const jobId = id && Number(id); if (jobId !== undefined) { getJobAd(jobId, true) .then(res => setFormData(res)) + .catch(err => setError(err)) } }, [id]) - - const [error, setError] = useState(null); - const [statusMessage, setStatusMessage] = useState(null); - const onSubmit = async (data) => { try { const payload = data.formData; @@ -146,10 +141,6 @@ const JobAdCreatePage: React.FC = ({ match: { params: { id } } - const onError = (data) => { - console.error("error, data:"); - console.log(data); - } const onChange = (data) => setFormData(data.formData); const onFocus = () => setStatusMessage(null); @@ -158,24 +149,23 @@ const JobAdCreatePage: React.FC = ({ match: { params: { id : "Create Ad"; return ( - + <> -

{title}

- {statusMessage &&
{statusMessage}
} - - {error &&
{error}
} -
+ statusMessage={statusMessage} + error={error} + widgets={widgets} + /> + ); } diff --git a/src/pages/admin/SignupCreatePage.tsx b/src/pages/admin/SignupCreatePage.tsx index 46bcd04..a64e52e 100644 --- a/src/pages/admin/SignupCreatePage.tsx +++ b/src/pages/admin/SignupCreatePage.tsx @@ -1,7 +1,6 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import { Helmet } from "react-helmet"; -import { Link } from "react-router-dom"; -import Form from "react-jsonschema-form"; +import { RouteComponentProps } from "react-router-dom"; import AdminCreateCommon from "./AdminCreateCommon"; import { SignupForm, createForm, getForm, updateForm } from "@models/SignupForm"; import DatetimeWidget from "@components/Widgets/DatetimeWidget/DatetimeWidget"; @@ -9,62 +8,119 @@ import SignupQuestionsWidget from "@components/Widgets/SignupQuestionsWidget"; import MarkdownEditorWidget from "@components/Widgets/MarkdownEditorWidget"; import { buildValidationSchema } from "@views/SignUpPage/FormUtils"; -const widgets = { - datetime: DatetimeWidget, - signup: SignupQuestionsWidget, - markdownEditor: MarkdownEditorWidget -}; const DEFAULT_EMAIL = `Moikka, Ilmottautuminen saapui perille.` ; -export interface SignupCreatePageProps { - history: { - push: (to: string) => void; + +const widgets = { + datetime: DatetimeWidget, + signup: SignupQuestionsWidget, + markdownEditor: MarkdownEditorWidget +}; + +const buildSchema = (formData: SignupForm) => { + const date = new Date(); + const currentDatetime = date.toISOString(); + const tomorrowDate = new Date(); + tomorrowDate.setDate(tomorrowDate.getDate() + 1); + const tomorrowDatetime = tomorrowDate.toISOString(); + + const schema = { + title: formData?.id ? formData.title_fi : "New Sign-up form", + type: "object", + required: ["title_fi", "title_en", "start_time", "end_time", "questions"], + properties: { + title_fi: { + type: "string", + title: "Title (FI)", + default: "", + }, + title_en: { + type: "string", + title: "Title (EN)", + default: "", + }, + visible: { + type: "boolean", + title: "Visible", + default: false, + }, + quota: { + type: "integer", + title: "Quota", + minimum: 0, + default: 0, + }, + start_time: { + type: "string", + title: "Start time", + default: currentDatetime, + }, + end_time: { + type: "string", + title: "End time", + default: tomorrowDatetime, + }, + email_content: { + type: "string", + title: "Email on signup", + default: DEFAULT_EMAIL + }, + questions: { + type: "string", + title: "Questions", + default: "[]", + }, + }, }; - match: { - params: { - id?: number; - }; - }; -} -export interface SignupCreatePageState { - error?: string; - statusMessage?: string; - formData: SignupForm; + return schema; } -class SignupCreatePage extends React.Component { - constructor(props: SignupCreatePageProps) { - super(props); - this.state = { - formData: {} as SignupForm, - }; +const buildUISchema = () => { + const uiSchema = { + email_content: { + "ui:widget": "markdownEditor", + }, + start_time: { + "ui:widget": "datetime", + }, + end_time: { + "ui:widget": "datetime", + }, + questions: { + "ui:widget": "signup", + }, + }; + return uiSchema; +} - const {id} = props.match.params; - if (id !== undefined) { - this.fetchInitialFormData(id); +interface MatchParams { + id?: string; +} + +type SignupCreatePageProps = RouteComponentProps; + +const SignupCreatePage: React.FC = ({ match: { params: { id } } }) => { + const [formData, setFormData] = useState(null); + const [error, setError] = useState(null); + const [statusMessage, setStatusMessage] = useState(null); + useEffect(() => { + const suId = id && Number(id); + if (suId !== undefined) { + getForm(suId, true) + .then(res => { + setFormData({ + ...res, + questions: JSON.stringify(res.questions) as any + }); + }) + .catch(err => setError(err)) } - } + }, [id]) - fetchInitialFormData = async (id: number) => { - try { - const data = await getForm(id, true); - this.setState({ - formData: { - ...data, - questions: JSON.stringify(data.questions) as any - }, - }); - } catch (err) { - this.setState({ - error: String(err), - }); - } - } - - onSubmit = async (data: any) => { + const onSubmit = async (data: any) => { try { const questions = JSON.parse(data.formData.questions); const payload: SignupForm = { @@ -75,158 +131,53 @@ class SignupCreatePage extends React.Component { - console.error("error, data:"); - console.log(data); - } + const onChange = (data) => setFormData(data.formData); + const onFocus = () => setStatusMessage(null); - onChange = (data) => { - this.setState({ - formData: data.formData, - }); - } + const title = formData?.id + ? `Edit Sign-up Form "${formData.title_fi}"` + : "Create Sign-up form"; - onFocus = () => { - this.setState({ - statusMessage: null, - }); - } - - buildSchema = () => { - const { error, formData } = this.state; - - const date = new Date(); - const currentDatetime = date.toISOString(); - const tomorrowDate = new Date(); - tomorrowDate.setDate(tomorrowDate.getDate() + 1); - const tomorrowDatetime = tomorrowDate.toISOString(); - - const schema = { - title: formData.id ? formData.title_fi : "New Sign-up form", - type: "object", - required: ["title_fi", "title_en", "start_time", "end_time", "questions"], - properties: { - title_fi: { - type: "string", - title: "Title (FI)", - default: "", - }, - title_en: { - type: "string", - title: "Title (EN)", - default: "", - }, - visible: { - type: "boolean", - title: "Visible", - default: false, - }, - quota: { - type: "integer", - title: "Quota", - minimum: 0, - default: 0, - }, - start_time: { - type: "string", - title: "Start time", - default: currentDatetime, - }, - end_time: { - type: "string", - title: "End time", - default: tomorrowDatetime, - }, - email_content: { - type: "string", - title: "Email on signup", - default: DEFAULT_EMAIL - }, - questions: { - type: "string", - title: "Questions", - default: "[]", - }, - }, - }; - return schema; - } - - buildUISchema = () => { - const uiSchema = { - email_content: { - "ui:widget": "markdownEditor", - }, - start_time: { - "ui:widget": "datetime", - }, - end_time: { - "ui:widget": "datetime", - }, - questions: { - "ui:widget": "signup", - }, - }; - return uiSchema; - } - - render() { - const { error, formData, statusMessage } = this.state; - const schema = this.buildSchema(); - const uiSchema = this.buildUISchema(); - - const title = formData.id - ? `Edit Sign-up Form "${formData.title_fi}"` - : "Create Sign-up form"; - - return ( - - - - -

{title}

- {statusMessage &&
{statusMessage}
} - {formData.id &&

+ return ( + <> + + + + + {/* {formData.id &&

Check out the signup form here: {formData.title_fi} -

} - - {error &&
{error}
} -
- ); - } +

} */} + + ); } export default SignupCreatePage;