diff --git a/src/models/Signup.ts b/src/models/Signup.ts index 3e11a4e..d788e8f 100644 --- a/src/models/Signup.ts +++ b/src/models/Signup.ts @@ -8,7 +8,7 @@ export interface Signup { answer: string; } -export async function getSignup(id: number): Promise { +export const getSignup = async (id: number): Promise => { try { const resp = await axios.get(`${url}${id}`, { headers: { @@ -22,7 +22,7 @@ export async function getSignup(id: number): Promise { } } -export async function createSignup(data: Signup): Promise { +export const createSignup = async (data: Signup): Promise => { try { const resp = await axios.post(url, data); return resp.data; @@ -32,7 +32,7 @@ export async function createSignup(data: Signup): Promise { } } -export async function updateSignup(data: Signup, uuid: string): Promise { +export const updateSignup = async (data: Signup, uuid: string): Promise => { try { const { id } = data; if (!id) throw new Error("SignupId required!"); @@ -46,7 +46,7 @@ export async function updateSignup(data: Signup, uuid: string): Promise } } -export async function getSignupUUID(id: number, uuid: string): Promise { +export const getSignupUUID = async (id: number, uuid: string): Promise => { try { const resp = await axios.get(`${url}${id}/edit/`, { params: { @@ -59,3 +59,17 @@ export async function getSignupUUID(id: number, uuid: string): Promise { throw err; } } + +export const deleteSignup = async (id: number): Promise => { + try { + const resp = await axios.delete(`${url}${id}`, { + headers: { + "Authorization": getAuthHeader() + }, + }); + return resp.data; + } catch (err) { + console.error(err); + throw err; + } +} diff --git a/src/models/SignupForm.ts b/src/models/SignupForm.ts index 923ee86..b17f737 100644 --- a/src/models/SignupForm.ts +++ b/src/models/SignupForm.ts @@ -2,6 +2,7 @@ import axios from "axios"; import { getAuthHeader } from "@utils/auth"; const url = `${process.env.API_URL}/signupForm/`; import { Question } from "@components/Widgets/SignupQuestionsWidget"; +import { Signup } from "./Signup"; export interface SignupForm { id?: number; @@ -77,3 +78,31 @@ export async function updateForm(data): Promise { throw err; } } + +export const signupFormSendEmail = async (data, id): Promise => { + try { + const resp = await axios.post(`${process.env.API_URL}/signupForm/${id}/sendemail/`, data, { + headers: { + "Authorization": getAuthHeader(), + }, + }); + return resp.data; + } catch (err) { + console.error(err); + throw err; + } +} + +export const getSignups = async (id): Promise => { + try { + const resp = await axios.get(`${process.env.API_URL}/signupForm/${id}/signups/`, { + headers: { + "Authorization": getAuthHeader(), + }, + }); + return resp.data; + } catch (err) { + console.error(err); + throw err; + } +} diff --git a/src/pages/admin/AdminSignupPage.tsx b/src/pages/admin/AdminSignupPage.tsx index 3239574..1b1d425 100644 --- a/src/pages/admin/AdminSignupPage.tsx +++ b/src/pages/admin/AdminSignupPage.tsx @@ -20,6 +20,8 @@ const renderData = (signupForms: SignupForm[]) => { Title Start time End time + Sign-ups + Send email @@ -28,6 +30,8 @@ const renderData = (signupForms: SignupForm[]) => { {signupForm.title_fi} {formatRelative(new Date(signupForm.start_time), new Date())} {formatRelative(new Date(signupForm.end_time), new Date())} + View + Send ))} diff --git a/src/pages/admin/SignupEmailPage.tsx b/src/pages/admin/SignupEmailPage.tsx new file mode 100644 index 0000000..8cd84bf --- /dev/null +++ b/src/pages/admin/SignupEmailPage.tsx @@ -0,0 +1,105 @@ +import React, { useEffect, useState } from "react"; +import { Helmet } from "react-helmet"; +import { RouteComponentProps } from "react-router-dom"; +import AdminCreateCommon from "@views/admin/AdminCreateCommon"; +import MarkdownEditorWidget from "@components/Widgets/MarkdownEditorWidget"; +import { SignupForm, getForm, signupFormSendEmail } from "@models/SignupForm"; + +const widgets = { + markdownEditor: MarkdownEditorWidget +}; + +const buildSchema = (title: string) => { + return { + title, + type: "object", + required: ["subject", "content", "mode"], + properties: { + subject: { + type: "string", + title: "Title", + default: "" + }, + content: { + type: "string", + title: "Content", + default: "", + }, + mode: { + type: "string", + title: "Send to", + enum : [ + "all", + "actual", + "reserved" + ], + default: "all", + } + } + }; +} + +const buildUISchema = () => ({ + content: { + "ui:widget": "markdownEditor", + }, +}); + +interface MatchParams { + id?: string; +} + +type SignupEmailPageProps = RouteComponentProps; + + +const SignupEmailPage: React.FC = ({ match: { params: { id } } }) => { + const [signupForm, setSignupForm] = useState(null); + useEffect(() => { + const formId = Number(id); + if (formId !== undefined) { + getForm(formId, true) + .then(res => setSignupForm(res)) + } + }, [id]) + + + + const [error, setError] = useState(null); + const [statusMessage, setStatusMessage] = useState(null); + + const onSubmit = async (data) => { + try { + const payload = data.formData; + await signupFormSendEmail(payload, id); + setStatusMessage("Email sent successfully"); + } catch (err) { + setError(err); + } + } + + // const onChange = (data) => setFormData(data.formData); + const onFocus = () => setStatusMessage(null); + + const title = signupForm ? signupForm.title_fi : "Loading..." + + return ( + <> + + + + + + )}; + +export default SignupEmailPage; diff --git a/src/pages/admin/SignupListPage.tsx b/src/pages/admin/SignupListPage.tsx new file mode 100644 index 0000000..186bed7 --- /dev/null +++ b/src/pages/admin/SignupListPage.tsx @@ -0,0 +1,79 @@ +import React, { useEffect, useState } from "react"; +import { Helmet } from "react-helmet"; +import { RouteComponentProps } from "react-router-dom"; +import AdminListCommon from "@views/admin/AdminListCommon"; +import { SignupForm, getForm, getSignups } from "@models/SignupForm"; +import { Signup, deleteSignup } from "@models/Signup"; + +interface MatchParams { + id?: string; +} + +type SignupEmailPageProps = RouteComponentProps; + + +const SignupEmailPage: React.FC = ({ match: { params: { id } } }) => { + const [signupForm, setSignupForm] = useState(null); + const [signups, setSignups] = useState([]); + useEffect(() => { + const formId = Number(id); + getForm(formId, true) + .then(res => setSignupForm(res)) + + getSignups(formId).then(res => setSignups(res)) + + }, [id]); + + const questions = signupForm ? signupForm.questions.map(q => ({ + title: q.name, + id: q.id + })) : []; + + const title = signupForm ? signupForm.title_fi : "Loading..."; + + const confirmDelete = async (signup: Signup, question: any) => { + if(confirm(`Delete: ${signup.id}: ${signup.answer[question.id]}; Are you sure?`) === true) { + try { + await deleteSignup(signup.id); + setSignups(signups.filter(s => s.id !== signup.id)) + } catch (err) { + alert("Delete failed!") + } + } + } + + return ( + + + + +

{title}: Sign-ups

+ + + + {questions.map(q => ( + + ))} + + + + {signups.map(s => ( + + {questions.map(q => ( + + ))} + + + ))} + +
{q.title}
+ {s.answer[q.id]} + + +
+
+ )}; + +export default SignupEmailPage; diff --git a/src/routes.tsx b/src/routes.tsx index bfa5b8a..eb34590 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -18,6 +18,8 @@ import EventCreatePage from "./pages/admin/EventCreatePage"; import FeedCreatePage from "./pages/admin/FeedCreatePage"; import ContactsPage from "./pages/ContactsPage"; import SignupCreatePage from "./pages/admin/SignupCreatePage"; +import SignupEmailPage from "./pages/admin/SignupEmailPage"; +import SignupListPage from "./pages/admin/SignupListPage"; import SignUpPage from "./pages/SignUpPage"; import ActualPage from "./pages/ActualPage"; import FreshmenPage from "./pages/FreshmenPage"; @@ -70,6 +72,8 @@ const adminRoutes = [ { path: "/admin/signups", page: AdminSignupPage }, { path: "/admin/signups/create", page: SignupCreatePage }, { path: "/admin/signups/:id", page: SignupCreatePage }, + { path: "/admin/signups/:id/list", page: SignupListPage }, + { path: "/admin/signups/:id/email", page: SignupEmailPage }, { path: "/admin/jobads", page: AdminJobAdPage }, { path: "/admin/jobads/create", page: JobAdCreatePage }, { path: "/admin/jobads/:id", page: JobAdCreatePage }, diff --git a/src/views/SignUpPage/FormUtils.tsx b/src/views/SignUpPage/FormUtils.tsx index b07af3b..7636779 100644 --- a/src/views/SignUpPage/FormUtils.tsx +++ b/src/views/SignUpPage/FormUtils.tsx @@ -16,9 +16,6 @@ const questionToUISchemaProp = (question: Question) => { "ui:widget": "radio", } } - else { - throw new Error(`No mapping to UI schema prop for question type ${question.type}`); - } return { [question.id]: obj, }; diff --git a/src/views/SignUpPage/SignUpPageView.tsx b/src/views/SignUpPage/SignUpPageView.tsx index 530a79d..7d425f6 100644 --- a/src/views/SignUpPage/SignUpPageView.tsx +++ b/src/views/SignUpPage/SignUpPageView.tsx @@ -51,7 +51,7 @@ const StyledSection = styled(TextSection)` } .reserved { - color: color(blue1); + color: ${colors.blue1}; } } diff --git a/src/views/admin/AdminCreateCommon.tsx b/src/views/admin/AdminCreateCommon.tsx index 3a9b750..03dd22f 100644 --- a/src/views/admin/AdminCreateCommon.tsx +++ b/src/views/admin/AdminCreateCommon.tsx @@ -109,7 +109,7 @@ type AdminCreateCommonProps = { UISchema: { [name: string]: any; }; - onChange: (e: IChangeEvent, es?: ErrorSchema) => any; + onChange?: (e: IChangeEvent, es?: ErrorSchema) => any; onFocus: (id: string, value: string | number | boolean) => void; onSubmit: (e: ISubmitEvent) => any; statusMessage: string;