diff --git a/apps/api/prisma/seed.ts b/apps/api/prisma/seed.ts
index 0e90d876e1019953bc0746abd0f21e52ed659898..6653a4f437933a712b422aecba85cc43e493ec21 100644
--- a/apps/api/prisma/seed.ts
+++ b/apps/api/prisma/seed.ts
@@ -1,7 +1,7 @@
 import { PrismaClient, Role } from '@prisma/client'
+import { pbkdf2, randomBytes } from 'node:crypto'
+import { promisify } from 'node:util'
 import { SeatCreateManyInput } from './generated/type-graphql'
-import { pbkdf2, randomBytes } from 'crypto'
-import { promisify } from 'util'
 
 const prisma = new PrismaClient()
 const currentDate = new Date()
@@ -258,11 +258,19 @@ async function main() {
     ],
   })
 
-  await prisma.screening.createManyAndReturn({
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
+  const screenings = await prisma.screening.createManyAndReturn({
     data: generateScreenings(
       [1, 2, 3, 4, 5, 6, 7, 8],
       [1, 2, 3, 4, 5, 6, 7, 8, 9]
     ),
+    include: {
+      hall: {
+        include: {
+          seats: true,
+        },
+      },
+    },
   })
 
   const salt = randomBytes(64).toString('base64')
@@ -274,7 +282,8 @@ async function main() {
     'sha512'
   )
 
-  await prisma.user.createManyAndReturn({
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
+  const users = await prisma.user.createManyAndReturn({
     data: [
       {
         role: Role.CASHIER,
diff --git a/apps/documentation/docs/application-deep-dive/backend.md b/apps/documentation/docs/application-deep-dive/backend.md
index e78ccf745872a612e7152f1f457a94f5cb62cdbf..b97763d759aef7ea6fdaa7384324cb7f167f51ac 100644
--- a/apps/documentation/docs/application-deep-dive/backend.md
+++ b/apps/documentation/docs/application-deep-dive/backend.md
@@ -91,6 +91,38 @@ For this purpose we create two different roles: `api` and `api_elevated`. The `a
 
 The file `/apps/api/prisma/rls.ts` allows us to create two instances of a Prisma Client, one with the role `api` and one with the role `api_elevated`.
 
+For any useful policies you will need access the information about the user that tries to access a certain resource on our database. For this we set custom settings for the database connection with the information about the user:
+
+```ts
+function setConfig(
+  privilegedPrisma: BasePrismaClient,
+  pgRole: string,
+  user: User
+) {
+  return privilegedPrisma.$executeRaw`select \
+    set_config('role', ${pgRole}, true), \
+    set_config('jwt.claims.sub', ${user?.sub ?? ''}, true)`
+}
+```
+
+:::info
+If you require more information about the user than just the subject (userId) like for example the user email you will have to extend this function by adding an additional config:
+
+```ts
+function setConfig(
+  privilegedPrisma: BasePrismaClient,
+  pgRole: string,
+  user: User
+) {
+  return privilegedPrisma.$executeRaw`select \
+    set_config('role', ${pgRole}, true), \
+    set_config('jwt.claims.sub', ${user?.sub ?? ''}, true), \
+    set_config('jwt.claims.email', ${user?.email ?? ''}, true)`
+}
+```
+
+:::
+
 ### Migrations
 
 `/apps/api/prisma/migrations/`
@@ -103,7 +135,30 @@ The generated migrations are also the point where you would add your custom RLS
 
 `/apps/api/prisma/seed.ts`
 
-In order to have some test-data we leverage the seed file feature of Prisma to create screenings in the near future of actually using and initializing the application. In this file we just have normal Prisma Client operations to create the data in the database.
+In order to have some test data we leverage the seed file feature of Prisma to create screenings in the near future of actually using and initializing the application. In this file we just have normal Prisma Client operations to create the data in the database.
+
+If you require additional test data you can perform these operations inside the `main` function like this:
+
+```ts
+...
+async function main() {
+  const locations = await prisma.location.createManyAndReturn({
+    data: [
+      {
+        name: 'Zurich',
+      },
+      {
+        name: 'Bern',
+      },
+    ],
+  })
+  ...
+}
+```
+
+:::info
+Prisma has [some limitations in creating batched entities with relationships](https://www.prisma.io/docs/orm/prisma-client/queries/relation-queries#create-multiple-records-and-multiple-related-records). Thats why we use `CreateManyAndReturn` to reference the created entities in subsequent queries.
+:::
 
 ## GraphQL server
 
diff --git a/apps/documentation/docs/further-details/_category_.json b/apps/documentation/docs/further-details/_category_.json
new file mode 100644
index 0000000000000000000000000000000000000000..96c7ba0616cb9e8ff1dd2c54252ef81b568e85b1
--- /dev/null
+++ b/apps/documentation/docs/further-details/_category_.json
@@ -0,0 +1,8 @@
+{
+  "label": "Further details",
+  "position": 6,
+  "link": {
+    "type": "generated-index",
+    "description": "Here are some additional useful resources for you."
+  }
+}
diff --git a/apps/documentation/docs/further-details/faq.md b/apps/documentation/docs/further-details/faq.md
new file mode 100644
index 0000000000000000000000000000000000000000..8ba837bcacd08955cfdee91d814f89e3ae9d0df3
--- /dev/null
+++ b/apps/documentation/docs/further-details/faq.md
@@ -0,0 +1,54 @@
+---
+sidebar_position: 1
+---
+
+# FAQ (frequently asked questions)
+
+## Input Object x must define one or more fields
+
+If you create a model with no scalar fields and only a primary key and relations the bulk operations that will be auto generated from TypeGraphQL-Prisma will have no fields to update. This is due to the limitation that in bulk operations you are restricted with the access to the relations. See [TypeGraphQL-Prisma Github issue for reference](https://github.com/MichalLytek/typegraphql-prisma/issues/19). As a workaround you can add a simple scalar field to the model:
+
+```prisma
+model ReservationToSeat {
+  reservation   Reservation @relation(fields: [reservationId], references: [id])
+  reservationId Int
+  seat          Seat        @relation(fields: [seatId], references: [id])
+  seatId        Int
+  // highlight-start
+  createdAt     DateTime    @default(now())
+  // highlight-end
+
+  @@id([reservationId, seatId])
+}
+```
+
+## Permission denied for table x
+
+You are trying to access a resource that you have not granted access to. If you are sure the API should have access to this table enable the specific operation to the table like this in your migration script where you create the table x:
+
+```sql
+GRANT SELECT ON x TO api;
+```
+
+Make sure that you also consider adding row level security policies to further restrict the rows in table x:
+
+```sql
+ALTER TABLE x ENABLE ROW LEVEL SECURITY;
+CREATE POLICY ... ON x USING ...;
+```
+
+An example can be found in [the tutorial "Create new model"](../tutorial-create-new/create_model.md#create-a-new-model).
+
+## API returns 502 Bad Gateway
+
+Most likely your API is not running correctly, this can happen when you perform certain changes like `npx prisma migrate dev`. The watcher did not notice the change and is in a state where he believes the code is incorrect. If you are sure your code is correct you can simply add and remove a `;` from any part of the API like in `/apps/api/src/index.ts:1`.
+
+## Unexpected token `'<', \"<html>\r\n<h\"... is not valid JSON`
+
+This relates to the same issue as [above and can be fixed the same way](#api-returns-502-bad-gateway).
+
+## IDE does not recognize the generated typings
+
+Our template watches your code for changes and triggers the generation every time something changed. Your IDE will not always notice that a certain file was recreated where the typings would exist. Depending on your IDE you will have to just change the focus of the window, try to lookup the typings or restart your language server as a whole.
+
+We acknowledge that this is not super convenient but we have no control over that.
diff --git a/apps/documentation/docs/tutorial-basics/_category_.json b/apps/documentation/docs/tutorial-basics/_category_.json
deleted file mode 100644
index ae129fb3b7a828c45bb2b58e1bc88a14e7293060..0000000000000000000000000000000000000000
--- a/apps/documentation/docs/tutorial-basics/_category_.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "label": "Tutorial - Basics",
-  "position": 10,
-  "link": {
-    "type": "generated-index",
-    "description": "5 minutes to learn the most important Docusaurus concepts."
-  }
-}
diff --git a/apps/documentation/docs/tutorial-basics/congratulations.md b/apps/documentation/docs/tutorial-basics/congratulations.md
deleted file mode 100644
index 04771a00b72f80ee4d829890a8229085da77db4d..0000000000000000000000000000000000000000
--- a/apps/documentation/docs/tutorial-basics/congratulations.md
+++ /dev/null
@@ -1,23 +0,0 @@
----
-sidebar_position: 6
----
-
-# Congratulations!
-
-You have just learned the **basics of Docusaurus** and made some changes to the **initial template**.
-
-Docusaurus has **much more to offer**!
-
-Have **5 more minutes**? Take a look at **[versioning](../tutorial-extras/manage-docs-versions.md)** and **[i18n](../tutorial-extras/translate-your-site.md)**.
-
-Anything **unclear** or **buggy** in this tutorial? [Please report it!](https://github.com/facebook/docusaurus/discussions/4610)
-
-## What's next?
-
-- Read the [official documentation](https://docusaurus.io/)
-- Modify your site configuration with [`docusaurus.config.js`](https://docusaurus.io/docs/api/docusaurus-config)
-- Add navbar and footer items with [`themeConfig`](https://docusaurus.io/docs/api/themes/configuration)
-- Add a custom [Design and Layout](https://docusaurus.io/docs/styling-layout)
-- Add a [search bar](https://docusaurus.io/docs/search)
-- Find inspirations in the [Docusaurus showcase](https://docusaurus.io/showcase)
-- Get involved in the [Docusaurus Community](https://docusaurus.io/community/support)
diff --git a/apps/documentation/docs/tutorial-basics/create-a-blog-post.md b/apps/documentation/docs/tutorial-basics/create-a-blog-post.md
deleted file mode 100644
index 550ae17ee1a2348bf4c66e75bf3a68f48a5a8083..0000000000000000000000000000000000000000
--- a/apps/documentation/docs/tutorial-basics/create-a-blog-post.md
+++ /dev/null
@@ -1,34 +0,0 @@
----
-sidebar_position: 3
----
-
-# Create a Blog Post
-
-Docusaurus creates a **page for each blog post**, but also a **blog index page**, a **tag system**, an **RSS** feed...
-
-## Create your first Post
-
-Create a file at `blog/2021-02-28-greetings.md`:
-
-```md title="blog/2021-02-28-greetings.md"
----
-slug: greetings
-title: Greetings!
-authors:
-  - name: Joel Marcey
-    title: Co-creator of Docusaurus 1
-    url: https://github.com/JoelMarcey
-    image_url: https://github.com/JoelMarcey.png
-  - name: Sébastien Lorber
-    title: Docusaurus maintainer
-    url: https://sebastienlorber.com
-    image_url: https://github.com/slorber.png
-tags: [greetings]
----
-
-Congratulations, you have made your first post!
-
-Feel free to play around and edit this post as much as you like.
-```
-
-A new blog post is now available at [http://localhost:3000/blog/greetings](http://localhost:3000/blog/greetings).
diff --git a/apps/documentation/docs/tutorial-basics/create-a-document.md b/apps/documentation/docs/tutorial-basics/create-a-document.md
deleted file mode 100644
index 2351eaee844e7c1667b8b88e87e19d0e1f949f1a..0000000000000000000000000000000000000000
--- a/apps/documentation/docs/tutorial-basics/create-a-document.md
+++ /dev/null
@@ -1,57 +0,0 @@
----
-sidebar_position: 2
----
-
-# Create a Document
-
-Documents are **groups of pages** connected through:
-
-- a **sidebar**
-- **previous/next navigation**
-- **versioning**
-
-## Create your first Doc
-
-Create a Markdown file at `docs/hello.md`:
-
-```md title="docs/hello.md"
-# Hello
-
-This is my **first Docusaurus document**!
-```
-
-A new document is now available at [http://localhost:3000/docs/hello](http://localhost:3000/docs/hello).
-
-## Configure the Sidebar
-
-Docusaurus automatically **creates a sidebar** from the `docs` folder.
-
-Add metadata to customize the sidebar label and position:
-
-```md title="docs/hello.md" {1-4}
----
-sidebar_label: 'Hi!'
-sidebar_position: 3
----
-
-# Hello
-
-This is my **first Docusaurus document**!
-```
-
-It is also possible to create your sidebar explicitly in `sidebars.js`:
-
-```js title="sidebars.js"
-export default {
-  tutorialSidebar: [
-    'intro',
-    // highlight-next-line
-    'hello',
-    {
-      type: 'category',
-      label: 'Tutorial',
-      items: ['tutorial-basics/create-a-document'],
-    },
-  ],
-}
-```
diff --git a/apps/documentation/docs/tutorial-basics/create-a-page.md b/apps/documentation/docs/tutorial-basics/create-a-page.md
deleted file mode 100644
index 36f46ce0c6d5f8c83872ae11da6cf370397c5b5f..0000000000000000000000000000000000000000
--- a/apps/documentation/docs/tutorial-basics/create-a-page.md
+++ /dev/null
@@ -1,43 +0,0 @@
----
-sidebar_position: 1
----
-
-# Create a Page
-
-Add **Markdown or React** files to `src/pages` to create a **standalone page**:
-
-- `src/pages/index.js` → `localhost:3000/`
-- `src/pages/foo.md` → `localhost:3000/foo`
-- `src/pages/foo/bar.js` → `localhost:3000/foo/bar`
-
-## Create your first React Page
-
-Create a file at `src/pages/my-react-page.js`:
-
-```jsx title="src/pages/my-react-page.js"
-import React from 'react'
-import Layout from '@theme/Layout'
-
-export default function MyReactPage() {
-  return (
-    <Layout>
-      <h1>My React page</h1>
-      <p>This is a React page</p>
-    </Layout>
-  )
-}
-```
-
-A new page is now available at [http://localhost:3000/my-react-page](http://localhost:3000/my-react-page).
-
-## Create your first Markdown Page
-
-Create a file at `src/pages/my-markdown-page.md`:
-
-```mdx title="src/pages/my-markdown-page.md"
-# My Markdown page
-
-This is a Markdown page
-```
-
-A new page is now available at [http://localhost:3000/my-markdown-page](http://localhost:3000/my-markdown-page).
diff --git a/apps/documentation/docs/tutorial-basics/deploy-your-site.md b/apps/documentation/docs/tutorial-basics/deploy-your-site.md
deleted file mode 100644
index 1c50ee063ef416333b994a910d10d05f984bf589..0000000000000000000000000000000000000000
--- a/apps/documentation/docs/tutorial-basics/deploy-your-site.md
+++ /dev/null
@@ -1,31 +0,0 @@
----
-sidebar_position: 5
----
-
-# Deploy your site
-
-Docusaurus is a **static-site-generator** (also called **[Jamstack](https://jamstack.org/)**).
-
-It builds your site as simple **static HTML, JavaScript and CSS files**.
-
-## Build your site
-
-Build your site **for production**:
-
-```bash
-npm run build
-```
-
-The static files are generated in the `build` folder.
-
-## Deploy your site
-
-Test your production build locally:
-
-```bash
-npm run serve
-```
-
-The `build` folder is now served at [http://localhost:3000/](http://localhost:3000/).
-
-You can now deploy the `build` folder **almost anywhere** easily, **for free** or very small cost (read the **[Deployment Guide](https://docusaurus.io/docs/deployment)**).
diff --git a/apps/documentation/docs/tutorial-basics/markdown-features.mdx b/apps/documentation/docs/tutorial-basics/markdown-features.mdx
deleted file mode 100644
index b1bec6a3440e25630fc02197da9b8741bee66d63..0000000000000000000000000000000000000000
--- a/apps/documentation/docs/tutorial-basics/markdown-features.mdx
+++ /dev/null
@@ -1,153 +0,0 @@
----
-sidebar_position: 4
----
-
-# Markdown Features
-
-Docusaurus supports **[Markdown](https://daringfireball.net/projects/markdown/syntax)** and a few **additional features**.
-
-## Front Matter
-
-Markdown documents have metadata at the top called [Front Matter](https://jekyllrb.com/docs/front-matter/):
-
-```text title="my-doc.md"
-// highlight-start
----
-id: my-doc-id
-title: My document title
-description: My document description
-slug: /my-custom-url
----
-// highlight-end
-
-## Markdown heading
-
-Markdown text with [links](./hello.md)
-```
-
-## Links
-
-Regular Markdown links are supported, using url paths or relative file paths.
-
-```md
-Let's see how to [Create a page](/create-a-page).
-```
-
-```md
-Let's see how to [Create a page](./create-a-page.md).
-```
-
-**Result:** Let's see how to [Create a page](./create-a-page.md).
-
-## Images
-
-Regular Markdown images are supported.
-
-You can use absolute paths to reference images in the static directory (`static/img/docusaurus.png`):
-
-```md
-![Docusaurus logo](/img/docusaurus.png)
-```
-
-![Docusaurus logo](/img/docusaurus.png)
-
-You can reference images relative to the current file as well. This is particularly useful to colocate images close to the Markdown files using them:
-
-```md
-![Docusaurus logo](./img/docusaurus.png)
-```
-
-## Code Blocks
-
-Markdown code blocks are supported with Syntax highlighting.
-
-````md
-```jsx title="src/components/HelloDocusaurus.js"
-function HelloDocusaurus() {
-  return <h1>Hello, Docusaurus!</h1>
-}
-```
-````
-
-```jsx title="src/components/HelloDocusaurus.js"
-function HelloDocusaurus() {
-  return <h1>Hello, Docusaurus!</h1>
-}
-```
-
-## Admonitions
-
-Docusaurus has a special syntax to create admonitions and callouts:
-
-```md
-:::tip My tip
-
-Use this awesome feature option
-
-:::
-
-:::danger Take care
-
-This action is dangerous
-
-:::
-```
-
-:::tip My tip
-
-Use this awesome feature option
-
-:::
-
-:::danger Take care
-
-This action is dangerous
-
-:::
-
-## MDX and React Components
-
-[MDX](https://mdxjs.com/) can make your documentation more **interactive** and allows using any **React components inside Markdown**:
-
-```jsx
-export const Highlight = ({children, color}) => (
-  <span
-    style={{
-      backgroundColor: color,
-      borderRadius: '20px',
-      color: '#fff',
-      padding: '10px',
-      cursor: 'pointer',
-    }}
-    onClick={() => {
-      alert(`You clicked the color ${color} with label ${children}`)
-    }}>
-    {children}
-  </span>
-);
-
-This is <Highlight color="#25c2a0">Docusaurus green</Highlight> !
-
-This is <Highlight color="#1877F2">Facebook blue</Highlight> !
-```
-
-export const Highlight = ({ children, color }) => (
-  <span
-    style={{
-      backgroundColor: color,
-      borderRadius: '20px',
-      color: '#fff',
-      padding: '10px',
-      cursor: 'pointer',
-    }}
-    onClick={() => {
-      alert(`You clicked the color ${color} with label ${children}`)
-    }}
-  >
-    {children}
-  </span>
-)
-
-This is <Highlight color="#25c2a0">Docusaurus green</Highlight> !
-
-This is <Highlight color="#1877F2">Facebook blue</Highlight> !
diff --git a/apps/documentation/docs/tutorial-create-new/_category_.json b/apps/documentation/docs/tutorial-create-new/_category_.json
new file mode 100644
index 0000000000000000000000000000000000000000..578153eaa65b012d837c40a9c112310fa7e52c72
--- /dev/null
+++ b/apps/documentation/docs/tutorial-create-new/_category_.json
@@ -0,0 +1,8 @@
+{
+  "label": "Tutorial - Create new model",
+  "position": 5,
+  "link": {
+    "type": "generated-index",
+    "description": "After learning the basic workflow, we can start with a bit more complex process by creating a whole new model and protecting that model with authentication/authorization"
+  }
+}
diff --git a/apps/documentation/docs/tutorial-create-new/create_model.md b/apps/documentation/docs/tutorial-create-new/create_model.md
new file mode 100644
index 0000000000000000000000000000000000000000..42781aa8f63cf71f9ed385074a5d3edf491747b1
--- /dev/null
+++ b/apps/documentation/docs/tutorial-create-new/create_model.md
@@ -0,0 +1,382 @@
+---
+sidebar_position: 1
+---
+
+# Create a new model
+
+After the previous tutorial you should be familiar on how to extend the application. In this tutorial we are going to show you how to create a new model with different relations and protect that model with authorization/authentication.
+
+## Update the Prisma schema
+
+Again start by locating the Prisma schema in `/apps/api/prisma/schema.prisma`.
+
+In this file you will now create a new model `Reservation`:
+
+```prisma
+// highlight-start
+model Reservation {
+  id          Int                 @id @default(autoincrement())
+  user        User                @relation(fields: [userId], references: [id])
+  userId      String
+  screening   Screening           @relation(fields: [screeningId], references: [id])
+  screeningId Int
+  seats       ReservationToSeat[]
+  status      ReservationStatus   @default(PENDING)
+}
+
+model ReservationToSeat {
+  reservation   Reservation @relation(fields: [reservationId], references: [id])
+  reservationId Int
+  seat          Seat        @relation(fields: [seatId], references: [id])
+  seatId        Int
+  createdAt     DateTime    @default(now())
+
+  @@id([reservationId, seatId])
+}
+
+enum ReservationStatus {
+  PENDING
+  CONFIRMED
+}
+// highlight-end
+```
+
+:::info
+With this model you will already have multiple relations. A reservation belongs to a single user. A reservation can be made for a single screening. A reservation can include multiple seats.
+:::
+
+In Prisma a relation can be accessed from both sides so both `Reservation` can see to which `User` it belongs, and a `User` can see what `Reservations` he has. We currently only modeled one side so we will have to update the existing models as well:
+
+```prisma
+model User {
+  id             String        @id @unique @default(cuid())
+  role           Role          @default(CUSTOMER)
+  email          String        @unique
+  hashedPassword String
+  salt           String
+  // highlight-start
+  reservations   Reservation[]
+  // highlight-end
+}
+...
+model Screening {
+  id          Int           @id @default(autoincrement())
+  movie       Movie         @relation(fields: [movieId], references: [id])
+  movieId     Int
+  showtime    DateTime
+  hall        Hall          @relation(fields: [hallId], references: [id])
+  hallId      Int
+  // highlight-start
+  reservations Reservation[]
+  // highlight-end
+}
+...
+model Seat {
+  id          Int           @id @default(autoincrement())
+  row         Int
+  number      Int
+  hall        Hall          @relation(fields: [hallId], references: [id])
+  hallId      Int
+  // highlight-start
+  reservations ReservationToSeat[]
+  // highlight-end
+}
+```
+
+## Update your database
+
+Again Prisma is aware of your new model, we also need to update your database.
+
+We have to enter the backend container to have access to the Prisma CLI:
+
+```bash
+docker exec -ti sa-api-1 bash
+npx prisma migrate dev --create-only
+```
+
+:::info
+We use the `--create-only` option so that we can modify the generated script manually before it's actually executed. In this script we can add the row level security policies to protect against unwanted access. We do recognize that this is not optimal but sadly you cannot create these policies inside the Prisma schema definition. See the [current issue](https://github.com/prisma/prisma/issues/12735) for any updates, maybe it will be possible at the time you are conducting this tutorial.
+:::
+
+```bash
+✔ Enter a name for the new migration: … add_model_reservation
+Prisma Migrate created the following migration without applying it 20241113090357_add_model_reservation
+
+You can now edit it and apply it by running prisma migrate dev.
+```
+
+Head to the mentioned folder, it will be different for you since you execute this command at a different time. For us it is `/apps/api/prisma/migrations/20241113090357_add_model_reservation/migration.sql`.
+
+Inside this file you will see all necessary SQL code to create these tables inside the database. Now we want to add some security to these tables. By default the API has no access to these tables (deny by default).
+
+```sql
+-- CreateTable
+CREATE TABLE "Reservation" (
+    "id" SERIAL NOT NULL,
+    "userId" TEXT NOT NULL,
+    "screeningId" INTEGER NOT NULL,
+    "status" "ReservationStatus" NOT NULL DEFAULT 'PENDING',
+
+    CONSTRAINT "Reservation_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "ReservationToSeat" (
+    "reservationId" INTEGER NOT NULL,
+    "seatId" INTEGER NOT NULL,
+    "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+
+    CONSTRAINT "ReservationToSeat_pkey" PRIMARY KEY ("reservationId","seatId")
+);
+```
+
+Lets extend these commands so that a user can only see his own reservations. Note that this has also be completed on the junction table `_ReservationToSeat`.
+
+```sql
+-- CreateTable
+CREATE TABLE "Reservation" (
+    "id" SERIAL NOT NULL,
+    "userId" TEXT NOT NULL,
+    "screeningId" INTEGER NOT NULL,
+    "status" "ReservationStatus" NOT NULL DEFAULT 'PENDING',
+
+    CONSTRAINT "Reservation_pkey" PRIMARY KEY ("id")
+);
+
+
+-- highlight-start
+ALTER TABLE "Reservation" ENABLE ROW LEVEL SECURITY;
+GRANT SELECT, INSERT ON "Reservation" TO api;
+CREATE POLICY user_can_only_view_his_own_reservations ON "Reservation"
+    USING ("userId" = current_setting('jwt.claims.sub'));
+CREATE POLICY api_can_only_create_pending_reservations ON "Reservation"
+    FOR INSERT
+    TO api
+    WITH CHECK (status = 'PENDING');
+-- highlight-end
+
+-- CreateTable
+CREATE TABLE "ReservationToSeat" (
+    "reservationId" INTEGER NOT NULL,
+    "seatId" INTEGER NOT NULL,
+
+    CONSTRAINT "ReservationToSeat_pkey" PRIMARY KEY ("reservationId","seatId")
+);
+
+-- highlight-start
+ALTER TABLE "ReservationToSeat" ENABLE ROW LEVEL SECURITY;
+GRANT SELECT, INSERT ON "ReservationToSeat" TO api;
+CREATE POLICY user_can_only_view_his_own_reservations ON "ReservationToSeat"
+    USING (
+        EXISTS (
+            SELECT 1
+            FROM "Reservation"
+            WHERE "Reservation"."id" = "ReservationToSeat"."reservationId"
+            AND "Reservation"."userId" = current_setting('jwt.claims.sub')
+        )
+    );
+CREATE POLICY api_can_only_reserve_seats_for_pending_reservations ON "ReservationToSeat"
+    FOR INSERT
+    TO api
+    WITH CHECK (
+        EXISTS (
+            SELECT 1
+            FROM "Reservation"
+            WHERE "Reservation"."id" = "ReservationToSeat"."reservationId"
+            AND "Reservation"."status" = 'PENDING'
+        )
+    );
+-- highlight-end
+```
+
+:::info
+You might be wondering where the `current_setting('jwt.claims.sub')` is being set. This comes from the creation of the two different Prisma instances [See the deep-dive section for more details](../application-deep-dive/backend.md#rls-row-level-security).
+:::
+
+:::info
+You can see that we also explicitly check the `userId` in the policy for the table `_ReservationToSeat` again. The policy expressions are run as part of the query and with the privileges of the user running the query and thus the security of the main table `Reservation` should already prevent access to other reservations. But as a best practice and to make it understandable we redeclare that access restriction of ` "Reservation"."userId" = current_setting('jwt.claims.sub')`.
+:::
+
+Now go back into the container and lets execute the migration:
+
+```bash
+docker exec -ti sa-api-1 bash
+npx prisma migrate dev
+```
+
+The output should look something like this:
+
+```bash
+Prisma schema loaded from prisma/schema.prisma
+Datasource "db": PostgreSQL database "cinema", schema "public" at "db:5432"
+
+Applying migration `20241113090357_add_model_reservation`
+
+The following migration(s) have been applied:
+
+migrations/
+  └─ 20241113090357_add_model_reservation/
+    └─ migration.sql
+
+Your database is now in sync with your schema.
+
+✔ Generated Prisma Client (v5.20.0) to ./node_modules/.pnpm/@prisma+client@5.20.0_prisma@5.20.0/node_modules/@prisma/client in 312ms
+✔ Generated TypeGraphQL integration to ./prisma/generated/type-graphql in 17.93s
+```
+
+You can see that Prisma applied the migration to the database and also triggered the generators Prisma Client and TypeGraphQL-Prisma.
+
+:::warning
+Similar when setting up the application your backend container probably crashed with the following message:
+
+```
+api-1            | 3:39:01 PM [tsx] unlink in ./prisma/generated/type-graphql/resolvers/relations/Movie/args/index.js Restarting...
+api-1            | node:internal/modules/cjs/loader:1249
+api-1            |   const err = new Error(message);
+api-1            |               ^
+api-1            |
+api-1            | Error: Cannot find module '../prisma/generated/type-graphql'
+api-1            | Require stack:
+api-1            | - /app/src/schema.ts
+api-1            | - /app/src/index.ts
+api-1            |     at node:internal/modules/cjs/loader:1249:15
+api-1            |     at nextResolveSimple (/app/node_modules/.pnpm/tsx@4.19.1/node_modules/tsx/dist/register-DpmFHar1.cjs:3:942)
+api-1            |     at /app/node_modules/.pnpm/tsx@4.19.1/node_modules/tsx/dist/register-DpmFHar1.cjs:2:2550
+api-1            |     at /app/node_modules/.pnpm/tsx@4.19.1/node_modules/tsx/dist/register-DpmFHar1.cjs:2:1624
+api-1            |     at resolveTsPaths (/app/node_modules/.pnpm/tsx@4.19.1/node_modules/tsx/dist/register-DpmFHar1.cjs:3:760)
+api-1            |     at /app/node_modules/.pnpm/tsx@4.19.1/node_modules/tsx/dist/register-DpmFHar1.cjs:3:1038
+api-1            |     at m._resolveFilename (file:///app/node_modules/.pnpm/tsx@4.19.1/node_modules/tsx/dist/register-Swi4xIvk.mjs:1:789)
+api-1            |     at Function._load (node:internal/modules/cjs/loader:1075:27)
+api-1            |     at TracingChannel.traceSync (node:diagnostics_channel:315:14)
+api-1            |     at wrapModuleLoad (node:internal/modules/cjs/loader:218:24) {
+api-1            |   code: 'MODULE_NOT_FOUND',
+api-1            |   requireStack: [ '/app/src/schema.ts', '/app/src/index.ts' ]
+api-1            | }
+api-1            |
+```
+
+The migration of the database (`npx prisma migrate dev`) triggers the recreation of the generated code. The watcher notices this with `[tsx] unlink in ./prisma/generated/...` and tries to restart your application, but this fails because the generated code is required to run the application. Sadly the watcher does not notice the recreation of the files and thus a manual change has to be done manually to trigger the generation (e.g. add and remove a `;` in `/apps/api/src/index.ts:1`)
+:::
+
+## Create test data
+
+It is always helpful to have some test data to manually test these features. For this purpose the [seeding script exists](../application-deep-dive/backend.md#seed).
+
+Lets head over to `/apps/api/prisma/seed.ts` and create some additional test data:
+
+```ts
+ ...
+ // highlight-start
+  await prisma.reservation.create({
+    data: {
+      userId: users[1].id,
+      screeningId: screenings[0].id,
+      seats: {
+        create: [
+          {
+            seatId: screenings[0].hall.seats[0].id,
+          },
+          {
+            seatId: screenings[0].hall.seats[1].id,
+          },
+        ],
+      },
+    },
+  })
+
+  await prisma.reservation.create({
+    data: {
+      userId: users[2].id,
+      screeningId: screenings[0].id,
+      seats: {
+        create: [
+          {
+            seatId: screenings[0].hall.seats[10].id,
+          },
+          {
+            seatId: screenings[0].hall.seats[11].id,
+          },
+        ],
+      },
+    },
+  })
+ // highlight-end
+```
+
+Lets recreate our database so that we can work with the newly created test data, since we are just developing we can use `reset`:
+
+```bash
+docker exec -ti sa-api-1 bash
+npx prisma migrate reset
+```
+
+## API playground
+
+Lets verify if the changes have any effect. Head over to your browser and go to playground [http://localhost:8080/api/](http://localhost:8080/api/).
+
+Try out this sample request:
+
+```
+query Reservations {
+  reservations {
+    id
+  }
+}
+```
+
+You should get the following output
+
+```
+{
+  "data": {
+    "reservations": []
+  }
+}
+```
+
+This is because we do not pass any JWT with the request. So we need to obtain a valid JWT.
+Execute the following request:
+
+```
+mutation login {
+    login(password: "SuperSecretPassword", email: "user1@user.com")
+}
+```
+
+From that you should get a JWT in the response:
+
+```
+{
+  "data": {
+    "login": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzE0OTUxMjUsInN1YiI6ImNtM2ZyNHM0MjAwMDAxMnl6dnF0aHpvbG4iLCJpYXQiOjE3MzE0OTQ1MjV9.JO-sQDVAMf_EvEDbyyBktKv3zmBMC_x5mE01B52QF4A"
+  }
+}
+```
+
+Copy your JWT (not this one, this one will be expired) and pass it as a header in the previous request:
+
+```
+Authorization    =>       Bearer <JWT>
+```
+
+![How to set authorization header](/img/header.png)
+
+You should now get the following output
+
+```
+{
+  "data": {
+    "reservations": [
+      {
+        "id": 1
+      }
+    ]
+  }
+}
+```
+
+:::info
+You can see that you only have access to your reservation but cannot be made aware of the reservations of `user2@user.com`. You can try to obtain a JWT for `user2@user.com` and repeat the process. You will see that now you can view the other reservation but not the previous one.
+:::
+
+In the next step we are going to extend our frontend with a new page and see a more complex use case.
diff --git a/apps/documentation/docs/tutorial-extending-existing/extend_model.md b/apps/documentation/docs/tutorial-extending-existing/extend_model.md
index 4ec3e56fe30ae3663248448efddf5f9d2439024b..9912d2313e35c5f2bb22535a0caf360a1ed4eebf 100644
--- a/apps/documentation/docs/tutorial-extending-existing/extend_model.md
+++ b/apps/documentation/docs/tutorial-extending-existing/extend_model.md
@@ -65,7 +65,7 @@ We have to enter the backend container because there we have the settings for th
 :::
 
 :::info
-We use `--create-only` option so that we can modify the generated script manually before it's actually executed. If you do not need to modify the script you remove this option.
+We use the `--create-only` option so that we can modify the generated script manually before it's actually executed. If you do not need to modify the script you remove this option.
 :::
 
 :::danger
@@ -123,7 +123,7 @@ ALTER TABLE "Movie" ALTER COLUMN "metascore" SET NOT NULL;
 -- highlight-end
 ```
 
-Now go back into the container and lets perform the migration:
+Now go back into the container and lets execute the migration:
 
 ```bash
 docker exec -ti sa-api-1 bash
@@ -250,7 +250,7 @@ You should get the following output
 }
 ```
 
-## Adjust seeding data
+## Adjust test data
 
 Having a metascore of 0 for all our test data is not optimal, so we want to adjust that.
 Head over to `/apps/api/prisma/seed.ts` and do the following adjustment.
diff --git a/apps/documentation/docs/tutorial-extras/_category_.json b/apps/documentation/docs/tutorial-extras/_category_.json
deleted file mode 100644
index baf50b81fc522a5113cdbdb0b7e12a40df8365e1..0000000000000000000000000000000000000000
--- a/apps/documentation/docs/tutorial-extras/_category_.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "label": "Tutorial - Extras",
-  "position": 11,
-  "link": {
-    "type": "generated-index"
-  }
-}
diff --git a/apps/documentation/docs/tutorial-extras/img/docsVersionDropdown.png b/apps/documentation/docs/tutorial-extras/img/docsVersionDropdown.png
deleted file mode 100644
index 97e4164618b5f8beda34cfa699720aba0ad2e342..0000000000000000000000000000000000000000
Binary files a/apps/documentation/docs/tutorial-extras/img/docsVersionDropdown.png and /dev/null differ
diff --git a/apps/documentation/docs/tutorial-extras/img/localeDropdown.png b/apps/documentation/docs/tutorial-extras/img/localeDropdown.png
deleted file mode 100644
index e257edc1f932985396bf59584c7ccfaddf955779..0000000000000000000000000000000000000000
Binary files a/apps/documentation/docs/tutorial-extras/img/localeDropdown.png and /dev/null differ
diff --git a/apps/documentation/docs/tutorial-extras/manage-docs-versions.md b/apps/documentation/docs/tutorial-extras/manage-docs-versions.md
deleted file mode 100644
index 0dbc5af532fb77ce53278919582407f126faa59e..0000000000000000000000000000000000000000
--- a/apps/documentation/docs/tutorial-extras/manage-docs-versions.md
+++ /dev/null
@@ -1,55 +0,0 @@
----
-sidebar_position: 1
----
-
-# Manage Docs Versions
-
-Docusaurus can manage multiple versions of your docs.
-
-## Create a docs version
-
-Release a version 1.0 of your project:
-
-```bash
-npm run docusaurus docs:version 1.0
-```
-
-The `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created.
-
-Your docs now have 2 versions:
-
-- `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs
-- `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs**
-
-## Add a Version Dropdown
-
-To navigate seamlessly across versions, add a version dropdown.
-
-Modify the `docusaurus.config.js` file:
-
-```js title="docusaurus.config.js"
-export default {
-  themeConfig: {
-    navbar: {
-      items: [
-        // highlight-start
-        {
-          type: 'docsVersionDropdown',
-        },
-        // highlight-end
-      ],
-    },
-  },
-}
-```
-
-The docs version dropdown appears in your navbar:
-
-![Docs Version Dropdown](./img/docsVersionDropdown.png)
-
-## Update an existing version
-
-It is possible to edit versioned docs in their respective folder:
-
-- `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello`
-- `docs/hello.md` updates `http://localhost:3000/docs/next/hello`
diff --git a/apps/documentation/docs/tutorial-extras/translate-your-site.md b/apps/documentation/docs/tutorial-extras/translate-your-site.md
deleted file mode 100644
index 43c336dd6eb57f64f957cb692e7679cb05f625f4..0000000000000000000000000000000000000000
--- a/apps/documentation/docs/tutorial-extras/translate-your-site.md
+++ /dev/null
@@ -1,88 +0,0 @@
----
-sidebar_position: 2
----
-
-# Translate your site
-
-Let's translate `docs/intro.md` to French.
-
-## Configure i18n
-
-Modify `docusaurus.config.js` to add support for the `fr` locale:
-
-```js title="docusaurus.config.js"
-export default {
-  i18n: {
-    defaultLocale: 'en',
-    locales: ['en', 'fr'],
-  },
-}
-```
-
-## Translate a doc
-
-Copy the `docs/intro.md` file to the `i18n/fr` folder:
-
-```bash
-mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/
-
-cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md
-```
-
-Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French.
-
-## Start your localized site
-
-Start your site on the French locale:
-
-```bash
-npm run start -- --locale fr
-```
-
-Your localized site is accessible at [http://localhost:3000/fr/](http://localhost:3000/fr/) and the `Getting Started` page is translated.
-
-:::caution
-
-In development, you can only use one locale at a time.
-
-:::
-
-## Add a Locale Dropdown
-
-To navigate seamlessly across languages, add a locale dropdown.
-
-Modify the `docusaurus.config.js` file:
-
-```js title="docusaurus.config.js"
-export default {
-  themeConfig: {
-    navbar: {
-      items: [
-        // highlight-start
-        {
-          type: 'localeDropdown',
-        },
-        // highlight-end
-      ],
-    },
-  },
-}
-```
-
-The locale dropdown now appears in your navbar:
-
-![Locale Dropdown](./img/localeDropdown.png)
-
-## Build your localized site
-
-Build your site for a specific locale:
-
-```bash
-npm run build -- --locale fr
-```
-
-Or build your site to include all the locales at once:
-
-```bash
-npm run build
-```
diff --git a/apps/documentation/static/img/header.png b/apps/documentation/static/img/header.png
new file mode 100644
index 0000000000000000000000000000000000000000..49ffd084b224600156f5297556aa17ee981a463a
Binary files /dev/null and b/apps/documentation/static/img/header.png differ
diff --git a/apps/frontend/package.json b/apps/frontend/package.json
index cc812c57861df1c684ebadecbee12bda161235dd..74ad0cb264899cc2699428a81be7ec4b451f405a 100644
--- a/apps/frontend/package.json
+++ b/apps/frontend/package.json
@@ -13,6 +13,7 @@
     "@apollo/client": "^3.11.8",
     "@emotion/react": "^11.13.3",
     "@emotion/styled": "^11.13.0",
+    "@mui/icons-material": "^6.1.7",
     "@mui/material": "^6.1.5",
     "date-fns": "^4.1.0",
     "graphql": "^16.9.0",
diff --git a/apps/frontend/src/assets/chair_free.svg b/apps/frontend/src/assets/chair_free.svg
deleted file mode 100644
index 6943ddef6c1d10bf968078a266780065963d8c64..0000000000000000000000000000000000000000
--- a/apps/frontend/src/assets/chair_free.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
-<svg fill="#FFFFFF" height="256px" width="256px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 510.396 510.396" xml:space="preserve" stroke="#FFFFFF">
-
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
-
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
-
<g id="SVGRepo_iconCarrier"> <g> <g> <path d="M460.344,159.26c-27.564,0-52.568,22.424-52.568,49.988v78.836v4.924v33.736c0,11.516-4.204,24.452-15.724,24.452H122.264 c-11.52,0-26.488-12.936-26.488-24.452v-33.736v-4.924v-78.836c0-27.564-19.624-49.988-47.184-49.988 C21.024,159.26,0,181.684,0,209.248c0,19.06,8.424,36.2,25.564,44.728c2.712,1.348,2.212,4.124,2.212,7.16v90.768 c0,18.468,12,34.252,28,40.568v33.984c0,11.536,15.076,24.744,26.612,24.744H432.6c11.54,0,15.176-13.204,15.176-24.744v-34.228 c16-6.488,28-22.104,28-40.324v-90.768c0-3.036,4.18-5.812,6.896-7.164c17.136-8.528,27.724-25.668,27.724-44.728 C510.396,181.684,487.912,159.26,460.344,159.26z"/> </g> </g> <g> <g> <path d="M381.472,59.196H133.344c-39.7,0-77.568,28.3-77.568,68v16.9c32,4.824,56,32.16,56,65.152v58.584 c0,4.176,5.388,3.428,11.812,3.364h266.936c1.692,0,1.252-3.744,1.252-3.364v-58.584c0-33.164,24-60.616,56-65.228v-16.824 C447.776,87.496,421.172,59.196,381.472,59.196z"/> </g> </g> <g> <g> <path d="M386.276,287.196H122.264c-2.648,0-10.488-0.46-10.488,2.188v3.624v33.736c0,8.452,0,8.452,10.488,8.452h265.512 c4,0,4,0,4-8.452v-33.736v-4.376C391.712,287.172,388.924,287.196,386.276,287.196z"/> </g> </g> </g>
-
</svg>
\ No newline at end of file
diff --git a/apps/frontend/src/assets/chair_reserved.svg b/apps/frontend/src/assets/chair_reserved.svg
deleted file mode 100644
index 2dd8383eb825c6c6ab628b0fb0c0db9bb7e5f829..0000000000000000000000000000000000000000
--- a/apps/frontend/src/assets/chair_reserved.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
-<svg fill="#D6424F" height="256px" width="256px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 510.396 510.396" xml:space="preserve" stroke="#D6424F">
-
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
-
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
-
<g id="SVGRepo_iconCarrier"> <g> <g> <path d="M460.344,159.26c-27.564,0-52.568,22.424-52.568,49.988v78.836v4.924v33.736c0,11.516-4.204,24.452-15.724,24.452H122.264 c-11.52,0-26.488-12.936-26.488-24.452v-33.736v-4.924v-78.836c0-27.564-19.624-49.988-47.184-49.988 C21.024,159.26,0,181.684,0,209.248c0,19.06,8.424,36.2,25.564,44.728c2.712,1.348,2.212,4.124,2.212,7.16v90.768 c0,18.468,12,34.252,28,40.568v33.984c0,11.536,15.076,24.744,26.612,24.744H432.6c11.54,0,15.176-13.204,15.176-24.744v-34.228 c16-6.488,28-22.104,28-40.324v-90.768c0-3.036,4.18-5.812,6.896-7.164c17.136-8.528,27.724-25.668,27.724-44.728 C510.396,181.684,487.912,159.26,460.344,159.26z"/> </g> </g> <g> <g> <path d="M381.472,59.196H133.344c-39.7,0-77.568,28.3-77.568,68v16.9c32,4.824,56,32.16,56,65.152v58.584 c0,4.176,5.388,3.428,11.812,3.364h266.936c1.692,0,1.252-3.744,1.252-3.364v-58.584c0-33.164,24-60.616,56-65.228v-16.824 C447.776,87.496,421.172,59.196,381.472,59.196z"/> </g> </g> <g> <g> <path d="M386.276,287.196H122.264c-2.648,0-10.488-0.46-10.488,2.188v3.624v33.736c0,8.452,0,8.452,10.488,8.452h265.512 c4,0,4,0,4-8.452v-33.736v-4.376C391.712,287.172,388.924,287.196,386.276,287.196z"/> </g> </g> </g>
-
</svg>
\ No newline at end of file
diff --git a/apps/frontend/src/common/components/navbar.tsx b/apps/frontend/src/common/components/navbar.tsx
index 0ed2aea24ab7a408243e37c0143517e0e25c1c70..5225949d5a84bfe2bfe91ed8d41273712b3f6f36 100644
--- a/apps/frontend/src/common/components/navbar.tsx
+++ b/apps/frontend/src/common/components/navbar.tsx
@@ -1,10 +1,12 @@
 import { useTranslation } from 'react-i18next'
-import { Box, Button, Link, Typography } from '@mui/material'
+import { Box, Button, Typography, Link } from '@mui/material'
 import { getToken, removeToken } from '../token.helper'
 import { useEffect, useState } from 'react'
+import { Outlet, useNavigate, Link as RouterLink } from 'react-router-dom'
 
 function Navbar() {
   const { t } = useTranslation()
+  const navigate = useNavigate()
 
   const [isLoggedIn, setIsLoggedIn] = useState(getToken() !== null)
 
@@ -21,71 +23,72 @@ function Navbar() {
   }, [])
 
   return (
-    <Box
-      sx={{
-        p: 2,
-      }}
-    >
+    <>
       <Box
         sx={{
-          backgroundColor: 'primary.light',
-          borderRadius: 1,
           p: 2,
-          display: 'flex',
-          alignItems: 'center',
-          justifyContent: 'space-between',
         }}
       >
         <Box
           sx={{
+            backgroundColor: 'primary.light',
+            borderRadius: 1,
+            p: 2,
             display: 'flex',
             alignItems: 'center',
+            justifyContent: 'space-between',
           }}
         >
-          <Link
-            href="/"
+          <Box
             sx={{
-              mb: -0.5,
+              display: 'flex',
+              alignItems: 'center',
             }}
           >
-            <img width="100" src="/ticket-favicon.svg" />
-          </Link>
-          <Typography
-            variant="h2"
-            sx={{
-              ml: 2,
-            }}
-          >
-            {t('navbar.title')}
-          </Typography>
-        </Box>
-        <nav>
-          <Link href="/">{t('navbar.links.overview')}</Link>
-
-          {isLoggedIn ? (
-            <Button
-              onClick={() => {
-                removeToken()
-              }}
-              sx={{
-                ml: 2,
-              }}
-            >
-              {t('navbar.logout')}
-            </Button>
-          ) : (
-            <Link
-              href="/login"
+            <Link component={RouterLink} to="/">
+              <img width="100" src="/ticket-favicon.svg" />
+            </Link>
+            <Typography
+              variant="h2"
               sx={{
                 ml: 2,
               }}
             >
-              {t('navbar.links.login')}
+              {t('navbar.title')}
+            </Typography>
+          </Box>
+          <nav>
+            <Link component={RouterLink} to="/">
+              {t('navbar.links.overview')}
             </Link>
-          )}
-        </nav>
+
+            {isLoggedIn ? (
+              <Button
+                onClick={() => {
+                  removeToken()
+                  navigate('/')
+                }}
+                sx={{
+                  ml: 2,
+                }}
+              >
+                {t('navbar.logout')}
+              </Button>
+            ) : (
+              <Link
+                href="/login"
+                sx={{
+                  ml: 2,
+                }}
+              >
+                {t('navbar.links.login')}
+              </Link>
+            )}
+          </nav>
+        </Box>
       </Box>
-    </Box>
+      <Outlet />
+    </>
   )
 }
 
diff --git a/apps/frontend/src/main.tsx b/apps/frontend/src/main.tsx
index 17665493eea935b3ca1798f481985a35e86d2c28..91ff2969a0966a337fdf74dd09ae8ea2894e3c21 100644
--- a/apps/frontend/src/main.tsx
+++ b/apps/frontend/src/main.tsx
@@ -17,24 +17,29 @@ import Detail from './pages/detail/detail.page.tsx'
 
 const router = createBrowserRouter([
   {
-    path: '/register',
-    element: <Register />,
-  },
-  {
-    path: '/login',
-    element: <Login />,
-  },
-  {
-    path: '/detail/:movieId',
-    element: <Detail />,
-  },
-  {
-    path: '/seatingplan/:screeningId',
-    element: <Seatingplan />,
-  },
-  {
-    path: '/',
-    element: <Overview />,
+    element: <Navbar />,
+    children: [
+      {
+        path: '/register',
+        element: <Register />,
+      },
+      {
+        path: '/login',
+        element: <Login />,
+      },
+      {
+        path: '/seatingplan/:screeningId',
+        element: <Seatingplan />,
+      },
+      {
+        path: '/detail/:movieId',
+        element: <Detail />,
+      },
+      {
+        path: '/',
+        element: <Overview />,
+      },
+    ],
   },
 ])
 
@@ -86,17 +91,6 @@ const theme = createTheme({
         },
       },
     },
-    MuiButton: {
-      styleOverrides: {
-        root: {
-          backgroundColor: '#42D6CA',
-          color: '#304b4a',
-          '&:hover': {
-            backgroundColor: '#4af1e2',
-          },
-        },
-      },
-    },
   },
   typography: {
     h1: {
@@ -113,7 +107,6 @@ createRoot(document.getElementById('root')!).render(
     <ApolloProvider client={client}>
       <ThemeProvider theme={theme}>
         <CssBaseline />
-        <Navbar />
         <RouterProvider router={router} />
       </ThemeProvider>
     </ApolloProvider>
diff --git a/apps/frontend/src/pages/login/login.page.tsx b/apps/frontend/src/pages/login/login.page.tsx
index 48b2f4cfd056f128a2c21b07a8afc20fd5f9f328..8bc044da5dfa821e0859436f6ddf9881eaa331bd 100644
--- a/apps/frontend/src/pages/login/login.page.tsx
+++ b/apps/frontend/src/pages/login/login.page.tsx
@@ -1,7 +1,7 @@
 import { useMutation } from '@apollo/client'
 import { useForm } from 'react-hook-form'
 import { useTranslation } from 'react-i18next'
-import { useNavigate } from 'react-router-dom'
+import { useLocation, useNavigate } from 'react-router-dom'
 import { graphql } from '../../gql'
 import {
   Alert,
@@ -31,6 +31,7 @@ const LOGIN = graphql(`
 function Login() {
   const { t } = useTranslation()
   const navigate = useNavigate()
+  const location = useLocation()
 
   const {
     register,
@@ -49,8 +50,9 @@ function Login() {
 
   const [triggerLogin, { loading, error }] = useMutation<LoginMutation>(LOGIN, {
     onCompleted: (jwt) => {
+      const redirectTo = location.state?.from || '/'
+      navigate(redirectTo, { replace: true })
       setToken(jwt.login)
-      navigate('/')
     },
     onError: () => {
       // required so that no error is thrown in the console
@@ -130,6 +132,7 @@ function Login() {
               sx={{
                 py: 1.5,
               }}
+              variant="contained"
             >
               {t('login.submit')}
             </Button>
diff --git a/apps/frontend/src/pages/register/register.page.tsx b/apps/frontend/src/pages/register/register.page.tsx
index ef79349049f08f854e5e4bede12a44040a485f6b..872040630b90c89b171f29d018b2089fd11f3650 100644
--- a/apps/frontend/src/pages/register/register.page.tsx
+++ b/apps/frontend/src/pages/register/register.page.tsx
@@ -127,6 +127,7 @@ function Register() {
               sx={{
                 py: 1.5,
               }}
+              variant="contained"
             >
               {t('register.submit')}
             </Button>
diff --git a/apps/frontend/src/pages/seatingplan/seatingplan.page.tsx b/apps/frontend/src/pages/seatingplan/seatingplan.page.tsx
index 54c176660485c535495cbf274ca2148862d8eb95..bddfeb5bfe4cb1eaaeab10d1447160d9315002c6 100644
--- a/apps/frontend/src/pages/seatingplan/seatingplan.page.tsx
+++ b/apps/frontend/src/pages/seatingplan/seatingplan.page.tsx
@@ -1,11 +1,20 @@
-import { Box, Button, Skeleton, Typography } from '@mui/material'
+import {
+  Box,
+  Button,
+  Chip,
+  IconButton,
+  Skeleton,
+  Typography,
+} from '@mui/material'
 import { graphql } from '../../gql'
 import { ScreeningQuery } from '../../gql/graphql.ts'
-import emptySeat from '../../assets/chair_free.svg'
 import { useTranslation } from 'react-i18next'
-import { useParams } from 'react-router-dom'
+import { useNavigate, useParams, useLocation } from 'react-router-dom'
 import { useQuery } from '@apollo/client'
 import MovieDetail from '../../common/components/MovieDetail.tsx'
+import { useState } from 'react'
+import { getToken } from '../../common/token.helper.ts'
+import ChairIcon from '@mui/icons-material/Chair'
 
 const SCREENING = graphql(`
   query SCREENING($id: Int!) {
@@ -27,6 +36,7 @@ const SCREENING = graphql(`
           id
         }
         seats {
+          id
           row
           number
         }
@@ -39,6 +49,12 @@ const SCREENING = graphql(`
 function Seatingplan() {
   const { t } = useTranslation()
   const { screeningId } = useParams()
+  const navigate = useNavigate()
+  const location = useLocation()
+
+  const [selectedSeats, setSelectedSeats] = useState<
+    NonNullable<ScreeningQuery['screening']>['hall']['seats']
+  >([])
 
   const { data, loading } = useQuery<ScreeningQuery>(SCREENING, {
     variables: {
@@ -55,12 +71,20 @@ function Seatingplan() {
       if (!acc[current.row]) {
         acc[current.row] = []
       }
-      acc[current.row].push(current.number)
+      acc[current.row].push(current)
       return acc
     },
-    {} as Record<number, number[]>
+    {} as Record<
+      number,
+      NonNullable<ScreeningQuery['screening']>['hall']['seats']
+    >
   )
 
+  const makeReservation = () => {
+    // TODO: perform reservation here
+    console.log(selectedSeats)
+  }
+
   return (
     <Box>
       {loading ? (
@@ -125,12 +149,31 @@ function Seatingplan() {
                       }}
                     >
                       {seats.map((seat) => (
-                        <img
-                          key={seat.toString()}
-                          src={emptySeat}
-                          alt="emptySeat"
-                          width="30"
-                        ></img>
+                        <IconButton
+                          key={seat.id}
+                          sx={{ padding: 0.5 }}
+                          color={
+                            selectedSeats.includes(seat, 0)
+                              ? 'primary'
+                              : 'default'
+                          }
+                          onClick={() => {
+                            if (getToken() === null) {
+                              navigate('/login', {
+                                state: { from: location.pathname },
+                              })
+                            }
+                            if (selectedSeats.includes(seat, 0)) {
+                              setSelectedSeats(
+                                selectedSeats.filter((s) => s.id !== seat.id)
+                              )
+                            } else {
+                              setSelectedSeats([...selectedSeats, seat])
+                            }
+                          }}
+                        >
+                          <ChairIcon />
+                        </IconButton>
                       ))}
                     </Box>
                   ))
@@ -165,17 +208,43 @@ function Seatingplan() {
             }}
           >
             <Box>
-              <Typography variant="h4">{t('seatingplan.tickets')}:</Typography>
+              <Typography variant="h4">
+                {t('seatingplan.tickets')}: {selectedSeats.length}
+              </Typography>
+              <Box
+                sx={{
+                  display: 'flex',
+                  flexWrap: 'wrap',
+                  gap: 1,
+                  mt: 1,
+                }}
+              >
+                {selectedSeats.map((seat) => (
+                  <Chip
+                    key={seat.id}
+                    label={`${seat.row} - ${seat.number}`}
+                    onDelete={() => {
+                      setSelectedSeats(
+                        selectedSeats.filter((s) => seat.id != s.id)
+                      )
+                    }}
+                  />
+                ))}
+              </Box>
             </Box>
             <Box>
               <Button
                 sx={{
                   mr: 1,
                 }}
+                disabled={selectedSeats.length <= 0}
+                variant="contained"
+                onClick={makeReservation}
               >
                 {t('seatingplan.confirm')}
               </Button>
               <Button
+                variant="contained"
                 sx={{
                   backgroundColor: '#8F8F8F',
                   color: '#3f3f3f',
@@ -183,6 +252,10 @@ function Seatingplan() {
                     backgroundColor: '#6F6F6F',
                   },
                 }}
+                disabled={selectedSeats.length <= 0}
+                onClick={() => {
+                  setSelectedSeats([])
+                }}
               >
                 {t('seatingplan.cancel')}
               </Button>
diff --git a/docs/sections/07_side-goal.typ b/docs/sections/07_side-goal.typ
index 72d25c521bc6d17e1eaa3a5453735bfebb7b2efb..509cc8f52450746d526729d13d72b8a0a0574b94 100644
--- a/docs/sections/07_side-goal.typ
+++ b/docs/sections/07_side-goal.typ
@@ -22,7 +22,7 @@ It should be possible for a user to simply create a data model with all its prop
   - customizable data type (string, number, date, raw bytes ...)
   - simple constraints (uniqueness, min-/max-length, regular expression pattern)
 - relationships
-  - support for one-many, many-one and many-many relationships
+  - support for one-to-many, many-to-one and many-to-many relationships
   - circular relationships
 
 After creating a model it should still be possible to edit a saved data model with all its properties.
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 946968d0f9f9eacce59396115a5c3775721b32d0..565ed3e497faa779572f9295896c80539ad14321 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -142,6 +142,9 @@ importers:
       '@emotion/styled':
         specifier: ^11.13.0
         version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.8)(react@18.3.1))(@types/react@18.3.8)(react@18.3.1)
+      '@mui/icons-material':
+        specifier: ^6.1.7
+        version: 6.1.7(@mui/material@6.1.5(@emotion/react@11.13.3(@types/react@18.3.8)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.8)(react@18.3.1))(@types/react@18.3.8)(react@18.3.1))(@types/react@18.3.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.8)(react@18.3.1)
       '@mui/material':
         specifier: ^6.1.5
         version: 6.1.5(@emotion/react@11.13.3(@types/react@18.3.8)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.8)(react@18.3.1))(@types/react@18.3.8)(react@18.3.1))(@types/react@18.3.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -1030,6 +1033,10 @@ packages:
     resolution: {integrity: sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/runtime@7.26.0':
+    resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/template@7.25.7':
     resolution: {integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==}
     engines: {node: '>=6.9.0'}
@@ -1892,6 +1899,17 @@ packages:
   '@mui/core-downloads-tracker@6.1.5':
     resolution: {integrity: sha512-3J96098GrC95XsLw/TpGNMxhUOnoG9NZ/17Pfk1CrJj+4rcuolsF2RdF3XAFTu/3a/A+5ouxlSIykzYz6Ee87g==}
 
+  '@mui/icons-material@6.1.7':
+    resolution: {integrity: sha512-RGzkeHNArIVy5ZQ12bq/8VYNeICEyngngsFskTJ/2hYKhIeIII3iRGtaZaSvLpXh7h3Fg3VKTulT+QU0w5K4XQ==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      '@mui/material': ^6.1.7
+      '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
+      react: ^17.0.0 || ^18.0.0 || ^19.0.0
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
   '@mui/material@6.1.5':
     resolution: {integrity: sha512-rhaxC7LnlOG8zIVYv7BycNbWkC5dlm9A/tcDUp0CuwA7Zf9B9JP6M3rr50cNKxI7Z0GIUesAT86ceVm44quwnQ==}
     engines: {node: '>=14.0.0'}
@@ -8348,6 +8366,10 @@ snapshots:
     dependencies:
       regenerator-runtime: 0.14.1
 
+  '@babel/runtime@7.26.0':
+    dependencies:
+      regenerator-runtime: 0.14.1
+
   '@babel/template@7.25.7':
     dependencies:
       '@babel/code-frame': 7.25.7
@@ -9860,6 +9882,14 @@ snapshots:
 
   '@mui/core-downloads-tracker@6.1.5': {}
 
+  '@mui/icons-material@6.1.7(@mui/material@6.1.5(@emotion/react@11.13.3(@types/react@18.3.8)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.8)(react@18.3.1))(@types/react@18.3.8)(react@18.3.1))(@types/react@18.3.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.8)(react@18.3.1)':
+    dependencies:
+      '@babel/runtime': 7.26.0
+      '@mui/material': 6.1.5(@emotion/react@11.13.3(@types/react@18.3.8)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.8)(react@18.3.1))(@types/react@18.3.8)(react@18.3.1))(@types/react@18.3.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      react: 18.3.1
+    optionalDependencies:
+      '@types/react': 18.3.8
+
   '@mui/material@6.1.5(@emotion/react@11.13.3(@types/react@18.3.8)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.8)(react@18.3.1))(@types/react@18.3.8)(react@18.3.1))(@types/react@18.3.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
       '@babel/runtime': 7.25.7