From 13a38ea31e2fc30ff230694f0a38106a274c5850 Mon Sep 17 00:00:00 2001 From: Alexander Schoch <schochal@student.ethz.ch> Date: Fri, 15 Sep 2023 18:39:36 +0200 Subject: [PATCH] implement evnets --- cinit.yml | 15 + components/about.jsx | 4 +- components/editor.jsx | 39 + components/eventCard.jsx | 213 +++ components/eventModal.jsx | 215 ++- components/events.jsx | 80 +- components/header.jsx | 5 +- components/map.jsx | 2 +- components/noAdblockBanner.jsx | 2 - components/philosophy.jsx | 7 +- components/signUpsModal.jsx | 42 + content/eventTemplates.json | 97 ++ graphql/context.js | 11 + graphql/resolvers.js | 95 ++ graphql/schema.js | 41 + package-lock.json | 1157 ++++++++++++++++- package.json | 8 + pages/api/graphql.js | 33 + prisma.sh | 2 +- .../migration.sql | 15 + .../migration.sql | 4 + .../migration.sql | 2 + .../20230915132050_add_sign_up/migration.sql | 11 + .../migration.sql | 12 + prisma/migrations/migration_lock.toml | 3 + prisma/schema.prisma | 35 + public/locales/de/common.json | 19 +- public/locales/en/common.json | 19 +- styles/globals.css | 4 + utilities/dates.js | 24 + utilities/hasAccess.js | 5 +- utilities/signUp.js | 8 + 32 files changed, 2177 insertions(+), 52 deletions(-) create mode 100644 components/editor.jsx create mode 100644 components/eventCard.jsx create mode 100644 components/signUpsModal.jsx create mode 100644 content/eventTemplates.json create mode 100644 graphql/context.js create mode 100644 graphql/resolvers.js create mode 100644 graphql/schema.js create mode 100644 pages/api/graphql.js create mode 100644 prisma/migrations/20230915103134_initial_migration/migration.sql create mode 100644 prisma/migrations/20230915104240_store_dates_as_string/migration.sql create mode 100644 prisma/migrations/20230915121556_description_is_now_text/migration.sql create mode 100644 prisma/migrations/20230915132050_add_sign_up/migration.sql create mode 100644 prisma/migrations/20230915135047_add_name_and_email_to_sign_up/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 prisma/schema.prisma create mode 100644 utilities/dates.js create mode 100644 utilities/signUp.js diff --git a/cinit.yml b/cinit.yml index 93707d1..820b047 100644 --- a/cinit.yml +++ b/cinit.yml @@ -20,3 +20,18 @@ programs: - MAILER_PASSWORD: - MAILER_HOST: - MAILER_NAME: + - name: 'Update DB Schema' + workdir: '/website' + path: 'bash' + args: + - 'prisma.sh' + state: 'running' + exit_code: 0 + env: + - SIP_MYSQL_ALT_NAME: + - SIP_MYSQL_ALT_USER: + - SIP_MYSQL_ALT_PW: + - SIP_MYSQL_ALT_SERVER: + - SIP_MYSQL_ALT_PORT: + - NEXTAUTH_URL: + - NEXTAUTH_SECRET: \ No newline at end of file diff --git a/components/about.jsx b/components/about.jsx index 2a13339..5b32fc8 100644 --- a/components/about.jsx +++ b/components/about.jsx @@ -55,7 +55,9 @@ export default function About() { <b>TheAlternative</b> {about.address.map((line, i) => ( - <p key={i}>{line}</p> + <p key={i} style={{ marginBottom: 0 }}> + {line} + </p> ))} <Space h="xs" /> diff --git a/components/editor.jsx b/components/editor.jsx new file mode 100644 index 0000000..7bceb14 --- /dev/null +++ b/components/editor.jsx @@ -0,0 +1,39 @@ +import { RichTextEditor } from "@mantine/tiptap"; + +export default function Editor({ editor }) { + return ( + <RichTextEditor editor={editor}> + <RichTextEditor.Toolbar sticky> + <RichTextEditor.ControlsGroup> + <RichTextEditor.Bold /> + <RichTextEditor.Italic /> + <RichTextEditor.Underline /> + <RichTextEditor.Strikethrough /> + <RichTextEditor.ClearFormatting /> + <RichTextEditor.Code /> + </RichTextEditor.ControlsGroup> + + <RichTextEditor.ControlsGroup> + <RichTextEditor.H1 /> + <RichTextEditor.H2 /> + <RichTextEditor.H3 /> + <RichTextEditor.H4 /> + </RichTextEditor.ControlsGroup> + + <RichTextEditor.ControlsGroup> + <RichTextEditor.Blockquote /> + <RichTextEditor.Hr /> + <RichTextEditor.BulletList /> + <RichTextEditor.OrderedList /> + </RichTextEditor.ControlsGroup> + + <RichTextEditor.ControlsGroup> + <RichTextEditor.Link /> + <RichTextEditor.Unlink /> + </RichTextEditor.ControlsGroup> + </RichTextEditor.Toolbar> + + <RichTextEditor.Content /> + </RichTextEditor> + ); +} diff --git a/components/eventCard.jsx b/components/eventCard.jsx new file mode 100644 index 0000000..8aa3daa --- /dev/null +++ b/components/eventCard.jsx @@ -0,0 +1,213 @@ +import { useRouter } from "next/router"; + +import { useSession, signIn } from "next-auth/react"; + +import { useTranslation } from "next-i18next"; + +import { + Alert, + Button, + Card, + Group, + List, + Space, + useMantineTheme, +} from "@mantine/core"; + +import { Icon, ICONS } from "vseth-canine-ui"; + +import parse from "html-react-parser"; + +import hasAccess from "../utilities/hasAccess"; +import { getAccentColor } from "../utilities/colors"; +import { formatDateFromDB, formatTimeFromDB } from "../utilities/dates"; +import { isRegistered } from "../utilities/signUp"; + +import { gql, useMutation } from "@apollo/client"; + +const addSignUpMutation = gql` + mutation addSignUp($id: Int) { + addSignUp(id: $id) + } +`; + +const removeSignUpMutation = gql` + mutation removeSignUp($id: Int) { + removeSignUp(id: $id) + } +`; + +export default function EventCard({ + event, + setEvent, + setOpen, + refetch, + setSignUpOpen, +}) { + const [addSignUp] = useMutation(addSignUpMutation); + const [removeSignUp] = useMutation(removeSignUpMutation); + + const { data: session } = useSession(); + const { t } = useTranslation("common"); + const theme = useMantineTheme(); + + const { locale } = useRouter(); + + const accentColor = getAccentColor(theme); + const signedUp = isRegistered(session, event); + + const signUp = async (id) => { + if (!session) signIn(); + await addSignUp({ + variables: { + id: event.id, + }, + }); + refetch(); + }; + + const signOff = async (id) => { + await removeSignUp({ + variables: { + id: event.id, + }, + }); + refetch(); + }; + + const editEvent = (event) => { + setEvent(event); + setOpen(true); + }; + + const viewSignUps = (event) => { + setEvent(event); + setSignUpOpen(true); + }; + + return ( + <Card + shadow="md" + radius="md" + style={{ + display: "flex", + justifyContent: "space-between", + flexDirection: "column", + width: "100%", + color: event.isStammtisch ? "gray" : "", + }} + withBorder + > + <div> + <Group> + <h2 style={{ margin: 0 }}>{event.title}</h2> + </Group> + + {signedUp && ( + <> + <Space h="xs" /> + <Alert + color="green" + variant="light" + icon={<Icon icon={ICONS.CHECK} color="green" />} + title={t("signedUpTitle")} + > + {t("signedUpText")} + </Alert> + </> + )} + + <Space h="lg" /> + <List + spacing="md" + size="md" + center + style={{ color: event.isStammtisch ? "gray" : "" }} + > + {event.speaker && ( + <List.Item + icon={<Icon icon={ICONS.USER} color="#f28a20" size={16} />} + > + <p>{event.speaker}</p> + </List.Item> + )} + <List.Item + icon={<Icon icon={ICONS.LOCATION} color="#f28a20" size={16} />} + > + <p>{event.place}</p> + </List.Item> + <List.Item + icon={<Icon icon={ICONS.CALENDAR} color="#f28a20" size={16} />} + > + <p>{formatDateFromDB(event.date, locale)}</p> + </List.Item> + <List.Item + icon={<Icon icon={ICONS.CLOCK} color="#f28a20" size={16} />} + > + <p>{formatTimeFromDB(event.startTime, event.endTime)}</p> + </List.Item> + </List> + <Space h="lg" /> + + <p>{parse(event.description)}</p> + </div> + <div> + {!event.isStammtisch && ( + <Group style={{ verticalAlign: "middle", marginTop: "20px" }}> + {event.signUp ? ( + <Button + href={event.signUp} + target="_blank" + rightIcon={<Icon icon={ICONS.RIGHT} color="#f28a20" />} + component="a" + > + {t("externalSignUp")} + </Button> + ) : ( + <> + {signedUp ? ( + <Button + onClick={() => signOff(event.id)} + leftIcon={<Icon icon={ICONS.USER_MINUS} color="white" />} + > + {t("signOff")} + </Button> + ) : ( + <Button + onClick={() => signUp(event.id)} + leftIcon={<Icon icon={ICONS.USER_PLUS} color="#f28a20" />} + style={{ color: "#f28a20" }} + > + {t("signUp")} + </Button> + )} + </> + )} + </Group> + )} + <Group grow mt="md" style={{ verticalAlign: "middle" }}> + {hasAccess(session, true) && ( + <> + <Button + leftIcon={<Icon icon={ICONS.EDIT} color={accentColor} />} + variant="light" + onClick={() => editEvent(event)} + > + {t("editEvent")} + </Button> + {!event.isStammtisch && ( + <Button + leftIcon={<Icon icon={ICONS.LIST} color={accentColor} />} + variant="light" + onClick={() => viewSignUps(event)} + > + {t("viewParticipants")} + </Button> + )} + </> + )} + </Group> + </div> + </Card> + ); +} diff --git a/components/eventModal.jsx b/components/eventModal.jsx index 46b94a2..e1bc58e 100644 --- a/components/eventModal.jsx +++ b/components/eventModal.jsx @@ -1,19 +1,95 @@ +import { useEffect } from "react"; + import { useTranslation } from "next-i18next"; -import { Grid, Modal, Text, Textarea, TextInput } from "@mantine/core"; +import { + Button, + Checkbox, + Divider, + Grid, + Modal, + Select, + Space, + Text, + Textarea, + TextInput, +} from "@mantine/core"; import { useForm } from "@mantine/form"; -//import { DateTimePicker } from '@mantine/dates'; +import { DatePicker, TimeRangeInput } from "@mantine/dates"; + +import { useEditor } from "@tiptap/react"; +import { Link } from "@mantine/tiptap"; +import StarterKit from "@tiptap/starter-kit"; +import Underline from "@tiptap/extension-underline"; + +import Editor from "../components/editor"; + +import templates from "../content/eventTemplates.json"; + +import { gql, useMutation } from "@apollo/client"; + +const addEventMutation = gql` + mutation addEvent( + $title: String + $speaker: String + $description: String + $date: DateTime + $time: [DateTime] + $place: String + $signUp: String + $isStammtisch: Boolean + ) { + addEvent( + title: $title + speaker: $speaker + description: $description + date: $date + time: $time + place: $place + signUp: $signUp + isStammtisch: $isStammtisch + ) + } +`; + +const editEventMutation = gql` + mutation editEvent( + $id: Int + $title: String + $speaker: String + $description: String + $date: DateTime + $time: [DateTime] + $place: String + $signUp: String + $isStammtisch: Boolean + ) { + editEvent( + id: $id + title: $title + speaker: $speaker + description: $description + date: $date + time: $time + place: $place + signUp: $signUp + isStammtisch: $isStammtisch + ) + } +`; + +export default function EventModal({ open, close, event, refetch }) { + const [addEvent] = useMutation(addEventMutation); + const [editEvent] = useMutation(editEventMutation); -export default function EventModal({ open, close, event }) { const { t } = useTranslation("common"); const initialValues = { title: "", - description: "", speaker: "", - start: null, - end: null, + date: null, + time: null, place: "", signUp: "", isStammtisch: false, @@ -24,20 +100,89 @@ export default function EventModal({ open, close, event }) { validate: { title: (value) => (value ? null : t("ENotEmpty")), - description: (value) => (value ? null : t("ENotEmpty")), - speaker: (value) => (value ? null : t("ENotEmpty")), - start: (value) => (value ? null : t("ENotEmpty")), - end: (value) => (value ? null : t("ENotEmpty")), + date: (value) => (value ? null : t("ENotEmpty")), + time: (value) => (value ? null : t("ENotEmpty")), place: (value) => (value ? null : t("ENotEmpty")), - signUp: (value) => (value ? null : t("ENotEmpty")), }, }); + const content = ""; + + const editor = useEditor({ + extensions: [StarterKit, Underline, Link], + content, + }); + + const templatesFmt = templates.map((template) => ({ + label: template.title, + value: template.title, + })); + + const setTemplate = (t) => { + const template = templates.filter((temp) => temp.title === t)[0]; + let timeArr = [new Date(), new Date()]; + if (template.time) { + timeArr[0].setHours( + Number(template.time[0].split(":")[0]), + Number(template.time[0].split(":")[1]), + 0 + ); + timeArr[1].setHours( + Number(template.time[1].split(":")[0]), + Number(template.time[1].split(":")[1]), + 0 + ); + } + + form.setValues({ + ...template, + time: template.time ? [...timeArr] : null, + }); + editor.commands.setContent(template.description); + }; + const setEvent = () => { if (event) { - form.setValues({ ...event }); + form.setValues({ + ...event, + date: new Date(event.date), + time: [new Date(event.startTime), new Date(event.endTime)], + }); + editor.commands.setContent(event.description); } else { form.setValues(initialValues); + editor.commands.setContent(content); + } + }; + + useEffect(() => { + setEvent(); + }, [open]); + + const submit = async (values) => { + if (event) { + const res = await editEvent({ + variables: { + id: event.id, + ...values, + description: editor.getHTML(), + }, + }); + if (res.data.editEvent) { + refetch(); + close(); + } + } else { + const res = await addEvent({ + variables: { + ...values, + description: editor.getHTML(), + }, + }); + if (res.data.addEvent) { + refetch(); + close(); + } } }; @@ -47,6 +192,20 @@ export default function EventModal({ open, close, event }) { {event ? t("editEvent") : t("addEvent")} </Text> + <Space h="xl" /> + + <Select + data={templatesFmt} + placeholder={t("loadTemplate")} + onChange={(e) => setTemplate(e)} + /> + + <Space h="xl" /> + + <Divider /> + + <Space h="xs" /> + <form onSubmit={form.onSubmit((values) => submit(values))}> <Grid> <Grid.Col sm={12} md={6}> @@ -66,11 +225,23 @@ export default function EventModal({ open, close, event }) { /> </Grid.Col> <Grid.Col sm={12}> - <Textarea + <Text fz="sm">{t("description")}</Text> + <Editor editor={editor} /> + </Grid.Col> + <Grid.Col sm={12} md={6}> + <DatePicker + placeholder="January 1, 1970" + label={t("date")} withAsterisk - label={t("description")} - placeholder="Lorem Ipsum..." - {...form.getInputProps("description")} + {...form.getInputProps("date")} + /> + </Grid.Col> + <Grid.Col sm={12} md={6}> + <TimeRangeInput + label={t("time")} + withAsterisk + clearable + {...form.getInputProps("time")} /> </Grid.Col> <Grid.Col sm={12} md={6}> @@ -81,15 +252,23 @@ export default function EventModal({ open, close, event }) { {...form.getInputProps("place")} /> </Grid.Col> - <Grid.Col sm={12} md={6}></Grid.Col> <Grid.Col sm={12} md={6}> <TextInput - withAsterisk label={t("signUp")} placeholder="https://..." {...form.getInputProps("signUp")} /> </Grid.Col> + <Grid.Col sm={12} md={3}> + <Checkbox + mt="xl" + label={t("stammtisch")} + {...form.getInputProps("isStammtisch", { type: "checkbox" })} + /> + </Grid.Col> + <Grid.Col sm={12}> + <Button type="submit">{t("submit")}</Button> + </Grid.Col> </Grid> </form> </Modal> diff --git a/components/events.jsx b/components/events.jsx index b1619bc..7e79552 100644 --- a/components/events.jsx +++ b/components/events.jsx @@ -2,25 +2,99 @@ import { useState } from "react"; import { useTranslation } from "next-i18next"; -import { Button } from "@mantine/core"; +import { Button, Grid, Space, useMantineTheme } from "@mantine/core"; + +import { Icon, ICONS } from "vseth-canine-ui"; import EventModal from "../components/eventModal"; +import SignUpsModal from "../components/signUpsModal"; +import EventCard from "../components/eventCard"; +import { getAccentColor } from "../utilities/colors"; + +import { gql, useQuery } from "@apollo/client"; + +const getFutureEventsQuery = gql` + query getFutureEvents { + getFutureEvents { + id + title + speaker + description + date + startTime + endTime + place + signUp + isStammtisch + signUps { + sub + firstName + lastName + email + } + } + } +`; export default function Events() { + const { data: events, refetch } = useQuery(getFutureEventsQuery); const [open, setOpen] = useState(false); + const [signUpOpen, setSignUpOpen] = useState(false); const [event, setEvent] = useState(null); + const theme = useMantineTheme(); const { t } = useTranslation("common"); + const addEvent = () => { + setEvent(null); + setOpen(true); + }; + return ( <> <h1>{t("events")}</h1> - <Button onClick={() => setOpen(true)} variant="light"> + <Button + leftIcon={<Icon icon={ICONS.PLUS} color={getAccentColor(theme)} />} + onClick={addEvent} + variant="default" + > {t("addEvent")} </Button> - <EventModal open={open} event={event} close={() => setOpen(false)} /> + <EventModal + open={open} + event={event} + close={() => setOpen(false)} + refetch={refetch} + /> + <SignUpsModal + open={signUpOpen} + event={event} + close={() => setSignUpOpen(false)} + /> + + <Space h="xl" /> + <Grid> + {events && + events.getFutureEvents.map((e) => ( + <Grid.Col + md={4} + sm={6} + xs={12} + key={e.id} + style={{ display: "flex" }} + > + <EventCard + event={e} + setEvent={setEvent} + setOpen={setOpen} + refetch={refetch} + setSignUpOpen={setSignUpOpen} + /> + </Grid.Col> + ))} + </Grid> </> ); } diff --git a/components/header.jsx b/components/header.jsx index 53b1f0d..2db25ea 100644 --- a/components/header.jsx +++ b/components/header.jsx @@ -54,10 +54,7 @@ export default function Header() { <Space h="xl" /> <Space h="xl" /> {content.topSection.map((entry, i) => ( - <div key={i}> - <p>{parse(entry[locale || "en"])}</p> - <Space h="xs" /> - </div> + <p key={i}>{parse(entry[locale || "en"])}</p> ))} </div> </Grid.Col> diff --git a/components/map.jsx b/components/map.jsx index 5cb5680..aed21aa 100644 --- a/components/map.jsx +++ b/components/map.jsx @@ -101,7 +101,7 @@ export default function OfficeMap() { > <b style={{ color: "black" }}>TheAlternative</b> {about.address.map((line, i) => ( - <p key={i} style={{ color: "black" }}> + <p key={i} style={{ color: "black", marginBottom: 0 }}> {line} </p> ))} diff --git a/components/noAdblockBanner.jsx b/components/noAdblockBanner.jsx index f932592..7616a45 100644 --- a/components/noAdblockBanner.jsx +++ b/components/noAdblockBanner.jsx @@ -13,8 +13,6 @@ export default function NoAdblockBanner() { const [hasAdblock, setHasAdblock] = useState(true); - console.log(hasAdblock); - useEffect(() => { isAdblocking().then((isAdblocking) => { setHasAdblock(isAdblocking); diff --git a/components/philosophy.jsx b/components/philosophy.jsx index af55e92..07451a6 100644 --- a/components/philosophy.jsx +++ b/components/philosophy.jsx @@ -19,11 +19,8 @@ export default function Philosophy() { {entry.summary[locale || "en"]} </Blockquote> - {entry.definition[locale || "en"].map((def) => ( - <> - <p>{parse(def)}</p> - <Space h="xs" /> - </> + {entry.definition[locale || "en"].map((def, i) => ( + <p key={i}>{parse(def)}</p> ))} <Space h="md" /> diff --git a/components/signUpsModal.jsx b/components/signUpsModal.jsx new file mode 100644 index 0000000..648aaec --- /dev/null +++ b/components/signUpsModal.jsx @@ -0,0 +1,42 @@ +import { useTranslation } from "next-i18next"; + +import { Button, Modal, Table } from "@mantine/core"; + +export default function SignUpsModal({ open, close, event }) { + const { t } = useTranslation("common"); + + const copyMailAddresses = () => { + const addresses = event.signUps.map((signUp) => signUp.email).join(", "); + navigator.clipboard.writeText(addresses); + }; + + if (!event) return <></>; + + return ( + <Modal opened={open} onClose={close} size="xl"> + <h2>{t("participants")}</h2> + <Table striped highlightOnHover> + <thead> + <tr> + <th>Name</th> + <th>Email</th> + </tr> + </thead> + <tbody> + {event.signUps.map((signUp, i) => ( + <tr> + <td> + {signUp.firstName} {signUp.lastName} + </td> + <td>{signUp.email}</td> + </tr> + ))} + </tbody> + </Table> + + <Button onClick={copyMailAddresses} mt="md"> + {t("copyMailAddresses")} + </Button> + </Modal> + ); +} diff --git a/content/eventTemplates.json b/content/eventTemplates.json new file mode 100644 index 0000000..6aec716 --- /dev/null +++ b/content/eventTemplates.json @@ -0,0 +1,97 @@ +[ + { + "title": "Stammtisch", + "description": "Join our Stammtisch! We meet in a casual atmosphere, have a drink and discuss upcoming technical trends. If you have troubles with Linux, bring your Laptop and we will be glad to help.", + "time": [ + "18:00", + "22:00" + ], + "place": "ETH CAB E 14", + "isStammtisch": true + }, + { + "title": "Introduction to Open Source Software", + "description": "<p>Discover the differences between Free and Open Source Software (FOSS) and proprietary software, and find out why these differences matter.</p><p>This captivating lecture will provide you with information about the advantages of Free Software licenses, as well as show you the historical background of the Linux kernel.</p><p>Additionally, we show why Free Software and Open Data is important even beyond the world of Linux.</p><p>Learn how you can regain full control over your data!</p>", + "time": [ + "18:15", + "20:00" + ], + "isStammtisch": false + }, + { + "title": "Introduction to Linux", + "description": "<p>In this course you will learn about the wide family of free operating systems based on the Linux kernel, some of the most useful software tools available for them, as well as the philosophy that made them so successful.</p><p>Find out how easy to use and yet powerful Linux systems are, discover their advantages when compared to other operating systems, and get to understand their basic structure and most important concepts.</p><p>Furthermore, discover different flavours of Linux and get a feeling for which distribution and desktop environment would be the right match for you.</p>", + "time": [ + "18:15", + "20:00" + ], + "isStammtisch": false + }, + { + "title": "Install Event (ETH)", + "speaker": "TheAlternative", + "description": "<p>Get started with Linux by installing it on your computer.</p><p>Of course, we'll help you: Bring your laptop and charger and we will personally assist you with the installation of Linux as well as with your first steps and basic customization.</p><p>You can choose to keep your existing operating system and install Linux next to it.</p><p>Still, it's important to back up your data before you come, just to be on the safe side.</p><p>You may of course stay longer than the indicated time should we encounter issues with installing Linux on your laptop.</p><p>ChromeBooks (all versions), Apple MacBook Pros (after Oct. 2016) and Apple MacBooks (12\" model) are not well supported by Linux due to hardware restrictions, and we do not recommend installing Linux on those devices.</p><p>If you intend to buy a new laptop to install Linux on, stay away from these devices altogether.</p>", + "time": [ + "17:30", + "21:00" + ], + "place": "Student Project House (Clausiusstrasse 16, 8006 Zürich)", + "isStammtisch": false + }, + { + "title": "Install Event (UZH)", + "speaker": "TheAlternative", + "description": "<p>Get started with Linux by installing it on your computer.</p><p>Of course, we'll help you: Bring your laptop and charger and we will personally assist you with the installation of Linux as well as with your first steps and basic customization.</p><p>You can choose to keep your existing operating system and install Linux next to it.</p><p>Still, it's important to back up your data before you come, just to be on the safe side.</p><p>You may of course stay longer than the indicated time should we encounter issues with installing Linux on your laptop.</p><p>ChromeBooks (all versions), Apple MacBook Pros (after Oct. 2016) and Apple MacBooks (12\" model) are not well supported by Linux due to hardware restrictions, and we do not recommend installing Linux on those devices.</p><p>If you intend to buy a new laptop to install Linux on, stay away from these devices altogether.</p>", + "time": [ + "17:30", + "21:00" + ], + "isStammtisch": false + }, + { + "title": "Console Toolkit", + "description": "<p>This event is a collaboration with Student Project House.</p><p>Linux is well-known for being configurable and fitting many different styles of use. But what can you do with it?</p><p>Our goal in the Linux Toolbox course is to introduce a large range of tools and to teach you how you can profit from them. We'll show you typical tasks that Linux is good at, such as renaming hundreds of files, searching and replacing text, automatic downloading etc.. This way, you'll be able to recognize those tasks in the wild and don't have to do them by hand.</p><p>Linux has many useful tools, so we don't intend to cover them all in detail. We'll instead take a sweeping glance to cover as many use cases as possible and provide you with learning resources if you want to delve deeper into a topic. Most of the course will take place on the command line, but no prerequisite knowledge is required.</p>", + "time": [ + "18:15", + "20:00" + ], + "place": "Student Project House (Clausiusstrasse 16, 8006 Zürich)", + "isStammtisch": false + }, + { + "title": "Git and GitLab", + "description": "<p>This event is a collaboration with Student Project House.</p><p>Git is the state-of-the-art version control system, taking away the pain of continuously changing file versions. Keep track of who changed what, when and why, and revert changes easily.</p><p>GitLab allows you to save the files to the cloud, giving you a secure backup location and enabling easy collaboration. Never lose your files again, and coordinate changes in even bigger projects with ease.</p><p>This course gives you a solid understanding of git and GitLab, including its basic concepts.</p><p>We will have exercise breaks where you can apply the concepts at your own pace.</p>", + "time": [ + "18:15", + "20:00" + ], + "place": "Student Project House (Clausiusstrasse 16, 8006 Zürich)", + "isStammtisch": false + }, + { + "title": "Bash", + "description": "<p>This event is a collaboration with Student Project House.</p><p>Learn how you can use Bash to program simple scripts that can save you a lot of work. Bash is a scripting language that is designed to interact with other programs and your system easily. Whether you want to backup your newest files to a hard drive or quickly download a lot of PDFs—with Bash, you can do it at a keystroke.</p>", + "time": [ + "18:15", + "20:00" + ], + "place": "Student Project House (Clausiusstrasse 16, 8006 Zürich)", + "isStammtisch": false + }, + { + "title": "Spotlight 1", + "time": [ + "18:15", + "20:00" + ], + "isStammtisch": false + }, + { + "title": "Spotlight 2", + "time": [ + "18:15", + "20:00" + ], + "isStammtisch": false + } +] \ No newline at end of file diff --git a/graphql/context.js b/graphql/context.js new file mode 100644 index 0000000..b032679 --- /dev/null +++ b/graphql/context.js @@ -0,0 +1,11 @@ +import { PrismaClient } from "@prisma/client"; +import prisma from "../lib/prisma"; + +import { NextApiRequest, NextApiResponse } from "next"; + +export async function createContext(req, res) { + return { + prisma, + }; +} + diff --git a/graphql/resolvers.js b/graphql/resolvers.js new file mode 100644 index 0000000..ed3f0b8 --- /dev/null +++ b/graphql/resolvers.js @@ -0,0 +1,95 @@ +import prisma from "../lib/prisma"; +import { getServerSession } from "next-auth/next"; +import { authOptions } from "../pages/api/auth/[...nextauth]"; + +import hasAccess from "../utilities/hasAccess"; +import { isInFuture } from '../utilities/dates'; + +export const resolvers = { + Query: { + getEvents: async () => { + const events = await prisma.event.findMany(); + return events; + }, + getFutureEvents: async (_, data, { session }) => { + const events = await prisma.event.findMany({ + include: { + signUps: { + select: { + sub: true, + firstName: hasAccess(session, true), + lastName: hasAccess(session, true), + email: hasAccess(session, true) + } + }, + } + }); + const futureEvents = events.filter((event) => isInFuture(event)); + return futureEvents; + }, + }, + Mutation: { + addEvent: async (_, data, { session }) => { + if (!hasAccess(session, true)) return false; + + const {time, ...dataWithoutTime} = data; + const event = await prisma.event.create({ + data: { + ...dataWithoutTime, + startTime: data.time[0], + endTime: data.time[1] + }, + }); + return event ? true : false; + }, + editEvent: async (_, data, { session }) => { + if (!hasAccess(session, true)) return false; + + const {time, ...dataWithoutTime} = data; + const event = await prisma.event.update({ + where: { id: data.id }, + data: { + ...dataWithoutTime, + startTime: data.time[0], + endTime: data.time[1] + }, + }); + return event ? true : false; + }, + addSignUp: async(_, { id }, { session }) => { + if(!hasAccess(session, false)) return false; + + const signUp = await prisma.signUp.create({ + data: { + eventId: id, + sub: session.info.payload.sub, + firstName: session.info.payload.given_name, + lastName: session.info.payload.family_name, + email: session.info.payload.email + } + }); + + return signUp ? true : false; + }, + removeSignUp: async(_, { id }, { session }) => { + if(!hasAccess(session, false)) return false; + + const signUp = await prisma.signUp.findMany({ + where: { + eventId: id, + sub: session.info.payload.sub + } + }); + + if(signUp.length == 0) return false; + + await prisma.signUp.delete({ + where: { + id: signUp[0].id + } + }); + + return true; + } + }, +}; diff --git a/graphql/schema.js b/graphql/schema.js new file mode 100644 index 0000000..74a0f41 --- /dev/null +++ b/graphql/schema.js @@ -0,0 +1,41 @@ +import { DateTime } from 'graphql-scalars'; + +export const typeDefs = ` + scalar DateTime + + type Event { + id: Int + title: String + speaker: String + description: String + date: DateTime + startTime: DateTime + endTime: DateTime + place: String + signUp: String + isStammtisch: Boolean + signUps: [SignUp] + } + + type SignUp { + id: Int + sub: String + firstName: String + lastName: String + email: String + event: Event + } + + type Query { + getEvents: [Event] + getFutureEvents: [Event] + } + + type Mutation { + addEvent(title: String, speaker: String, description: String, date: DateTime, time: [DateTime], place: String, signUp: String, isStammtisch: Boolean): Boolean + editEvent(id: Int, title: String, speaker: String, description: String, date: DateTime, time: [DateTime], place: String, signUp: String, isStammtisch: Boolean): Boolean + addSignUp(id: Int): Boolean + removeSignUp(id: Int): Boolean + } +`; + diff --git a/package-lock.json b/package-lock.json index 4d6c58e..05a6fec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,20 @@ "version": "0.1.0", "dependencies": { "@apollo/client": "^3.8.1", + "@mantine/dates": "^5.10.5", "@mantine/form": "^6.0.20", "@mantine/notifications": "^5.10.5", + "@mantine/tiptap": "^5.10.5", + "@prisma/client": "^5.3.1", + "@tiptap/extension-underline": "^2.1.10", + "@tiptap/react": "^2.1.10", + "@tiptap/starter-kit": "^2.1.10", "adblock-hunter": "^1.0.1", "axios": "^1.5.0", "eslint": "8.48.0", "eslint-config-next": "13.4.19", + "graphql-scalars": "^1.22.2", + "graphql-yoga": "^4.0.4", "html-react-parser": "^4.2.1", "jsonwebtoken": "^9.0.1", "next": "13.4.19", @@ -2248,6 +2256,29 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, + "node_modules/@envelop/core": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@envelop/core/-/core-4.0.1.tgz", + "integrity": "sha512-uBLI7ql3hZopz7vMi9UDAb9HWzKw4STKiqg4QT+lb+tu5ZNaeuJ4fom2rrmgITz38B85QZOhZrGyVrlJXxfDzw==", + "dependencies": { + "@envelop/types": "4.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@envelop/types": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@envelop/types/-/types-4.0.1.tgz", + "integrity": "sha512-ULo27/doEsP7uUhm2iTnElx13qTO6I5FKvmLoX41cpfuw8x6e0NUFknoqhEsLzAbgz8xVS5mjwcxGCXh4lDYzg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -2348,6 +2379,72 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz", "integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==" }, + "node_modules/@graphql-tools/executor": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.2.0.tgz", + "integrity": "sha512-SKlIcMA71Dha5JnEWlw4XxcaJ+YupuXg0QCZgl2TOLFz4SkGCwU/geAsJvUJFwK2RbVLpQv/UMq67lOaBuwDtg==", + "dependencies": { + "@graphql-tools/utils": "^10.0.0", + "@graphql-typed-document-node/core": "3.2.0", + "@repeaterjs/repeater": "^3.0.4", + "tslib": "^2.4.0", + "value-or-promise": "^1.0.12" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/merge": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.0.tgz", + "integrity": "sha512-J7/xqjkGTTwOJmaJQJ2C+VDBDOWJL3lKrHJN4yMaRLAJH3PosB7GiPRaSDZdErs0+F77sH2MKs2haMMkywzx7Q==", + "dependencies": { + "@graphql-tools/utils": "^10.0.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/schema": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.0.tgz", + "integrity": "sha512-kf3qOXMFcMs2f/S8Y3A8fm/2w+GaHAkfr3Gnhh2LOug/JgpY/ywgFVxO3jOeSpSEdoYcDKLcXVjMigNbY4AdQg==", + "dependencies": { + "@graphql-tools/merge": "^9.0.0", + "@graphql-tools/utils": "^10.0.0", + "tslib": "^2.4.0", + "value-or-promise": "^1.0.12" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/utils": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.0.6.tgz", + "integrity": "sha512-hZMjl/BbX10iagovakgf3IiqArx8TPsotq5pwBld37uIX1JiZoSbgbCIFol7u55bh32o6cfDEiiJgfAD5fbeyQ==", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "dset": "^3.1.2", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/@graphql-typed-document-node/core": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", @@ -2356,6 +2453,43 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, + "node_modules/@graphql-yoga/logger": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@graphql-yoga/logger/-/logger-1.0.0.tgz", + "integrity": "sha512-JYoxwnPggH2BfO+dWlWZkDeFhyFZqaTRGLvFhy+Pjp2UxitEW6nDrw+pEDw/K9tJwMjIFMmTT9VfTqrnESmBHg==", + "dependencies": { + "tslib": "^2.5.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@graphql-yoga/subscription": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@graphql-yoga/subscription/-/subscription-4.0.0.tgz", + "integrity": "sha512-0qsN/BPPZNMoC2CZ8i+P6PgiJyHh1H35aKDt37qARBDaIOKDQuvEOq7+4txUKElcmXi7DYFo109FkhSQoEajrg==", + "dependencies": { + "@graphql-yoga/typed-event-target": "^2.0.0", + "@repeaterjs/repeater": "^3.0.4", + "@whatwg-node/events": "^0.1.0", + "tslib": "^2.5.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@graphql-yoga/typed-event-target": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@graphql-yoga/typed-event-target/-/typed-event-target-2.0.0.tgz", + "integrity": "sha512-oA/VGxGmaSDym1glOHrltw43qZsFwLLjBwvh57B79UKX/vo3+UQcRgOyE44c5RP7DCYjkrC2tuArZmb6jCzysw==", + "dependencies": { + "@repeaterjs/repeater": "^3.0.4", + "tslib": "^2.5.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", @@ -2603,6 +2737,20 @@ "react-dom": ">=16.8.0" } }, + "node_modules/@mantine/dates": { + "version": "5.10.5", + "resolved": "https://registry.npmjs.org/@mantine/dates/-/dates-5.10.5.tgz", + "integrity": "sha512-l7lSgDKEV+phTgUAHIkc/22Qgq+WBg0P7JS2WpeZeERX8eyD4BGlsY3ZWqHigYxhBq/Ycd2tHZFmwl2p+694Zw==", + "dependencies": { + "@mantine/utils": "5.10.5" + }, + "peerDependencies": { + "@mantine/core": "5.10.5", + "@mantine/hooks": "5.10.5", + "dayjs": ">=1.0.0", + "react": ">=16.8.0" + } + }, "node_modules/@mantine/form": { "version": "6.0.20", "resolved": "https://registry.npmjs.org/@mantine/form/-/form-6.0.20.tgz", @@ -2657,6 +2805,22 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.9.tgz", "integrity": "sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==" }, + "node_modules/@mantine/tiptap": { + "version": "5.10.5", + "resolved": "https://registry.npmjs.org/@mantine/tiptap/-/tiptap-5.10.5.tgz", + "integrity": "sha512-ia8Oji18LfFO7x3sBBFR3FFMbWTHstTHnuZQvOS4Kkwi9KJLFDY5m+az0EVP548fthTK6yBHM1v/Hq1md1+D9w==", + "dependencies": { + "@mantine/utils": "5.10.5" + }, + "peerDependencies": { + "@mantine/core": "5.10.5", + "@mantine/hooks": "5.10.5", + "@tabler/icons": "^1.119.0", + "@tiptap/extension-link": "^2.0.0-beta.202", + "@tiptap/react": "^2.0.0-beta.202", + "react": ">=16.8.0" + } + }, "node_modules/@mantine/utils": { "version": "5.10.5", "resolved": "https://registry.npmjs.org/@mantine/utils/-/utils-5.10.5.tgz", @@ -2957,6 +3121,40 @@ "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.3.tgz", "integrity": "sha512-an2OZ7/6er9Jja8EDUvU/tmtGIutdlb6LwXOwgjzoCjDRAsUd8sRZMBjoPEy78Xa9iOp+Kglk2CHgVwZuZbWbw==" }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@prisma/client": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.3.1.tgz", + "integrity": "sha512-ArOKjHwdFZIe1cGU56oIfy7wRuTn0FfZjGuU/AjgEBOQh+4rDkB6nF+AGHP8KaVpkBIiHGPQh3IpwQ3xDMdO0Q==", + "hasInstallScript": true, + "dependencies": { + "@prisma/engines-version": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59" + }, + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/engines-version": { + "version": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59.tgz", + "integrity": "sha512-y5qbUi3ql2Xg7XraqcXEdMHh0MocBfnBzDn5GbV1xk23S3Mq8MGs+VjacTNiBh3dtEdUERCrUUG7Z3QaJ+h79w==" + }, "node_modules/@radix-ui/number": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.0.tgz", @@ -3088,6 +3286,59 @@ "react": "^16.8 || ^17.0 || ^18.0" } }, + "node_modules/@remirror/core-constants": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-2.0.2.tgz", + "integrity": "sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==", + "peer": true + }, + "node_modules/@remirror/core-helpers": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@remirror/core-helpers/-/core-helpers-3.0.0.tgz", + "integrity": "sha512-tusEgQJIqg4qKj6HSBUFcyRnWnziw3neh4T9wOmsPGHFC3w9kl5KSrDb9UAgE8uX6y32FnS7vJ955mWOl3n50A==", + "peer": true, + "dependencies": { + "@remirror/core-constants": "^2.0.2", + "@remirror/types": "^1.0.1", + "@types/object.omit": "^3.0.0", + "@types/object.pick": "^1.3.2", + "@types/throttle-debounce": "^2.1.0", + "case-anything": "^2.1.13", + "dash-get": "^1.0.2", + "deepmerge": "^4.3.1", + "fast-deep-equal": "^3.1.3", + "make-error": "^1.3.6", + "object.omit": "^3.0.0", + "object.pick": "^1.3.0", + "throttle-debounce": "^3.0.1" + } + }, + "node_modules/@remirror/types": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@remirror/types/-/types-1.0.1.tgz", + "integrity": "sha512-VlZQxwGnt1jtQ18D6JqdIF+uFZo525WEqrfp9BOc3COPpK4+AWCgdnAWL+ho6imWcoINlGjR/+3b6y5C1vBVEA==", + "peer": true, + "dependencies": { + "type-fest": "^2.19.0" + } + }, + "node_modules/@remirror/types/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "peer": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@repeaterjs/repeater": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.4.tgz", + "integrity": "sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==" + }, "node_modules/@rushstack/eslint-patch": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.3.tgz", @@ -4253,25 +4504,420 @@ "regenerator-runtime": "^0.13.7" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/theming/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/@swc/helpers": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", + "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tabler/icons": { + "version": "1.119.0", + "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-1.119.0.tgz", + "integrity": "sha512-Fk3Qq4w2SXcTjc/n1cuL5bccPkylrOMo7cYpQIf/yw6zP76LQV9dtLcHQUjFiUnaYuswR645CnURIhlafyAh9g==", + "peer": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + }, + "peerDependencies": { + "react": "^16.x || 17.x || 18.x", + "react-dom": "^16.x || 17.x || 18.x" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@tiptap/core": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.1.10.tgz", + "integrity": "sha512-yhUKsac6nlqbPQfwQnp+4Jb110EqmzocXKoZacLwzHpM7JVsr2+LXMDu9kahtrvHNJErJljhnQvDHRsrrYeJkQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.1.10.tgz", + "integrity": "sha512-lpBF/a+qgv4Bdf7HYisTkMFdFdGfn2SqspsydvG8UI7N9B/PfnCCrtoMaC3bqTaT6u8ZVxyM3Y3vnq2AxXJvBw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.1.10.tgz", + "integrity": "sha512-I43WCwc7pyz5vtKGj24Rjv7HN0EK5S4PlADQPBuhC1qQvfCTFvjrBB6ZmsekUMGmllW0qMOFVLSjtffpckqshA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.1.10.tgz", + "integrity": "sha512-XxgJajXkfAj/fChXkIwKBs7/3pd7OxV1uGc6Opx1qW/nSRYx/rr97654Sx/sg6auwIlbpRoqTmyqjbykGX1/yA==", + "dependencies": { + "tippy.js": "^6.3.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.1.10.tgz", + "integrity": "sha512-e6aFr29OSOmXsjFZB2zt3p8aeCWOx0C9Ayrpdf4QBUCOUJtt6FQPxxiYc+XZcdrYbLGLznA7QJlulCK9SGv2Fw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.1.10.tgz", + "integrity": "sha512-1yy/kR0FAeMkDdAt1LW/FH6vlyZLqLZqY6BM+wBCiGrr+XeA5FTXih9iT/4gbTRuIzG0EPqx18nvroG7hUsWBg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.1.10.tgz", + "integrity": "sha512-M+s89V9mP3tOoS6p/X2Dzw/Z7Fcg9EF0ZXlsMNifdlpwJlhAIYxI7vjPBmkMAFXTDB5eMZblXyNQaZ7v6V2Yeg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.1.10.tgz", + "integrity": "sha512-jNlNGQIGg471DvzhADaEoRINa3LNghowrBbKK9d5wGVnbKRykNEPwjCf8zNl+m5NBmCZl3lsdznlwBk5zyh5Bg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.1.10.tgz", + "integrity": "sha512-GhsWsCq6wLb8HJ32BeAm7ndv4lPyu1F7FFwmnARzEF5q54FV20kWSv2zC+Dv0dTvynXR3quXybdUM92xeNDovw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-floating-menu": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.1.10.tgz", + "integrity": "sha512-uChrDrY3usnF9wSegqq+YGaqd229p9gmaB5xyOyMERDs972hKj4Ul95rXzBBiMKAWUMw9eM09i7+ijTzz4KDUw==", + "dependencies": { + "tippy.js": "^6.3.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.1.10.tgz", + "integrity": "sha512-WSBT9X7dzg0HyMoMP/Yyxl28QwIJO90YzobI9z5mav86BQv7C5wU0fQSpbpAbsN3s7lxKhPwNrXkwkpnXT4ZCA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.1.10.tgz", + "integrity": "sha512-sYrzpPoV5jQri+duGb50nDTs+hOBQDxXTKlJuZNFfZMwgx6epwxb8xICcGAUJFShuuW8UAWCNcB4jG9tMqgvyw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.1.10.tgz", + "integrity": "sha512-1OgmrRPMcY52WI7I4799xd4eIsEX/bI813B8mZvNYXLzZI75pLW1hmz1mUvBYyMwlcek74zVTGYgPy11o+2JEg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-history": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.1.10.tgz", + "integrity": "sha512-tApuN8MIJMzc0dxvkYJPt3t5cea9NuZBGNiuVedJwMMUF6hbFpMZAt20GW2qwjBaZ76rQwbLp1s3KnImFsPe5A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.1.10.tgz", + "integrity": "sha512-91lGpK2d6WMPhrMDPBURS8z8pEg1CUBYy7GmBenKvvgh+JzVhG+U6MtykfWNfm2R4iRXOl1xLbyUOCiOSUXodQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.1.10.tgz", + "integrity": "sha512-ebw5m+rWx6K5UoBVXSkz3fpvDJh/wScfYmwl6pkbjc2jNbZiln2LSiLHYc2eIYJ2aTsVxcw/n0Azfk5Lb19InA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.1.10.tgz", + "integrity": "sha512-dXxPTWzJzpbDRAewM4P8jN/n9h8uUH83lOLwweuODYCqHRdjQL/uGkQworFFrgqmRHs+9JjHZ4DETILZVawJ+Q==", + "peer": true, + "dependencies": { + "linkifyjs": "^4.1.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.1.10.tgz", + "integrity": "sha512-rRRyB14vOcSjTMAh8Y+50TRC/jO469CelGwFjOLrK1ZSEag5wmLDaqpWOOb52BFYnvCHuIm1HqZtdL5bTI/J1w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.1.10.tgz", + "integrity": "sha512-jouo3RHUMxU4dPzZcfZdUzmsLVp1KHrLIAD2YAxBuqArACrBNfJpIhtkTKuGLlaFhKqGr+EmNdNQnK8JOBhLtQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.1.10.tgz", + "integrity": "sha512-kzuHbrxcxpWkha5P+JFzCKT54pNqb4IBKMU5qT9YGhZSdNTtU63ncdCHM+Ad1ukLuvXAv95zh1IQC5j+Z1Qk4A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.1.10.tgz", + "integrity": "sha512-KW63lZLPFIir5AIeh2I7UK6Tx1O3jetD7JIPUzEqp1I1BfJlHGHVQxV8VXAmJl0hTOzjQBsHW42PmBxSC97NUg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.1.10.tgz", + "integrity": "sha512-ubU/WQwNB0MVKyMAHr8ka3Nu3jCR03HARGKUwNRzppZYtRXWyXHNlAaJdplNb1NMGb8hd0ElBJmwFlVqmh8haQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-underline": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.1.10.tgz", + "integrity": "sha512-f+rJKviGNqORGv4/1pTLZuVTb9VsKMZMLucL8423M6s8TdrH//sBB8QeU92JSnO9PjAGwxWjS1f23/KtufxP8g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@tiptap/core": "^2.0.0" } }, - "node_modules/@storybook/theming/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "node_modules/@tiptap/pm": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.1.10.tgz", + "integrity": "sha512-Y+AqizKnjQpx4pSaA6m/cCD5QHQRPtALhO4ZO4YFZV1idYmsJA3/S5lgJI3ZL5eAHKHcGk6Vv3/8Y+eej5YIPw==", + "peer": true, + "dependencies": { + "prosemirror-changeset": "^2.2.0", + "prosemirror-collab": "^1.3.0", + "prosemirror-commands": "^1.3.1", + "prosemirror-dropcursor": "^1.5.0", + "prosemirror-gapcursor": "^1.3.1", + "prosemirror-history": "^1.3.0", + "prosemirror-inputrules": "^1.2.0", + "prosemirror-keymap": "^1.2.0", + "prosemirror-markdown": "^1.10.1", + "prosemirror-menu": "^1.2.1", + "prosemirror-model": "^1.18.1", + "prosemirror-schema-basic": "^1.2.0", + "prosemirror-schema-list": "^1.2.2", + "prosemirror-state": "^1.4.1", + "prosemirror-tables": "^1.3.0", + "prosemirror-trailing-node": "^2.0.2", + "prosemirror-transform": "^1.7.0", + "prosemirror-view": "^1.28.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } }, - "node_modules/@swc/helpers": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", - "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==", + "node_modules/@tiptap/react": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.1.10.tgz", + "integrity": "sha512-kzCWzbV2dnD5NmHjN8GiS+k0GOmoEhKnMuMzuuU6FjtOALhJzPTrIXITzWDpU3jL+r/4eeXYhAt64Wp7PVwscg==", "dependencies": { - "tslib": "^2.4.0" + "@tiptap/extension-bubble-menu": "^2.1.10", + "@tiptap/extension-floating-menu": "^2.1.10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + } + }, + "node_modules/@tiptap/starter-kit": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.1.10.tgz", + "integrity": "sha512-h5mH1qv7SDFXWZPbOWC8zpGZ62EnDizRNtM45Gani0HYWJXcbPFpgN1qJmESP/jP+v+0hxtnVEkgfpiy3LRm6A==", + "dependencies": { + "@tiptap/core": "^2.1.10", + "@tiptap/extension-blockquote": "^2.1.10", + "@tiptap/extension-bold": "^2.1.10", + "@tiptap/extension-bullet-list": "^2.1.10", + "@tiptap/extension-code": "^2.1.10", + "@tiptap/extension-code-block": "^2.1.10", + "@tiptap/extension-document": "^2.1.10", + "@tiptap/extension-dropcursor": "^2.1.10", + "@tiptap/extension-gapcursor": "^2.1.10", + "@tiptap/extension-hard-break": "^2.1.10", + "@tiptap/extension-heading": "^2.1.10", + "@tiptap/extension-history": "^2.1.10", + "@tiptap/extension-horizontal-rule": "^2.1.10", + "@tiptap/extension-italic": "^2.1.10", + "@tiptap/extension-list-item": "^2.1.10", + "@tiptap/extension-ordered-list": "^2.1.10", + "@tiptap/extension-paragraph": "^2.1.10", + "@tiptap/extension-strike": "^2.1.10", + "@tiptap/extension-text": "^2.1.10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" } }, "node_modules/@types/eslint": { @@ -4384,6 +5030,18 @@ "resolved": "https://registry.npmjs.org/@types/npmlog/-/npmlog-4.1.4.tgz", "integrity": "sha512-WKG4gTr8przEZBiJ5r3s8ZIAoMXNbOgQ+j/d5O4X3x6kZJRLNvyUJuUK/KoG3+8BaOHPhp2m7WC6JKKeovDSzQ==" }, + "node_modules/@types/object.omit": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/object.omit/-/object.omit-3.0.1.tgz", + "integrity": "sha512-24XD34UeRWw505TsMNBrQ4bES2s8IxiFC59mmNUFhTz9IX2hAtA7gQ8wVww1i17QmhBYILg5iqYP2y7aqA3pwQ==", + "peer": true + }, + "node_modules/@types/object.pick": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/object.pick/-/object.pick-1.3.2.tgz", + "integrity": "sha512-sn7L+qQ6RLPdXRoiaE7bZ/Ek+o4uICma/lBFPyJEKDTPTBP1W8u0c4baj3EiS4DiqLs+Hk+KUGvMVJtAw3ePJg==", + "peer": true + }, "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -4419,6 +5077,12 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, + "node_modules/@types/throttle-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/throttle-debounce/-/throttle-debounce-2.1.0.tgz", + "integrity": "sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==", + "peer": true + }, "node_modules/@types/unist": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.7.tgz", @@ -4784,6 +5448,53 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@whatwg-node/events": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.1.tgz", + "integrity": "sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w==", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@whatwg-node/fetch": { + "version": "0.9.13", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.13.tgz", + "integrity": "sha512-PPtMwhjtS96XROnSpowCQM85gCUG2m7AXZFw0PZlGbhzx2GK7f2iOXilfgIJ0uSlCuuGbOIzfouISkA7C4FJOw==", + "dependencies": { + "@whatwg-node/node-fetch": "^0.4.17", + "urlpattern-polyfill": "^9.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@whatwg-node/node-fetch": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.4.18.tgz", + "integrity": "sha512-zdey6buMKCqDVDq+tMqcjopO75Fb6iLqWo+g6cWwN5kiwctEHtVcbws2lJUFhCbo+TLZeH6bMDRUXEo5bkPtcQ==", + "dependencies": { + "@whatwg-node/events": "^0.1.0", + "busboy": "^1.6.0", + "fast-querystring": "^1.1.1", + "fast-url-parser": "^1.1.3", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@whatwg-node/server": { + "version": "0.9.14", + "resolved": "https://registry.npmjs.org/@whatwg-node/server/-/server-0.9.14.tgz", + "integrity": "sha512-I8TT0NoCP+xThLBuGlU6dgq5wpExkphNMo2geZwQW0vAmEPtc3MNMZMIYqg5GyNmpv5Nf7fnxb8tVOIHbDvuDA==", + "dependencies": { + "@whatwg-node/fetch": "^0.9.10", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@wry/context": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.3.tgz", @@ -6017,6 +6728,18 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/case-anything": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", + "integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==", + "peer": true, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/ccount": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", @@ -6575,6 +7298,12 @@ "sha.js": "^2.4.8" } }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "peer": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -6624,6 +7353,18 @@ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" }, + "node_modules/dash-get": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/dash-get/-/dash-get-1.0.2.tgz", + "integrity": "sha512-4FbVrHDwfOASx7uQVxeiCTo7ggSdYZbqs8lH+WU6ViypPlDbe9y6IP5VVUDQBv9DcnyaiPT5XT0UWHgJ64zLeQ==", + "peer": true + }, + "node_modules/dayjs": { + "version": "1.11.9", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz", + "integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==", + "peer": true + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -6912,6 +7653,14 @@ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" }, + "node_modules/dset": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz", + "integrity": "sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==", + "engines": { + "node": ">=4" + } + }, "node_modules/duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -7989,6 +8738,11 @@ "node": ">=0.10.0" } }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -8035,6 +8789,27 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fast-url-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", + "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", + "dependencies": { + "punycode": "^1.3.2" + } + }, + "node_modules/fast-url-parser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -8851,6 +9626,20 @@ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, + "node_modules/graphql-scalars": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/graphql-scalars/-/graphql-scalars-1.22.2.tgz", + "integrity": "sha512-my9FB4GtghqXqi/lWSVAOPiTzTnnEzdOXCsAC2bb5V7EFNQjVjwy3cSSbUvgYOtDuDibd+ZsCDhz+4eykYOlhQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, "node_modules/graphql-tag": { "version": "2.12.6", "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", @@ -8865,6 +9654,38 @@ "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, + "node_modules/graphql-yoga": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/graphql-yoga/-/graphql-yoga-4.0.4.tgz", + "integrity": "sha512-MvCLhFecYNIKuxAZisPjpIL9lxRYbpgPSNKENDO/8CV3oiFlsLJHZb5dp2sVAeLafXHeZ9TgkijLthUBc1+Jag==", + "dependencies": { + "@envelop/core": "^4.0.0", + "@graphql-tools/executor": "^1.0.0", + "@graphql-tools/schema": "^10.0.0", + "@graphql-tools/utils": "^10.0.0", + "@graphql-yoga/logger": "^1.0.0", + "@graphql-yoga/subscription": "^4.0.0", + "@whatwg-node/fetch": "^0.9.7", + "@whatwg-node/server": "^0.9.1", + "dset": "^3.1.1", + "lru-cache": "^10.0.0", + "tslib": "^2.5.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^15.2.0 || ^16.0.0" + } + }, + "node_modules/graphql-yoga/node_modules/lru-cache": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", + "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/handlebars": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", @@ -10275,6 +11096,21 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/linkify-it": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", + "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", + "peer": true, + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/linkifyjs": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.1.tgz", + "integrity": "sha512-zFN/CTVmbcVef+WaDXT63dNzzkfRBKT1j464NJQkV7iSgJU0sLBus9W0HBwnXK13/hf168pbrx/V/bjEHOXNHA==", + "peer": true + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -10384,6 +11220,12 @@ "semver": "bin/semver" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "peer": true + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -10425,6 +11267,34 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/markdown-it": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", + "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", + "peer": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -11294,6 +12164,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.omit": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-3.0.0.tgz", + "integrity": "sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==", + "peer": true, + "dependencies": { + "is-extendable": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -11403,6 +12285,12 @@ "node": ">= 0.8.0" } }, + "node_modules/orderedmap": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", + "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==", + "peer": true + }, "node_modules/os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", @@ -11879,6 +12767,201 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/prosemirror-changeset": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.2.1.tgz", + "integrity": "sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==", + "peer": true, + "dependencies": { + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-collab": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz", + "integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==", + "peer": true, + "dependencies": { + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.5.2.tgz", + "integrity": "sha512-hgLcPaakxH8tu6YvVAaILV2tXYsW3rAdDR8WNkeKGcgeMVQg3/TMhPdVoh7iAmfgVjZGtcOSjKiQaoeKjzd2mQ==", + "peer": true, + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.1.tgz", + "integrity": "sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==", + "peer": true, + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz", + "integrity": "sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==", + "peer": true, + "dependencies": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "node_modules/prosemirror-history": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.3.2.tgz", + "integrity": "sha512-/zm0XoU/N/+u7i5zepjmZAEnpvjDtzoPWW6VmKptcAnPadN/SStsBjMImdCEbb3seiNTpveziPTIrXQbHLtU1g==", + "peer": true, + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-inputrules": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.2.1.tgz", + "integrity": "sha512-3LrWJX1+ULRh5SZvbIQlwZafOXqp1XuV21MGBu/i5xsztd+9VD15x6OtN6mdqSFI7/8Y77gYUbQ6vwwJ4mr6QQ==", + "peer": true, + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.2.tgz", + "integrity": "sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==", + "peer": true, + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-markdown": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.11.2.tgz", + "integrity": "sha512-Eu5g4WPiCdqDTGhdSsG9N6ZjACQRYrsAkrF9KYfdMaCmjIApH75aVncsWYOJvEk2i1B3i8jZppv3J/tnuHGiUQ==", + "peer": true, + "dependencies": { + "markdown-it": "^13.0.1", + "prosemirror-model": "^1.0.0" + } + }, + "node_modules/prosemirror-menu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.4.tgz", + "integrity": "sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==", + "peer": true, + "dependencies": { + "crelt": "^1.0.0", + "prosemirror-commands": "^1.0.0", + "prosemirror-history": "^1.0.0", + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-model": { + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.19.3.tgz", + "integrity": "sha512-tgSnwN7BS7/UM0sSARcW+IQryx2vODKX4MI7xpqY2X+iaepJdKBPc7I4aACIsDV/LTaTjt12Z56MhDr9LsyuZQ==", + "peer": true, + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-basic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.2.tgz", + "integrity": "sha512-/dT4JFEGyO7QnNTe9UaKUhjDXbTNkiWTq/N4VpKaF79bBjSExVV2NXmJpcM7z/gD7mbqNjxbmWW5nf1iNSSGnw==", + "peer": true, + "dependencies": { + "prosemirror-model": "^1.19.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.3.0.tgz", + "integrity": "sha512-Hz/7gM4skaaYfRPNgr421CU4GSwotmEwBVvJh5ltGiffUJwm7C8GfN/Bc6DR1EKEp5pDKhODmdXXyi9uIsZl5A==", + "peer": true, + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.7.3" + } + }, + "node_modules/prosemirror-state": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz", + "integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==", + "peer": true, + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.27.0" + } + }, + "node_modules/prosemirror-tables": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.3.4.tgz", + "integrity": "sha512-z6uLSQ1BLC3rgbGwZmpfb+xkdvD7W/UOsURDfognZFYaTtc0gsk7u/t71Yijp2eLflVpffMk6X0u0+u+MMDvIw==", + "peer": true, + "dependencies": { + "prosemirror-keymap": "^1.1.2", + "prosemirror-model": "^1.8.1", + "prosemirror-state": "^1.3.1", + "prosemirror-transform": "^1.2.1", + "prosemirror-view": "^1.13.3" + } + }, + "node_modules/prosemirror-trailing-node": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.7.tgz", + "integrity": "sha512-8zcZORYj/8WEwsGo6yVCRXFMOfBo0Ub3hCUvmoWIZYfMP26WqENU0mpEP27w7mt8buZWuGrydBewr0tOArPb1Q==", + "peer": true, + "dependencies": { + "@remirror/core-constants": "^2.0.2", + "@remirror/core-helpers": "^3.0.0", + "escape-string-regexp": "^4.0.0" + }, + "peerDependencies": { + "prosemirror-model": "^1.19.0", + "prosemirror-state": "^1.4.2", + "prosemirror-view": "^1.31.2" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.7.5.tgz", + "integrity": "sha512-U/fWB6frEzY7dzwJUo+ir8dU1JEanaI/RwL12Imy9js/527N0v/IRUKewocP1kTq998JNT18IGtThaDLwLOBxQ==", + "peer": true, + "dependencies": { + "prosemirror-model": "^1.0.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.31.8", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.31.8.tgz", + "integrity": "sha512-VQrEIdiPJ4YV65Ifj2kWISwaiqocMHy7cpUKVQYt19C/87FepoqnwVW3kMKRpeY/nQzED8L+vyOaYDBn0WqT7w==", + "peer": true, + "dependencies": { + "prosemirror-model": "^1.16.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, "node_modules/protocol-buffers-schema": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", @@ -12611,6 +13694,12 @@ "inherits": "^2.0.1" } }, + "node_modules/rope-sequence": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", + "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==", + "peer": true + }, "node_modules/rsvp": { "version": "4.8.5", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", @@ -14082,6 +15171,15 @@ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, + "node_modules/throttle-debounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", + "integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==", + "peer": true, + "engines": { + "node": ">=10" + } + }, "node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -14139,6 +15237,14 @@ "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" }, + "node_modules/tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "dependencies": { + "@popperjs/core": "^2.9.0" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -14424,6 +15530,12 @@ "node": ">=14.17" } }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "peer": true + }, "node_modules/uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", @@ -14790,6 +15902,11 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" }, + "node_modules/urlpattern-polyfill": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-9.0.0.tgz", + "integrity": "sha512-WHN8KDQblxd32odxeIgo83rdVDE2bvdkb86it7bMhYZwWKJz0+O0RK/eZiHYnM+zgt/U7hAHOlCQGfjjvSkw2g==" + }, "node_modules/use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -14869,6 +15986,14 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/value-or-promise": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", + "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==", + "engines": { + "node": ">=12" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -14945,6 +16070,12 @@ "react-dom": "^18.0.0" } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "peer": true + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", diff --git a/package.json b/package.json index 26b2a29..33c4567 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,20 @@ }, "dependencies": { "@apollo/client": "^3.8.1", + "@mantine/dates": "^5.10.5", "@mantine/form": "^6.0.20", "@mantine/notifications": "^5.10.5", + "@mantine/tiptap": "^5.10.5", + "@prisma/client": "^5.3.1", + "@tiptap/extension-underline": "^2.1.10", + "@tiptap/react": "^2.1.10", + "@tiptap/starter-kit": "^2.1.10", "adblock-hunter": "^1.0.1", "axios": "^1.5.0", "eslint": "8.48.0", "eslint-config-next": "13.4.19", + "graphql-scalars": "^1.22.2", + "graphql-yoga": "^4.0.4", "html-react-parser": "^4.2.1", "jsonwebtoken": "^9.0.1", "next": "13.4.19", diff --git a/pages/api/graphql.js b/pages/api/graphql.js new file mode 100644 index 0000000..547f486 --- /dev/null +++ b/pages/api/graphql.js @@ -0,0 +1,33 @@ +import { createSchema, createYoga } from "graphql-yoga"; +import { typeDefs } from "../../graphql/schema"; +import { resolvers } from "../../graphql/resolvers"; +import { createContext } from "../../graphql/context"; + +import { getServerSession } from "next-auth/next"; +import { authOptions } from "./auth/[...nextauth]"; + +export default createYoga({ + schema: createSchema({ + typeDefs, + resolvers, + context: createContext, + }), + context: async (context) => { + const session = await getServerSession( + context.req, + context.res, + authOptions + ); + + return { + session, + }; + }, + graphqlEndpoint: "/api/graphql", +}); + +export const config = { + api: { + bodyParser: false, + }, +}; diff --git a/prisma.sh b/prisma.sh index 545b9da..f2964ea 100755 --- a/prisma.sh +++ b/prisma.sh @@ -3,4 +3,4 @@ echo "DATABASE_URL=mysql://$SIP_MYSQL_ALT_USER:$SIP_MYSQL_ALT_PW@$SIP_MYSQL_ALT_SERVER:$SIP_MYSQL_ALT_PORT/$SIP_MYSQL_ALT_NAME?schema=public" > .env npx prisma generate -npx prisma migrate deploy +npx prisma migrate deploy \ No newline at end of file diff --git a/prisma/migrations/20230915103134_initial_migration/migration.sql b/prisma/migrations/20230915103134_initial_migration/migration.sql new file mode 100644 index 0000000..2668ce1 --- /dev/null +++ b/prisma/migrations/20230915103134_initial_migration/migration.sql @@ -0,0 +1,15 @@ +-- CreateTable +CREATE TABLE `Event` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `title` VARCHAR(191) NOT NULL, + `speaker` VARCHAR(191) NOT NULL, + `description` VARCHAR(191) NOT NULL, + `date` DATETIME(3) NOT NULL, + `startTime` DATETIME(3) NOT NULL, + `endTime` DATETIME(3) NOT NULL, + `place` VARCHAR(191) NOT NULL, + `signUp` VARCHAR(191) NOT NULL, + `isStammtisch` BOOLEAN NOT NULL DEFAULT false, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; diff --git a/prisma/migrations/20230915104240_store_dates_as_string/migration.sql b/prisma/migrations/20230915104240_store_dates_as_string/migration.sql new file mode 100644 index 0000000..22ca17c --- /dev/null +++ b/prisma/migrations/20230915104240_store_dates_as_string/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable +ALTER TABLE `Event` MODIFY `date` VARCHAR(191) NOT NULL, + MODIFY `startTime` VARCHAR(191) NOT NULL, + MODIFY `endTime` VARCHAR(191) NOT NULL; diff --git a/prisma/migrations/20230915121556_description_is_now_text/migration.sql b/prisma/migrations/20230915121556_description_is_now_text/migration.sql new file mode 100644 index 0000000..c4bf214 --- /dev/null +++ b/prisma/migrations/20230915121556_description_is_now_text/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE `Event` MODIFY `description` TEXT NOT NULL; diff --git a/prisma/migrations/20230915132050_add_sign_up/migration.sql b/prisma/migrations/20230915132050_add_sign_up/migration.sql new file mode 100644 index 0000000..1bf9d8e --- /dev/null +++ b/prisma/migrations/20230915132050_add_sign_up/migration.sql @@ -0,0 +1,11 @@ +-- CreateTable +CREATE TABLE `SignUp` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `sub` VARCHAR(191) NOT NULL, + `eventId` INTEGER NOT NULL, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- AddForeignKey +ALTER TABLE `SignUp` ADD CONSTRAINT `SignUp_eventId_fkey` FOREIGN KEY (`eventId`) REFERENCES `Event`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20230915135047_add_name_and_email_to_sign_up/migration.sql b/prisma/migrations/20230915135047_add_name_and_email_to_sign_up/migration.sql new file mode 100644 index 0000000..143b682 --- /dev/null +++ b/prisma/migrations/20230915135047_add_name_and_email_to_sign_up/migration.sql @@ -0,0 +1,12 @@ +/* + Warnings: + + - Added the required column `email` to the `SignUp` table without a default value. This is not possible if the table is not empty. + - Added the required column `firstName` to the `SignUp` table without a default value. This is not possible if the table is not empty. + - Added the required column `lastName` to the `SignUp` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE `SignUp` ADD COLUMN `email` VARCHAR(191) NOT NULL, + ADD COLUMN `firstName` VARCHAR(191) NOT NULL, + ADD COLUMN `lastName` VARCHAR(191) NOT NULL; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..e5a788a --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "mysql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..48d318d --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,35 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mysql" + url = env("DATABASE_URL") +} + +model Event { + id Int @id @default(autoincrement()) + title String + speaker String + description String @db.Text + date String + startTime String + endTime String + place String + signUp String + isStammtisch Boolean @default(false) + signUps SignUp[] +} + +model SignUp { + id Int @id @default(autoincrement()) + sub String + firstName String + lastName String + email String + event Event @relation(fields: [eventId], references: [id], onDelete: Cascade) + eventId Int +} diff --git a/public/locales/de/common.json b/public/locales/de/common.json index 9cfbe26..48d6371 100644 --- a/public/locales/de/common.json +++ b/public/locales/de/common.json @@ -23,5 +23,22 @@ "editEvent": "Event Bearbeiten", "ENotEmpty": "Dieses Feld bitte ausfüllen", "noAdblockTitle": "Kein Adblocker Installiert!", - "noAdblockText": "Du hast deinen Adblocker ausgeschaltet oder keinen installiert! Installiere jetzt einen für <a target='_blank' href='https://addons.mozilla.org/de/firefox/addon/ublock-origin/'>Firefox</a> oder <a target='_blank' href='https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm?hl=de'>Chrome</a>. Weil <a href='#philosophy'>Datenschutz</a> ein Menschenrecht ist." + "noAdblockText": "Du hast deinen Adblocker ausgeschaltet oder keinen installiert! Installiere jetzt einen für <a target='_blank' href='https://addons.mozilla.org/de/firefox/addon/ublock-origin/'>Firefox</a> oder <a target='_blank' href='https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm?hl=de'>Chrome</a>. Weil <a href='#philosophy'>Datenschutz</a> ein Menschenrecht ist.", + "stammtisch": "Stammtisch", + "date": "Datum", + "time": "Zeit", + "title": "Titel", + "speaker": "Sprecher*in", + "description": "Beschreibung", + "place": "Ort", + "signUpLink": "Anmdeldelink (falls extern)", + "signUp": "Anmelden", + "externalSignUp": "Anmelden (extern)", + "signedUpTitle": "Angemeldet", + "signedUpText": "Du bist für dieses Event angemeldet.", + "signOff": "Abmelden", + "viewParticipants": "Teilnehmendenliste", + "participants": "Teilnehmendenliste", + "copyMailAddresses": "Mailadressen Kopieren", + "loadTemplate": "Template Laden" } \ No newline at end of file diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 7b4427f..653fc69 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -23,5 +23,22 @@ "editEvent": "Edit Event", "ENotEmpty": "Please fill out this field", "noAdblockTitle": "No Adblocker Installed", - "noAdblockText": "You have turned off or did not install an Adblocker! Install it now for <a target='_blank' href='https://addons.mozilla.org/de/firefox/addon/ublock-origin/'>Firefox</a> or <a target='_blank' href='https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm?hl=de'>Chrome</a>. Because <a href='#philosophy'>privacy</a> is a human right." + "noAdblockText": "You have turned off or did not install an Adblocker! Install it now for <a target='_blank' href='https://addons.mozilla.org/de/firefox/addon/ublock-origin/'>Firefox</a> or <a target='_blank' href='https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm?hl=de'>Chrome</a>. Because <a href='#philosophy'>privacy</a> is a human right.", + "stammtisch": "Stammtisch", + "date": "Date", + "time": "Time", + "title": "Title", + "speaker": "Speaker", + "description": "Description", + "place": "Place", + "signUpLink": "Sign Up Link (if external)", + "signUp": "Sign Up", + "externalSignUp": "Sign Up (external)", + "signedUpTitle": "Signed Up", + "signedUpText": "You are signed up to this event", + "signOff": "Sign Off", + "viewParticipants": "View Participants", + "participants": "Participants", + "copyMailAddresses": "Copy Mail Addresses", + "loadTemplate": "Load Template" } \ No newline at end of file diff --git a/styles/globals.css b/styles/globals.css index 35dcb78..913d8e3 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -50,6 +50,10 @@ body { margin: 0; } +p { + margin-bottom: .5em; +} + .mantine-Select-label { font-family: "Source Sans Pro"; } diff --git a/utilities/dates.js b/utilities/dates.js new file mode 100644 index 0000000..36d8bc7 --- /dev/null +++ b/utilities/dates.js @@ -0,0 +1,24 @@ +import dayjs from "dayjs"; + +const customParseFormat = require("dayjs/plugin/customParseFormat"); +dayjs.extend(customParseFormat); + +require("dayjs/locale/en"); +require("dayjs/locale/de"); + +export function formatDateFromDB(date, locale) { + dayjs.locale(locale); + const d = dayjs(date); + return locale == "de" ? d.format("dd, DD.MM.YY") : d.format("ddd, DD.MM.YY"); +} + +export function formatTimeFromDB(startTime, endTime) { + const s = dayjs(startTime); + const e = dayjs(endTime); + return s.format("HH:mm") + " — " + e.format("HH:mm"); +} + +export function isInFuture(event) { + // TODO: implement logic + return true; +} diff --git a/utilities/hasAccess.js b/utilities/hasAccess.js index ed8851d..3437edc 100644 --- a/utilities/hasAccess.js +++ b/utilities/hasAccess.js @@ -1,8 +1,9 @@ export default function hasAccess(session, admin) { - const stag_role = "pub_staging_papperlaweb_papperlaweb"; - const prod_role = "pub_prod_papperlaweb_papperlaweb"; + const stag_role = "thealt_staging_website_website"; + const prod_role = "thealt_prod_website_website"; // this is just for development purposes + return true; return session ? true : false; if (process.env.NODE_ENV && process.env.NODE_ENV === "development") diff --git a/utilities/signUp.js b/utilities/signUp.js new file mode 100644 index 0000000..9877594 --- /dev/null +++ b/utilities/signUp.js @@ -0,0 +1,8 @@ +export function isRegistered(session, event) { + if (!session) return false; + const matches = event.signUps.filter( + (signUp) => signUp.sub === session.info.payload.sub + ); + if (matches.length > 0) return true; + return false; +} -- GitLab