From 63b946131bcd8ee72d50b3d2038e93eb85770c6f Mon Sep 17 00:00:00 2001
From: Anis Koubaa <mohamed.koubaa@kit.edu>
Date: Sun, 23 Feb 2025 13:08:55 +0100
Subject: [PATCH] make vuetify as framework.

---
 services/.idea/.gitignore                     |   8 +
 services/frontend/.eslintrc-auto-import.json  |  79 +++++++++
 services/frontend/README.md                   |   2 +-
 services/frontend/eslint.config.js            |  23 +++
 services/frontend/index.html                  |  13 ++
 services/frontend/jsconfig.json               |   3 +-
 services/frontend/package.json                |  73 ++++----
 services/frontend/src/App.vue                 |  16 +-
 services/frontend/src/assets/logo.svg         |   6 +
 .../frontend/src/components/AppFooter.vue     |  82 +++++++++
 .../frontend/src/components/HelloWorld.vue    | 163 ++++++++++++++++++
 services/frontend/src/components/README.md    |  35 ++++
 services/frontend/src/layouts/README.md       |   5 +
 services/frontend/src/layouts/default.vue     |  12 ++
 services/frontend/src/main.js                 |  26 +--
 services/frontend/src/pages/README.md         |   5 +
 services/frontend/src/pages/index.vue         |  18 ++
 services/frontend/src/plugins/README.md       |   3 +
 services/frontend/src/plugins/index.js        |  17 ++
 services/frontend/src/plugins/vuetify.js      |  19 ++
 services/frontend/src/router/index.js         |  55 +++---
 services/frontend/src/stores/README.md        |   5 +
 services/frontend/src/stores/app.js           |   8 +
 services/frontend/src/stores/index.js         |   4 +
 services/frontend/src/styles/README.md        |   3 +
 services/frontend/src/styles/settings.scss    |  10 ++
 services/frontend/vite.config.mjs             |  74 ++++++++
 services/frontend/vue.config.js               |   4 -
 28 files changed, 676 insertions(+), 95 deletions(-)
 create mode 100644 services/.idea/.gitignore
 create mode 100644 services/frontend/.eslintrc-auto-import.json
 create mode 100644 services/frontend/eslint.config.js
 create mode 100644 services/frontend/index.html
 create mode 100644 services/frontend/src/assets/logo.svg
 create mode 100644 services/frontend/src/components/AppFooter.vue
 create mode 100644 services/frontend/src/components/HelloWorld.vue
 create mode 100644 services/frontend/src/components/README.md
 create mode 100644 services/frontend/src/layouts/README.md
 create mode 100644 services/frontend/src/layouts/default.vue
 create mode 100644 services/frontend/src/pages/README.md
 create mode 100644 services/frontend/src/pages/index.vue
 create mode 100644 services/frontend/src/plugins/README.md
 create mode 100644 services/frontend/src/plugins/index.js
 create mode 100644 services/frontend/src/plugins/vuetify.js
 create mode 100644 services/frontend/src/stores/README.md
 create mode 100644 services/frontend/src/stores/app.js
 create mode 100644 services/frontend/src/stores/index.js
 create mode 100644 services/frontend/src/styles/README.md
 create mode 100644 services/frontend/src/styles/settings.scss
 create mode 100644 services/frontend/vite.config.mjs
 delete mode 100644 services/frontend/vue.config.js

diff --git a/services/.idea/.gitignore b/services/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/services/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/services/frontend/.eslintrc-auto-import.json b/services/frontend/.eslintrc-auto-import.json
new file mode 100644
index 0000000..0a9140c
--- /dev/null
+++ b/services/frontend/.eslintrc-auto-import.json
@@ -0,0 +1,79 @@
+{
+  "globals": {
+    "Component": true,
+    "ComponentPublicInstance": true,
+    "ComputedRef": true,
+    "DirectiveBinding": true,
+    "EffectScope": true,
+    "ExtractDefaultPropTypes": true,
+    "ExtractPropTypes": true,
+    "ExtractPublicPropTypes": true,
+    "InjectionKey": true,
+    "MaybeRef": true,
+    "MaybeRefOrGetter": true,
+    "PropType": true,
+    "Ref": true,
+    "VNode": true,
+    "WritableComputedRef": true,
+    "computed": true,
+    "createApp": true,
+    "customRef": true,
+    "defineAsyncComponent": true,
+    "defineComponent": true,
+    "effectScope": true,
+    "getCurrentInstance": true,
+    "getCurrentScope": true,
+    "h": true,
+    "inject": true,
+    "isProxy": true,
+    "isReactive": true,
+    "isReadonly": true,
+    "isRef": true,
+    "markRaw": true,
+    "nextTick": true,
+    "onActivated": true,
+    "onBeforeMount": true,
+    "onBeforeRouteLeave": true,
+    "onBeforeRouteUpdate": true,
+    "onBeforeUnmount": true,
+    "onBeforeUpdate": true,
+    "onDeactivated": true,
+    "onErrorCaptured": true,
+    "onMounted": true,
+    "onRenderTracked": true,
+    "onRenderTriggered": true,
+    "onScopeDispose": true,
+    "onServerPrefetch": true,
+    "onUnmounted": true,
+    "onUpdated": true,
+    "onWatcherCleanup": true,
+    "provide": true,
+    "reactive": true,
+    "readonly": true,
+    "ref": true,
+    "resolveComponent": true,
+    "shallowReactive": true,
+    "shallowReadonly": true,
+    "shallowRef": true,
+    "toRaw": true,
+    "toRef": true,
+    "toRefs": true,
+    "toValue": true,
+    "triggerRef": true,
+    "unref": true,
+    "useAttrs": true,
+    "useCssModule": true,
+    "useCssVars": true,
+    "useId": true,
+    "useLink": true,
+    "useModel": true,
+    "useRoute": true,
+    "useRouter": true,
+    "useSlots": true,
+    "useTemplateRef": true,
+    "watch": true,
+    "watchEffect": true,
+    "watchPostEffect": true,
+    "watchSyncEffect": true
+  }
+}
diff --git a/services/frontend/README.md b/services/frontend/README.md
index 576b980..3d9295b 100644
--- a/services/frontend/README.md
+++ b/services/frontend/README.md
@@ -7,7 +7,7 @@ npm install
 
 ### Compiles and hot-reloads for development
 ```
-npm run serve
+npm run dev
 ```
 
 ### Compiles and minifies for production
diff --git a/services/frontend/eslint.config.js b/services/frontend/eslint.config.js
new file mode 100644
index 0000000..944e82b
--- /dev/null
+++ b/services/frontend/eslint.config.js
@@ -0,0 +1,23 @@
+import js from '@eslint/js'
+import pluginVue from 'eslint-plugin-vue'
+
+export default [
+  {
+    name: 'app/files-to-lint',
+    files: ['**/*.{js,mjs,jsx,vue}'],
+  },
+
+  {
+    name: 'app/files-to-ignore',
+    ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
+  },
+
+  js.configs.recommended,
+  ...pluginVue.configs['flat/recommended'],
+
+  {
+    rules: {
+      'vue/multi-word-component-names': 'off',
+    },
+  }
+]
diff --git a/services/frontend/index.html b/services/frontend/index.html
new file mode 100644
index 0000000..0a84a1c
--- /dev/null
+++ b/services/frontend/index.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8">
+    <link rel="icon" href="/favicon.ico">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Welcome to Vuetify 3</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>
diff --git a/services/frontend/jsconfig.json b/services/frontend/jsconfig.json
index 4aafc5f..dad0634 100644
--- a/services/frontend/jsconfig.json
+++ b/services/frontend/jsconfig.json
@@ -1,9 +1,10 @@
 {
   "compilerOptions": {
+    "allowJs": true,
     "target": "es5",
     "module": "esnext",
     "baseUrl": "./",
-    "moduleResolution": "node",
+    "moduleResolution": "bundler",
     "paths": {
       "@/*": [
         "src/*"
diff --git a/services/frontend/package.json b/services/frontend/package.json
index 62fd8cd..915cc08 100644
--- a/services/frontend/package.json
+++ b/services/frontend/package.json
@@ -1,49 +1,40 @@
 {
-  "name": "frontend",
-  "version": "0.1.0",
+  "name": "regimo",
   "private": true,
+  "type": "module",
+  "version": "0.0.0",
   "scripts": {
-    "serve": "vue-cli-service serve",
-    "build": "vue-cli-service build",
-    "lint": "vue-cli-service lint"
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview",
+    "lint": "eslint . --fix"
   },
   "dependencies": {
-    "axios": "^1.2.1",
-    "bootstrap": "^5.3.3",
-    "bootstrap-vue": "^2.23.1",
-    "core-js": "^3.8.3",
-    "vue": "^3.4.27",
-    "vue-router": "^4.0.3",
-    "vuex": "^4.1.0"
+    "@mdi/font": "7.4.47",
+    "roboto-fontface": "*",
+    "vue": "^3.4.31",
+    "vuetify": "^3.6.14"
   },
   "devDependencies": {
-    "@babel/core": "^7.12.16",
-    "@babel/eslint-parser": "^7.12.16",
-    "@vue/cli-plugin-babel": "~5.0.0",
-    "@vue/cli-plugin-eslint": "~5.0.0",
-    "@vue/cli-plugin-router": "~5.0.0",
-    "@vue/cli-service": "~5.0.0",
-    "eslint": "^7.32.0",
-    "eslint-plugin-vue": "^8.0.3"
-  },
-  "eslintConfig": {
-    "root": true,
-    "env": {
-      "node": true
-    },
-    "extends": [
-      "plugin:vue/vue3-essential",
-      "eslint:recommended"
-    ],
-    "parserOptions": {
-      "parser": "@babel/eslint-parser"
-    },
-    "rules": {}
-  },
-  "browserslist": [
-    "> 1%",
-    "last 2 versions",
-    "not dead",
-    "not ie 11"
-  ]
+    "@eslint/js": "^9.14.0",
+    "@vitejs/plugin-vue": "^5.0.5",
+    "eslint": "^9.14.0",
+    "eslint-plugin-import": "^2.29.1",
+    "eslint-plugin-n": "^16.6.2",
+    "eslint-plugin-node": "^11.1.0",
+    "eslint-plugin-promise": "^6.4.0",
+    "eslint-plugin-vue": "^9.30.0",
+    "eslint-visitor-keys": "^4.2.0",
+    "pinia": "^2.1.7",
+    "sass": "1.77.8",
+    "sass-embedded": "^1.77.8",
+    "unplugin-auto-import": "^0.17.6",
+    "unplugin-fonts": "^1.1.1",
+    "unplugin-vue-components": "^0.27.2",
+    "unplugin-vue-router": "^0.10.0",
+    "vite": "^6.1.1",
+    "vite-plugin-vue-layouts": "^0.11.0",
+    "vite-plugin-vuetify": "^2.0.3",
+    "vue-router": "^4.4.0"
+  }
 }
diff --git a/services/frontend/src/App.vue b/services/frontend/src/App.vue
index b2897ab..4f21c35 100644
--- a/services/frontend/src/App.vue
+++ b/services/frontend/src/App.vue
@@ -1,15 +1,5 @@
 <template>
-  <div id="app">
-    <router-view/>
-  </div>
+  <v-app>
+    <router-view />
+  </v-app>
 </template>
-
-<style>
-#app {
-  font-family: Avenir, Helvetica, Arial, sans-serif;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  text-align: center;
-  color: #2c3e50;
-}
-</style>
\ No newline at end of file
diff --git a/services/frontend/src/assets/logo.svg b/services/frontend/src/assets/logo.svg
new file mode 100644
index 0000000..d57771c
--- /dev/null
+++ b/services/frontend/src/assets/logo.svg
@@ -0,0 +1,6 @@
+<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M261.126 140.65L164.624 307.732L256.001 466L377.028 256.5L498.001 47H315.192L261.126 140.65Z" fill="#1697F6"/>
+<path d="M135.027 256.5L141.365 267.518L231.64 111.178L268.731 47H256H14L135.027 256.5Z" fill="#AEDDFF"/>
+<path d="M315.191 47C360.935 197.446 256 466 256 466L164.624 307.732L315.191 47Z" fill="#1867C0"/>
+<path d="M268.731 47C76.0026 47 141.366 267.518 141.366 267.518L268.731 47Z" fill="#7BC6FF"/>
+</svg>
diff --git a/services/frontend/src/components/AppFooter.vue b/services/frontend/src/components/AppFooter.vue
new file mode 100644
index 0000000..0561fef
--- /dev/null
+++ b/services/frontend/src/components/AppFooter.vue
@@ -0,0 +1,82 @@
+<template>
+  <v-footer 
+    height="40" 
+    app
+  >
+    <a 
+      v-for="item in items"
+      :key="item.title"
+      :href="item.href"
+      :title="item.title"
+      class="d-inline-block mx-2 social-link"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      <v-icon
+        :icon="item.icon"
+        :size="item.icon === '$vuetify' ? 24 : 16"
+      />
+    </a>
+
+    <div
+      class="text-caption text-disabled"
+      style="position: absolute; right: 16px;"
+    >
+      &copy; 2016-{{ (new Date()).getFullYear() }} <span class="d-none d-sm-inline-block">Vuetify, LLC</span>
+      —
+      <a
+        class="text-decoration-none on-surface"
+        href="https://vuetifyjs.com/about/licensing/"
+        rel="noopener noreferrer"
+        target="_blank"
+      >
+        MIT License
+      </a>
+    </div>
+  </v-footer>
+</template>
+
+<script setup>
+const items = [
+  {
+    title: 'Vuetify Documentation',
+    icon: `$vuetify`,
+    href: 'https://vuetifyjs.com/',
+  },
+  {
+    title: 'Vuetify Support',
+    icon: 'mdi-shield-star-outline',
+    href: 'https://support.vuetifyjs.com/',
+  },
+  {
+    title: 'Vuetify X',
+    icon: ['M2.04875 3.00002L9.77052 13.3248L1.99998 21.7192H3.74882L10.5519 14.3697L16.0486 21.7192H22L13.8437 10.8137L21.0765 3.00002H19.3277L13.0624 9.76874L8.0001 3.00002H2.04875ZM4.62054 4.28821H7.35461L19.4278 20.4308H16.6937L4.62054 4.28821Z'],
+    href: 'https://x.com/vuetifyjs',
+  },
+  {
+    title: 'Vuetify GitHub',
+    icon: `mdi-github`,
+    href: 'https://github.com/vuetifyjs/vuetify',
+  },
+  {
+    title: 'Vuetify Discord',
+    icon: ['M22,24L16.75,19L17.38,21H4.5A2.5,2.5 0 0,1 2,18.5V3.5A2.5,2.5 0 0,1 4.5,1H19.5A2.5,2.5 0 0,1 22,3.5V24M12,6.8C9.32,6.8 7.44,7.95 7.44,7.95C8.47,7.03 10.27,6.5 10.27,6.5L10.1,6.33C8.41,6.36 6.88,7.53 6.88,7.53C5.16,11.12 5.27,14.22 5.27,14.22C6.67,16.03 8.75,15.9 8.75,15.9L9.46,15C8.21,14.73 7.42,13.62 7.42,13.62C7.42,13.62 9.3,14.9 12,14.9C14.7,14.9 16.58,13.62 16.58,13.62C16.58,13.62 15.79,14.73 14.54,15L15.25,15.9C15.25,15.9 17.33,16.03 18.73,14.22C18.73,14.22 18.84,11.12 17.12,7.53C17.12,7.53 15.59,6.36 13.9,6.33L13.73,6.5C13.73,6.5 15.53,7.03 16.56,7.95C16.56,7.95 14.68,6.8 12,6.8M9.93,10.59C10.58,10.59 11.11,11.16 11.1,11.86C11.1,12.55 10.58,13.13 9.93,13.13C9.29,13.13 8.77,12.55 8.77,11.86C8.77,11.16 9.28,10.59 9.93,10.59M14.1,10.59C14.75,10.59 15.27,11.16 15.27,11.86C15.27,12.55 14.75,13.13 14.1,13.13C13.46,13.13 12.94,12.55 12.94,11.86C12.94,11.16 13.45,10.59 14.1,10.59Z'],
+    href: 'https://community.vuetifyjs.com/',
+  },
+  {
+    title: 'Vuetify Reddit',
+    icon: `mdi-reddit`,
+    href: 'https://reddit.com/r/vuetifyjs',
+  },
+]
+</script>
+
+<style scoped lang="sass">
+.social-link :deep(.v-icon)
+  color: rgba(var(--v-theme-on-background), var(--v-disabled-opacity))
+  text-decoration: none
+  transition: .2s ease-in-out
+
+  &:hover
+    color: rgba(25, 118, 210, 1)
+</style>
diff --git a/services/frontend/src/components/HelloWorld.vue b/services/frontend/src/components/HelloWorld.vue
new file mode 100644
index 0000000..0f1cc41
--- /dev/null
+++ b/services/frontend/src/components/HelloWorld.vue
@@ -0,0 +1,163 @@
+<template>
+  <v-container class="fill-height">
+    <v-responsive
+      class="align-centerfill-height mx-auto"
+      max-width="900"
+    >
+      <v-img
+        class="mb-4"
+        height="150"
+        src="@/assets/logo.png"
+      />
+
+      <div class="text-center">
+        <div class="text-body-2 font-weight-light mb-n1">
+          Welcome to
+        </div>
+
+        <h1 class="text-h2 font-weight-bold">
+          Vuetify
+        </h1>
+      </div>
+
+      <div class="py-4" />
+
+      <v-row>
+        <v-col cols="12">
+          <v-card
+            class="py-4"
+            color="surface-variant"
+            image="https://cdn.vuetifyjs.com/docs/images/one/create/feature.png"
+            prepend-icon="mdi-rocket-launch-outline"
+            rounded="lg"
+            variant="outlined"
+          >
+            <template #image>
+              <v-img position="top right" />
+            </template>
+
+            <template #title>
+              <h2 class="text-h5 font-weight-bold">
+                Get started
+              </h2>
+            </template>
+
+            <template #subtitle>
+              <div class="text-subtitle-1">
+                Replace this page by removing <v-kbd>{{`<HelloWorld></HelloWorld>`}}</v-kbd> in <v-kbd>pages/index.vue</v-kbd>.
+              </div>
+            </template>
+
+            <v-overlay
+              opacity=".12"
+              scrim="primary"
+              contained
+              model-value
+              persistent
+            />
+          </v-card>
+        </v-col>
+
+        <v-col cols="6">
+          <v-card
+            append-icon="mdi-open-in-new"
+            class="py-4"
+            color="surface-variant"
+            href="https://vuetifyjs.com/"
+            prepend-icon="mdi-text-box-outline"
+            rel="noopener noreferrer"
+            rounded="lg"
+            subtitle="Learn about all things Vuetify in our documentation."
+            target="_blank"
+            title="Documentation"
+            variant="text"
+          >
+            <v-overlay
+              opacity=".06"
+              scrim="primary"
+              contained
+              model-value
+              persistent
+            />
+          </v-card>
+        </v-col>
+
+        <v-col cols="6">
+          <v-card
+            append-icon="mdi-open-in-new"
+            class="py-4"
+            color="surface-variant"
+            href="https://vuetifyjs.com/introduction/why-vuetify/#feature-guides"
+            prepend-icon="mdi-star-circle-outline"
+            rel="noopener noreferrer"
+            rounded="lg"
+            subtitle="Explore available framework Features."
+            target="_blank"
+            title="Features"
+            variant="text"
+          >
+            <v-overlay
+              opacity=".06"
+              scrim="primary"
+              contained
+              model-value
+              persistent
+            />
+          </v-card>
+        </v-col>
+
+        <v-col cols="6">
+          <v-card
+            append-icon="mdi-open-in-new"
+            class="py-4"
+            color="surface-variant"
+            href="https://vuetifyjs.com/components/all"
+            prepend-icon="mdi-widgets-outline"
+            rel="noopener noreferrer"
+            rounded="lg"
+            subtitle="Discover components in the API Explorer."
+            target="_blank"
+            title="Components"
+            variant="text"
+          >
+            <v-overlay
+              opacity=".06"
+              scrim="primary"
+              contained
+              model-value
+              persistent
+            />
+          </v-card>
+        </v-col>
+
+        <v-col cols="6">
+          <v-card
+            append-icon="mdi-open-in-new"
+            class="py-4"
+            color="surface-variant"
+            href="https://discord.vuetifyjs.com"
+            prepend-icon="mdi-account-group-outline"
+            rel="noopener noreferrer"
+            rounded="lg"
+            subtitle="Connect with Vuetify developers."
+            target="_blank"
+            title="Community"
+            variant="text"
+          >
+            <v-overlay
+              opacity=".06"
+              scrim="primary"
+              contained
+              model-value
+              persistent
+            />
+          </v-card>
+        </v-col>
+      </v-row>
+    </v-responsive>
+  </v-container>
+</template>
+
+<script setup>
+//
+</script>
diff --git a/services/frontend/src/components/README.md b/services/frontend/src/components/README.md
new file mode 100644
index 0000000..84e56cf
--- /dev/null
+++ b/services/frontend/src/components/README.md
@@ -0,0 +1,35 @@
+# Components
+
+Vue template files in this folder are automatically imported.
+
+## 🚀 Usage
+
+Importing is handled by [unplugin-vue-components](https://github.com/unplugin/unplugin-vue-components). This plugin automatically imports `.vue` files created in the `src/components` directory, and registers them as global components. This means that you can use any component in your application without having to manually import it.
+
+The following example assumes a component located at `src/components/MyComponent.vue`:
+
+```vue
+<template>
+  <div>
+    <MyComponent />
+  </div>
+</template>
+
+<script lang="ts" setup>
+  //
+</script>
+```
+
+When your template is rendered, the component's import will automatically be inlined, which renders to this:
+
+```vue
+<template>
+  <div>
+    <MyComponent />
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import HelloWorld from 'HelloWorld.vue'
+</script>
+```
diff --git a/services/frontend/src/layouts/README.md b/services/frontend/src/layouts/README.md
new file mode 100644
index 0000000..4016af3
--- /dev/null
+++ b/services/frontend/src/layouts/README.md
@@ -0,0 +1,5 @@
+# Layouts
+
+Layouts are reusable components that wrap around pages. They are used to provide a consistent look and feel across multiple pages.
+
+Full documentation for this feature can be found in the Official [vite-plugin-vue-layouts](https://github.com/JohnCampionJr/vite-plugin-vue-layouts) repository.
diff --git a/services/frontend/src/layouts/default.vue b/services/frontend/src/layouts/default.vue
new file mode 100644
index 0000000..055c1ff
--- /dev/null
+++ b/services/frontend/src/layouts/default.vue
@@ -0,0 +1,12 @@
+<template>
+  <v-main>
+    <router-view />
+  </v-main>
+
+  <AppFooter />
+</template>
+
+<script setup>
+  //
+import AppFooter from "@/components/AppFooter.vue";
+</script>
diff --git a/services/frontend/src/main.js b/services/frontend/src/main.js
index e333042..a2ef932 100644
--- a/services/frontend/src/main.js
+++ b/services/frontend/src/main.js
@@ -1,14 +1,20 @@
-import 'bootstrap/dist/css/bootstrap.css';
-import { createApp } from "vue";
-import axios from 'axios';
+/**
+ * main.js
+ *
+ * Bootstraps Vuetify and other plugins then mounts the App`
+ */
 
-import App from './App.vue';
-import router from './router';
+// Plugins
+import { registerPlugins } from '@/plugins'
 
-const app = createApp(App);
+// Components
+import App from './App.vue'
 
-axios.defaults.withCredentials = true;
-axios.defaults.baseURL = 'http://localhost:8000/';  // the FastAPI backend
+// Composable
+import { createApp } from 'vue'
 
-app.use(router);
-app.mount("#app");
+const app = createApp(App)
+
+registerPlugins(app)
+
+app.mount('#app')
diff --git a/services/frontend/src/pages/README.md b/services/frontend/src/pages/README.md
new file mode 100644
index 0000000..341536c
--- /dev/null
+++ b/services/frontend/src/pages/README.md
@@ -0,0 +1,5 @@
+# Pages
+
+Vue components created in this folder will automatically be converted to navigatable routes.
+
+Full documentation for this feature can be found in the Official [unplugin-vue-router](https://github.com/posva/unplugin-vue-router) repository.
diff --git a/services/frontend/src/pages/index.vue b/services/frontend/src/pages/index.vue
new file mode 100644
index 0000000..291b3db
--- /dev/null
+++ b/services/frontend/src/pages/index.vue
@@ -0,0 +1,18 @@
+<template>
+  <v-layout class="rounded rounded-md">
+    <v-app-bar title="Application bar" />
+
+    <v-navigation-drawer>
+      <v-list>
+        <v-list-item title="Navigation drawer" />
+      </v-list>
+    </v-navigation-drawer>
+
+    <v-main
+      class="d-flex align-center justify-center"
+      style="min-height: 300px;"
+    >
+      Main Content
+    </v-main>
+  </v-layout>
+</template>
\ No newline at end of file
diff --git a/services/frontend/src/plugins/README.md b/services/frontend/src/plugins/README.md
new file mode 100644
index 0000000..62201c7
--- /dev/null
+++ b/services/frontend/src/plugins/README.md
@@ -0,0 +1,3 @@
+# Plugins
+
+Plugins are a way to extend the functionality of your Vue application. Use this folder for registering plugins that you want to use globally.
diff --git a/services/frontend/src/plugins/index.js b/services/frontend/src/plugins/index.js
new file mode 100644
index 0000000..be6e2da
--- /dev/null
+++ b/services/frontend/src/plugins/index.js
@@ -0,0 +1,17 @@
+/**
+ * plugins/index.js
+ *
+ * Automatically included in `./src/main.js`
+ */
+
+// Plugins
+import vuetify from './vuetify'
+import pinia from '@/stores'
+import router from '@/router'
+
+export function registerPlugins (app) {
+  app
+    .use(vuetify)
+    .use(router)
+    .use(pinia)
+}
diff --git a/services/frontend/src/plugins/vuetify.js b/services/frontend/src/plugins/vuetify.js
new file mode 100644
index 0000000..247a149
--- /dev/null
+++ b/services/frontend/src/plugins/vuetify.js
@@ -0,0 +1,19 @@
+/**
+ * plugins/vuetify.js
+ *
+ * Framework documentation: https://vuetifyjs.com`
+ */
+
+// Styles
+import '@mdi/font/css/materialdesignicons.css'
+import 'vuetify/styles'
+
+// Composables
+import { createVuetify } from 'vuetify'
+
+// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
+export default createVuetify({
+  theme: {
+    defaultTheme: 'dark',
+  },
+})
diff --git a/services/frontend/src/router/index.js b/services/frontend/src/router/index.js
index 90e1b84..a65e6b9 100644
--- a/services/frontend/src/router/index.js
+++ b/services/frontend/src/router/index.js
@@ -1,31 +1,36 @@
-import { createRouter, createWebHistory } from 'vue-router'
-import HomeView from '../views/HomeView.vue'
-import RegisterView from '../views/RegisterView.vue'
+/**
+ * router/index.ts
+ *
+ * Automatic routes for `./src/pages/*.vue`
+ */
 
-const routes = [
-  {
-    path: '/',
-    name: 'home',
-    component: HomeView
-  },
-  {
-    path: '/register',
-    name: 'register',
-    component: RegisterView
-  },
-  {
-    path: '/about',
-    name: 'about',
-    // route level code-splitting
-    // this generates a separate chunk (about.[hash].js) for this route
-    // which is lazy-loaded when the route is visited.
-    component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
-  }
-]
+// Composable
+import { createRouter, createWebHistory } from 'vue-router/auto'
+import { setupLayouts } from 'virtual:generated-layouts'
+import { routes } from 'vue-router/auto-routes'
 
 const router = createRouter({
-  history: createWebHistory(process.env.BASE_URL),
-  routes
+  history: createWebHistory(import.meta.env.BASE_URL),
+  routes: setupLayouts(routes),
+})
+
+// Workaround for https://github.com/vitejs/vite/issues/11804
+router.onError((err, to) => {
+  if (err?.message?.includes?.('Failed to fetch dynamically imported module')) {
+    if (!localStorage.getItem('vuetify:dynamic-reload')) {
+      console.log('Reloading page to fix dynamic import error')
+      localStorage.setItem('vuetify:dynamic-reload', 'true')
+      location.assign(to.fullPath)
+    } else {
+      console.error('Dynamic import error, reloading page did not fix it', err)
+    }
+  } else {
+    console.error(err)
+  }
+})
+
+router.isReady().then(() => {
+  localStorage.removeItem('vuetify:dynamic-reload')
 })
 
 export default router
diff --git a/services/frontend/src/stores/README.md b/services/frontend/src/stores/README.md
new file mode 100644
index 0000000..54f8e03
--- /dev/null
+++ b/services/frontend/src/stores/README.md
@@ -0,0 +1,5 @@
+# Store
+
+Pinia stores are used to store reactive state and expose actions to mutate it.
+
+Full documentation for this feature can be found in the Official [Pinia](https://pinia.esm.dev/) repository.
diff --git a/services/frontend/src/stores/app.js b/services/frontend/src/stores/app.js
new file mode 100644
index 0000000..7429543
--- /dev/null
+++ b/services/frontend/src/stores/app.js
@@ -0,0 +1,8 @@
+// Utilities
+import { defineStore } from 'pinia'
+
+export const useAppStore = defineStore('app', {
+  state: () => ({
+    //
+  }),
+})
diff --git a/services/frontend/src/stores/index.js b/services/frontend/src/stores/index.js
new file mode 100644
index 0000000..1536252
--- /dev/null
+++ b/services/frontend/src/stores/index.js
@@ -0,0 +1,4 @@
+// Utilities
+import { createPinia } from 'pinia'
+
+export default createPinia()
diff --git a/services/frontend/src/styles/README.md b/services/frontend/src/styles/README.md
new file mode 100644
index 0000000..ea86179
--- /dev/null
+++ b/services/frontend/src/styles/README.md
@@ -0,0 +1,3 @@
+# Styles
+
+This directory is for configuring the styles of the application.
diff --git a/services/frontend/src/styles/settings.scss b/services/frontend/src/styles/settings.scss
new file mode 100644
index 0000000..3e36a27
--- /dev/null
+++ b/services/frontend/src/styles/settings.scss
@@ -0,0 +1,10 @@
+/**
+ * src/styles/settings.scss
+ *
+ * Configures SASS variables and Vuetify overwrites
+ */
+
+// https://vuetifyjs.com/features/sass-variables/`
+// @use 'vuetify/settings' with (
+//   $color-pack: false
+// );
diff --git a/services/frontend/vite.config.mjs b/services/frontend/vite.config.mjs
new file mode 100644
index 0000000..73375db
--- /dev/null
+++ b/services/frontend/vite.config.mjs
@@ -0,0 +1,74 @@
+// Plugins
+import AutoImport from 'unplugin-auto-import/vite'
+import Components from 'unplugin-vue-components/vite'
+import Fonts from 'unplugin-fonts/vite'
+import Layouts from 'vite-plugin-vue-layouts'
+import Vue from '@vitejs/plugin-vue'
+import VueRouter from 'unplugin-vue-router/vite'
+import Vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'
+
+// Utilities
+import { defineConfig } from 'vite'
+import { fileURLToPath, URL } from 'node:url'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [
+    VueRouter(),
+    Layouts(),
+    Vue({
+      template: { transformAssetUrls }
+    }),
+    // https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme
+    Vuetify({
+      autoImport: true,
+      styles: {
+        configFile: 'src/styles/settings.scss',
+      },
+    }),
+    Components(),
+    Fonts({
+      google: {
+        families: [{
+          name: 'Roboto',
+          styles: 'wght@100;300;400;500;700;900',
+        }],
+      },
+    }),
+    AutoImport({
+      imports: [
+        'vue',
+        'vue-router',
+      ],
+      eslintrc: {
+        enabled: true,
+      },
+      vueTemplate: true,
+    }),
+  ],
+  define: { 'process.env': {} },
+  resolve: {
+    alias: {
+      '@': fileURLToPath(new URL('./src', import.meta.url))
+    },
+    extensions: [
+      '.js',
+      '.json',
+      '.jsx',
+      '.mjs',
+      '.ts',
+      '.tsx',
+      '.vue',
+    ],
+  },
+  server: {
+    port: 3000,
+  },
+  css: {
+    preprocessorOptions: {
+      sass: {
+        api: 'modern-compiler',
+      },
+    },
+  },
+})
diff --git a/services/frontend/vue.config.js b/services/frontend/vue.config.js
deleted file mode 100644
index 910e297..0000000
--- a/services/frontend/vue.config.js
+++ /dev/null
@@ -1,4 +0,0 @@
-const { defineConfig } = require('@vue/cli-service')
-module.exports = defineConfig({
-  transpileDependencies: true
-})
-- 
GitLab