diff --git a/src/api/signupApi.ts b/src/api/signupApi.ts index 66fb599..2c233b4 100644 --- a/src/api/signupApi.ts +++ b/src/api/signupApi.ts @@ -2,9 +2,11 @@ import axios from "axios"; import { Signup, SignupForm } from "@models/Signup"; import { getAuthHeader } from "@utils/auth"; +import { TemplateQuestion } from "@models/TemplateQuestion"; export const URL = `${process.env.NEXT_PUBLIC_API_URL}/signup/`; export const FORM_URL = `${process.env.NEXT_PUBLIC_API_URL}/signupForm/`; +export const QUESTIONS_URL = `${process.env.NEXT_PUBLIC_API_URL}/questions/`; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface Options { @@ -177,6 +179,69 @@ class SignupApi { throw err; } } + + static async getTemplateQuestions(): Promise { + try { + const resp = await axios.get(`${QUESTIONS_URL}`); + return resp.data.results; + } catch (err) { + console.error(err); + throw err; + } + } + + static async getTemplateQuestion(id: number): Promise { + try { + const resp = await axios.get(`${QUESTIONS_URL}${id}/`); + return resp.data; + } catch (err) { + console.error(err); + throw err; + } + } + + static async createTemplateQuestion(question: TemplateQuestion): Promise { + try { + const resp = await axios.post(`${QUESTIONS_URL}`, question, { + headers: { + Authorization: getAuthHeader(), + }, + }); + return resp.data; + } catch (err) { + console.error(err); + throw err; + } + } + + static async updateTemplateQuestion(question: TemplateQuestion): Promise { + try { + const putUrl = `${QUESTIONS_URL}${question.id}/`; + const resp = await axios.put(putUrl, question, { + headers: { + Authorization: getAuthHeader(), + }, + }); + return resp.data; + } catch (err) { + console.error(err); + throw err; + } + } + + static async deleteTemplateQuestion(id: number): Promise<{ message: string }> { + try { + const resp = await axios.delete(`${QUESTIONS_URL}${id}`, { + headers: { + Authorization: getAuthHeader(), + }, + }); + return resp.data; + } catch (err) { + console.error(err); + throw err; + } + } } export default SignupApi; diff --git a/src/components/AdminSidebar.tsx b/src/components/AdminSidebar.tsx index 1b34939..9850095 100644 --- a/src/components/AdminSidebar.tsx +++ b/src/components/AdminSidebar.tsx @@ -49,6 +49,7 @@ const AdminSidebar: React.FC = ({ path }) => ( Events › Feed › Signup forms › + Template questions › Job advertisements › Files › Logout › diff --git a/src/models/TemplateQuestion.ts b/src/models/TemplateQuestion.ts new file mode 100644 index 0000000..87bc85f --- /dev/null +++ b/src/models/TemplateQuestion.ts @@ -0,0 +1,7 @@ +import { Question } from "@components/Widgets/SignupQuestionsWidget/common"; + +export type TemplateQuestion = { + id?: number; + name: string; + questions: Question[]; +}; diff --git a/src/pages/admin/signups/[id].tsx b/src/pages/admin/signups/[id].tsx index c47f982..89ed17f 100644 --- a/src/pages/admin/signups/[id].tsx +++ b/src/pages/admin/signups/[id].tsx @@ -1,6 +1,7 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { NextPage } from "next"; import { useRouter } from "next/router"; +import shortid from "shortid"; import AdminCreateCommon from "@views/admin/AdminCreateCommon"; import { SignupForm } from "@models/Signup"; import SignupApi from "@api/signupApi"; @@ -9,6 +10,8 @@ import SignupQuestionsWidget from "@components/Widgets/SignupQuestionsWidget/Sig import MarkdownEditorWidget from "@components/Widgets/MarkdownEditorWidget"; import { buildValidationSchema } from "@views/SignUpPage/FormUtils"; import { toast } from "react-toastify"; +import { TemplateQuestion } from "@models/TemplateQuestion"; +import { Question } from "@components/Widgets/SignupQuestionsWidget/common"; const DEFAULT_EMAIL = `Moikka, @@ -98,7 +101,10 @@ const buildUISchema = () => { const SignupCreatePage: NextPage = () => { const [formData, setFormData] = useState(null); + const [templateQuestions, setTemplateQuestions] = useState([]); const [error, setError] = useState(null); + const templateSelectionRef = useRef(null); + const templateNameRef = useRef(null); const router = useRouter(); @@ -118,10 +124,15 @@ const SignupCreatePage: NextPage = () => { questions: JSON.stringify(res.questions) as any, }); }) - .catch((err) => setError(err)); + .catch((err) => setError(err.message)); } }, [id]); + useEffect(() => { + SignupApi.getTemplateQuestions().then((res) => setTemplateQuestions(res)) + .catch((err) => setError(err.message)); + }, []); + const onSubmit = async (data: any) => { try { const questions = JSON.parse(data.formData.questions); @@ -172,6 +183,40 @@ const SignupCreatePage: NextPage = () => { error={error} widgets={widgets} /> +
+ + + +
{/* {formData.id &&

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

} */} diff --git a/src/pages/admin/template-questions/[id].tsx b/src/pages/admin/template-questions/[id].tsx new file mode 100644 index 0000000..b68978b --- /dev/null +++ b/src/pages/admin/template-questions/[id].tsx @@ -0,0 +1,118 @@ +import React, { useEffect, useState } from "react"; +import { NextPage } from "next"; +import { useRouter } from "next/router"; +import AdminCreateCommon from "@views/admin/AdminCreateCommon"; +import SignupApi from "@api/signupApi"; +import SignupQuestionsWidget from "@components/Widgets/SignupQuestionsWidget/SignupQuestionsWidget"; +import { toast } from "react-toastify"; +import { TemplateQuestion } from "@models/TemplateQuestion"; + +const widgets = { + signup: SignupQuestionsWidget, +}; + +const buildSchema = (formData: TemplateQuestion) => ({ + title: formData?.name ?? "New Sign-up form", + type: "object", + required: ["name", "questions"], + properties: { + name: { + type: "string", + title: "Name", + default: "", + }, + questions: { + type: "string", + title: "Questions", + default: "[]", + }, + }, +}); + +const buildUISchema = () => ({ + questions: { + "ui:widget": "signup", + }, +}); + +const TemplateQuestionCreatePage: NextPage = () => { + const [formData, setFormData] = useState(null); + const [error, setError] = useState(null); + + const router = useRouter(); + + let id: string; + + if (router.query?.id && router.query.id !== "create") { + id = router.query.id as string; + } + + useEffect(() => { + const templateId = id && Number(id); + SignupApi.getTemplateQuestion(templateId, true) + .then((res) => { + setFormData({ + ...res, + questions: JSON.stringify(res.questions) as any, + }); + }) + .catch((err) => setError(err.message)); + }, [id]); + + const onSubmit = async (data: any) => { + try { + const questions = JSON.parse(data.formData.questions); + const payload: TemplateQuestion = { + ...data.formData, + questions, + }; + + if (payload.id === undefined) { + const resp = await SignupApi.createTemplateQuestion(payload); + toast.success("Sign-up created successfully 😎"); + router.push("/admin/template-questions"); + setFormData({ + ...resp, + questions: JSON.stringify(resp.questions) as any, + }); + } else { + const resp = await SignupApi.updateTemplateQuestion(payload); + toast.success("Sign-up updated successfully 😎"); + router.push("/admin/template-questions"); + setFormData({ + ...resp, + questions: JSON.stringify(resp.questions) as any, + }); + } + } catch (err) { + toast.error("Uh oh! Something went wrong! Try again later. 😟"); + setError(err); + } + }; + + const onChange = (data) => setFormData(data.formData); + + const title = formData?.id + ? `Edit template questions "${formData.name}"` + : "Create template questions"; + + return ( + <> + + {/* {formData.id &&

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

} */} + + ); +}; + +export default TemplateQuestionCreatePage; diff --git a/src/pages/admin/template-questions/index.tsx b/src/pages/admin/template-questions/index.tsx new file mode 100644 index 0000000..069eebe --- /dev/null +++ b/src/pages/admin/template-questions/index.tsx @@ -0,0 +1,80 @@ +import React, { useEffect, useState } from "react"; +import { NextPage } from "next"; +import { toast } from "react-toastify"; +import styled from "styled-components"; +import AdminListCommon from "@views/admin/AdminListCommon"; +import { Button, Link } from "@components/index"; +import AddLink from "@components/AddLink"; +import SignupApi from "@api/signupApi"; +import { TemplateQuestion } from "@models/TemplateQuestion"; + +const URL = "/admin/template-questions"; + +const StyledButton = styled(Button) <{ $colorOverride: "red" }>` + background-color: ${(p) => p.$colorOverride}; + border-radius: 8px; + color: white; + font-size: 13px; + font-weight: bold; +`; + +const confirmDelete = async (template: TemplateQuestion) => { + if (window.confirm(`Delete: ${template.id}: ${template.name}; Are you sure?`) === true) { + try { + await SignupApi.deleteTemplateQuestion(template.id); + toast.success("Template question removed successfully 😎"); + window.location.reload(); // TODO: Fetch/update event list, so user sees the signup in the list + } catch (err) { + toast.error("Uh oh! Something went wrong! Try again later. 😟"); + } + } +}; + +const renderData = (templates: TemplateQuestion[]) => { + if (templates.length === 0) { + return
No signup forms.
; + } + + return ( + + + + + + + + {templates.map((template) => ( + + + + + ))} + +
Name
{template.name} + confirmDelete(template)}> + Delete + +
+ ); +}; + +const AdminSignupTemplateQuestions: NextPage = () => { + const [allTemplates, setTemplates] = useState([]); + + useEffect(() => { + SignupApi.getTemplateQuestions(true) + .then((res) => setTemplates(res)); + }, []); + + console.log(allTemplates); + + return ( + +

Sign-up forms

+ + {renderData(allTemplates)} +
+ ); +}; + +export default AdminSignupTemplateQuestions; diff --git a/src/views/admin/AdminCreateCommon.tsx b/src/views/admin/AdminCreateCommon.tsx index f3a5646..8243d19 100644 --- a/src/views/admin/AdminCreateCommon.tsx +++ b/src/views/admin/AdminCreateCommon.tsx @@ -6,6 +6,7 @@ import Event from "@models/Event"; import Post from "@models/Feed"; import { SignupForm } from "@models/Signup"; import JobAd from "@models/JobAd"; +import { TemplateQuestion } from "@models/TemplateQuestion"; import FormWrapper from "@views/common/FormWrapper"; import AdminPageWrapper from "@views/common/AdminPageWrapper"; @@ -24,7 +25,7 @@ const ErrorMsg = styled.p` display: inline-block; `; -type FormTypes = Event | SignupForm | Post | JobAd; +type FormTypes = Event | SignupForm | Post | JobAd | TemplateQuestion; type AdminCreateCommonProps = { title: string;