diff --git a/cinit.yml b/cinit.yml
index 93707d1ce6fe55d141f490452d1e8abc7648031a..820b0475f57e899c4bcbdf671ea84520e2af8b9d 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 2a13339eedf08f711f8c6d7490ea3b73c950ccc5..5b32fc86c92e41495dc81834c9a79fd4b0eb4e40 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 0000000000000000000000000000000000000000..7bceb145eb3a2747abf346da506a92e8bc3893e3
--- /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 0000000000000000000000000000000000000000..8aa3daa92b72dc839b0a394f6b7949ccbd41c5cb
--- /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 46b94a2219f513a8e29c4b879816734246b00877..e1bc58e50a40c34209aef1a1cbaed60bccdcc4e4 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 b1619bc26daa139de853ac9c80cf67c6cf48f6ff..7e795528bda5de4d4d14722d7318931d17ca8469 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 53b1f0d6042ef71dec6c97ccf2a6abebf7a7495a..2db25ea1f5dd6f86ab7d1dc5967c8cc65f8899cd 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 5cb5680c062cee442d25127627c4320558e9f4b3..aed21aa98a2051010b758e28a7c8029e280e65ad 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 f932592c58511b740b16b9aea924556f76ba275a..7616a45c8a4c3c5423a2fc33777473a971c199ff 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 af55e925c938e5fcce83d7fd0d12a7f415065365..07451a6001b289639b5cf3fb7d203f362bddca90 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 0000000000000000000000000000000000000000..648aaecaac704d23cf9bba443c62933ec33174ec
--- /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 0000000000000000000000000000000000000000..6aec7168d9559f06ec6207ea0d3a10153caf7769
--- /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 0000000000000000000000000000000000000000..b0326799274dd58debf90a382a646f7cbb9e3738
--- /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 0000000000000000000000000000000000000000..ed3f0b85ec434cb7794818d2a749f9cb29dff4cd
--- /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 0000000000000000000000000000000000000000..74a0f41866bad7f48b8f750cda26492fa03d7a1a
--- /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 4d6c58ed6b9e21fc945a8476260462c245d992c5..05a6fec1f8d0ee511fd256d98f6f60064b48f6a4 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 26b2a2956fe86a10f17bffbaf790aaa61e1aff92..33c4567f45fe2d0a14ddaaba5456ac824166a33d 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 0000000000000000000000000000000000000000..547f4860a155ca71d9e6be11aab4b7de01d241ba
--- /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 545b9da75c8fd4d2f7a8013deb602bee0e79f91f..f2964eaae2f5e06cf762ed3e8602cd4d03550e75 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 0000000000000000000000000000000000000000..2668ce14f452975c8a0a36bdee53d885e14a777a
--- /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 0000000000000000000000000000000000000000..22ca17c055a1e774ebab6c8d4981ca2f2dfb7218
--- /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 0000000000000000000000000000000000000000..c4bf21425789fa9653f62a07fae68e87e2ee9fea
--- /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 0000000000000000000000000000000000000000..1bf9d8ebbafa19a4586c0a3a610a115af757dd30
--- /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 0000000000000000000000000000000000000000..143b68226b466af0743b59c5ba5b36b22f3880d5
--- /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 0000000000000000000000000000000000000000..e5a788a7af8fecc0478ef418b8e95c2cf2bbffdf
--- /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 0000000000000000000000000000000000000000..48d318d695a6a4c00c757c110754f9aeb76cedf4
--- /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 9cfbe266097ebacd76612f99c5eff04db56d3202..48d63715de96de6339f164eb5a1b3f0733bfdaba 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 7b4427f0dcf5acd9f96d26aef7f1d69ed586c13e..653fc69799f74accb3875e4ab6ca68f4245a7a18 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 35dcb789f2118b157aaf9130923b334428d34840..913d8e331cfa11c90ce13b4dc1df4763255c9188 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 0000000000000000000000000000000000000000..36d8bc7f80f2714445471528364221405e01d82d
--- /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 ed8851d73a90af1a366962244c8d9e5c411aa6b9..3437edcfde839bfebf7991ec0107bbcdedd43026 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 0000000000000000000000000000000000000000..987759476d786eec97df45b1844c18635c47ba1f
--- /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;
+}