diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs
new file mode 100644
index 00000000..2ab20f7b
--- /dev/null
+++ b/client/.eslintrc.cjs
@@ -0,0 +1,25 @@
+/* eslint-env node */
+require("@rushstack/eslint-patch/modern-module-resolution");
+
+module.exports = {
+ "root": true,
+ "extends": [
+ "plugin:vue/vue3-essential",
+ "eslint:recommended",
+ "@vue/eslint-config-typescript/recommended",
+ "@vue/eslint-config-prettier"
+ ],
+ "env": {
+ "vue/setup-compiler-macros": true
+ },
+ "overrides": [
+ {
+ "files": [
+ "cypress/integration/**.spec.{js,ts,jsx,tsx}"
+ ],
+ "extends": [
+ "plugin:cypress/recommended"
+ ]
+ }
+ ]
+}
diff --git a/client/.vscode/extensions.json b/client/.vscode/extensions.json
new file mode 100644
index 00000000..806eacda
--- /dev/null
+++ b/client/.vscode/extensions.json
@@ -0,0 +1,3 @@
+{
+ "recommendations": ["johnsoncodehk.volar", "johnsoncodehk.vscode-typescript-vue-plugin"]
+}
diff --git a/client/cypress/fixtures/example.json b/client/cypress/fixtures/example.json
new file mode 100644
index 00000000..02e42543
--- /dev/null
+++ b/client/cypress/fixtures/example.json
@@ -0,0 +1,5 @@
+{
+ "name": "Using fixtures to represent data",
+ "email": "hello@cypress.io",
+ "body": "Fixtures are a great way to mock data for responses to routes"
+}
diff --git a/client/cypress/integration/example.spec.ts b/client/cypress/integration/example.spec.ts
new file mode 100644
index 00000000..7a8c909f
--- /dev/null
+++ b/client/cypress/integration/example.spec.ts
@@ -0,0 +1,8 @@
+// https://docs.cypress.io/api/introduction/api.html
+
+describe('My First Test', () => {
+ it('visits the app root url', () => {
+ cy.visit('/')
+ cy.contains('h1', 'You did it!')
+ })
+})
diff --git a/client/cypress/plugins/index.ts b/client/cypress/plugins/index.ts
new file mode 100644
index 00000000..c6679b40
--- /dev/null
+++ b/client/cypress/plugins/index.ts
@@ -0,0 +1,19 @@
+/* eslint-env node */
+// ***********************************************************
+// This example plugins/index.ts can be used to load plugins
+//
+// You can change the location of this file or turn off loading
+// the plugins file with the 'pluginsFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/plugins-guide
+// ***********************************************************
+
+// This function is called when a project is opened or re-opened (e.g. due to
+// the project's config changing)
+
+export default ((on, config) => {
+ // `on` is used to hook into various events Cypress emits
+ // `config` is the resolved Cypress config
+ return config
+}) as Cypress.PluginConfig
diff --git a/client/cypress/plugins/tsconfig.json b/client/cypress/plugins/tsconfig.json
new file mode 100644
index 00000000..b5aedd53
--- /dev/null
+++ b/client/cypress/plugins/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "@vue/tsconfig/tsconfig.node.json",
+ "include": ["./**/*"],
+ "compilerOptions": {
+ "module": "CommonJS",
+ "preserveValueImports": false,
+ "types": ["node", "cypress/types/cypress"]
+ }
+}
diff --git a/client/cypress/support/commands.ts b/client/cypress/support/commands.ts
new file mode 100644
index 00000000..119ab03f
--- /dev/null
+++ b/client/cypress/support/commands.ts
@@ -0,0 +1,25 @@
+// ***********************************************
+// This example commands.js shows you how to
+// create various custom commands and overwrite
+// existing commands.
+//
+// For more comprehensive examples of custom
+// commands please read more here:
+// https://on.cypress.io/custom-commands
+// ***********************************************
+//
+//
+// -- This is a parent command --
+// Cypress.Commands.add('login', (email, password) => { ... })
+//
+//
+// -- This is a child command --
+// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
+//
+//
+// -- This is a dual command --
+// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
+//
+//
+// -- This will overwrite an existing command --
+// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
diff --git a/client/cypress/support/index.ts b/client/cypress/support/index.ts
new file mode 100644
index 00000000..d68db96d
--- /dev/null
+++ b/client/cypress/support/index.ts
@@ -0,0 +1,20 @@
+// ***********************************************************
+// This example support/index.js is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// Import commands.js using ES2015 syntax:
+import './commands'
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')
diff --git a/client/cypress/tsconfig.json b/client/cypress/tsconfig.json
new file mode 100644
index 00000000..a2dbf9e3
--- /dev/null
+++ b/client/cypress/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "@vue/tsconfig/tsconfig.web.json",
+ "include": ["./integration/**/*", "./support/**/*"],
+ "compilerOptions": {
+ "isolatedModules": false,
+ "target": "es5",
+ "lib": ["es5", "dom"],
+ "types": ["cypress"]
+ }
+}
diff --git a/client/env.d.ts b/client/env.d.ts
new file mode 100644
index 00000000..11f02fe2
--- /dev/null
+++ b/client/env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/client/index.html b/client/index.html
new file mode 100644
index 00000000..11603f87
--- /dev/null
+++ b/client/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Vite App
+
+
+
+
+
+
diff --git a/client/src/assets/base.css b/client/src/assets/base.css
new file mode 100644
index 00000000..71dc55a3
--- /dev/null
+++ b/client/src/assets/base.css
@@ -0,0 +1,74 @@
+/* color palette from */
+:root {
+ --vt-c-white: #ffffff;
+ --vt-c-white-soft: #f8f8f8;
+ --vt-c-white-mute: #f2f2f2;
+
+ --vt-c-black: #181818;
+ --vt-c-black-soft: #222222;
+ --vt-c-black-mute: #282828;
+
+ --vt-c-indigo: #2c3e50;
+
+ --vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
+ --vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
+ --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
+ --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
+
+ --vt-c-text-light-1: var(--vt-c-indigo);
+ --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
+ --vt-c-text-dark-1: var(--vt-c-white);
+ --vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
+}
+
+/* semantic color variables for this project */
+:root {
+ --color-background: var(--vt-c-white);
+ --color-background-soft: var(--vt-c-white-soft);
+ --color-background-mute: var(--vt-c-white-mute);
+
+ --color-border: var(--vt-c-divider-light-2);
+ --color-border-hover: var(--vt-c-divider-light-1);
+
+ --color-heading: var(--vt-c-text-light-1);
+ --color-text: var(--vt-c-text-light-1);
+
+ --section-gap: 160px;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --color-background: var(--vt-c-black);
+ --color-background-soft: var(--vt-c-black-soft);
+ --color-background-mute: var(--vt-c-black-mute);
+
+ --color-border: var(--vt-c-divider-dark-2);
+ --color-border-hover: var(--vt-c-divider-dark-1);
+
+ --color-heading: var(--vt-c-text-dark-1);
+ --color-text: var(--vt-c-text-dark-2);
+ }
+}
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+ margin: 0;
+ position: relative;
+ font-weight: normal;
+}
+
+body {
+ min-height: 100vh;
+ color: var(--color-text);
+ background: var(--color-background);
+ transition: color 0.5s, background-color 0.5s;
+ line-height: 1.6;
+ font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
+ Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
+ font-size: 15px;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
diff --git a/client/src/assets/logo.svg b/client/src/assets/logo.svg
new file mode 100644
index 00000000..bc826fed
--- /dev/null
+++ b/client/src/assets/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/client/src/components/TheWelcome.vue b/client/src/components/TheWelcome.vue
new file mode 100644
index 00000000..ba5cb527
--- /dev/null
+++ b/client/src/components/TheWelcome.vue
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+ Documentation
+
+ Vue’s
+ official documentation
+ provides you with all information you need to get started.
+
+
+
+
+
+
+ Tooling
+
+ This project is served and bundled with
+ Vite. The recommended IDE
+ setup is VSCode +
+ Volar. If you need to test
+ your components and web pages, check out
+ Cypress and
+ Cypress Component Testing.
+
+
+
+ More instructions are available in README.md.
+
+
+
+
+
+
+ Ecosystem
+
+ Get official tools and libraries for your project:
+ Pinia,
+ Vue Router,
+ Vue Test Utils, and
+ Vue Dev Tools. If you need more
+ resources, we suggest paying
+ Awesome Vue
+ a visit.
+
+
+
+
+
+
+ Community
+
+ Got stuck? Ask your question on
+ Vue Land, our official Discord server, or
+ StackOverflow.
+ You should also subscribe to
+ our mailing list and follow the official
+ @vuejs
+ twitter account for latest news in the Vue world.
+
+
+
+
+
+
+ Support Vue
+
+ As an independent project, Vue relies on community backing for its sustainability. You can help
+ us by
+ becoming a sponsor.
+
+
diff --git a/client/src/components/WelcomeItem.vue b/client/src/components/WelcomeItem.vue
new file mode 100644
index 00000000..ba0def33
--- /dev/null
+++ b/client/src/components/WelcomeItem.vue
@@ -0,0 +1,86 @@
+
+
+
+
+
diff --git a/client/src/components/__tests__/HelloWorld.spec.ts b/client/src/components/__tests__/HelloWorld.spec.ts
new file mode 100644
index 00000000..25332020
--- /dev/null
+++ b/client/src/components/__tests__/HelloWorld.spec.ts
@@ -0,0 +1,11 @@
+import { describe, it, expect } from 'vitest'
+
+import { mount } from '@vue/test-utils'
+import HelloWorld from '../HelloWorld.vue'
+
+describe('HelloWorld', () => {
+ it('renders properly', () => {
+ const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } })
+ expect(wrapper.text()).toContain('Hello Vitest')
+ })
+})
diff --git a/client/src/components/icons/IconCommunity.vue b/client/src/components/icons/IconCommunity.vue
new file mode 100644
index 00000000..2dc8b055
--- /dev/null
+++ b/client/src/components/icons/IconCommunity.vue
@@ -0,0 +1,7 @@
+
+
+
diff --git a/client/src/components/icons/IconDocumentation.vue b/client/src/components/icons/IconDocumentation.vue
new file mode 100644
index 00000000..6d4791cf
--- /dev/null
+++ b/client/src/components/icons/IconDocumentation.vue
@@ -0,0 +1,7 @@
+
+
+
diff --git a/client/src/components/icons/IconEcosystem.vue b/client/src/components/icons/IconEcosystem.vue
new file mode 100644
index 00000000..c3a4f078
--- /dev/null
+++ b/client/src/components/icons/IconEcosystem.vue
@@ -0,0 +1,7 @@
+
+
+
diff --git a/client/src/components/icons/IconSupport.vue b/client/src/components/icons/IconSupport.vue
new file mode 100644
index 00000000..7452834d
--- /dev/null
+++ b/client/src/components/icons/IconSupport.vue
@@ -0,0 +1,7 @@
+
+
+
diff --git a/client/src/components/icons/IconTooling.vue b/client/src/components/icons/IconTooling.vue
new file mode 100644
index 00000000..660598d7
--- /dev/null
+++ b/client/src/components/icons/IconTooling.vue
@@ -0,0 +1,19 @@
+
+
+
+
diff --git a/client/src/stores/counter.ts b/client/src/stores/counter.ts
new file mode 100644
index 00000000..4a2d2427
--- /dev/null
+++ b/client/src/stores/counter.ts
@@ -0,0 +1,16 @@
+import { defineStore } from 'pinia'
+
+export const useCounterStore = defineStore({
+ id: 'counter',
+ state: () => ({
+ counter: 0
+ }),
+ getters: {
+ doubleCount: (state) => state.counter * 2
+ },
+ actions: {
+ increment() {
+ this.counter++
+ }
+ }
+})
diff --git a/client/src/views/AboutView.vue b/client/src/views/AboutView.vue
new file mode 100644
index 00000000..756ad2a1
--- /dev/null
+++ b/client/src/views/AboutView.vue
@@ -0,0 +1,15 @@
+
+
+
This is an about page
+
+
+
+
diff --git a/client/src/views/HomeView.vue b/client/src/views/HomeView.vue
new file mode 100644
index 00000000..6555a646
--- /dev/null
+++ b/client/src/views/HomeView.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/client/tsconfig.app.json b/client/tsconfig.app.json
new file mode 100644
index 00000000..cdbea1d7
--- /dev/null
+++ b/client/tsconfig.app.json
@@ -0,0 +1,12 @@
+{
+ "extends": "@vue/tsconfig/tsconfig.web.json",
+ "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
+ "exclude": ["src/**/__tests__/*"],
+ "compilerOptions": {
+ "composite": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ }
+}
diff --git a/client/tsconfig.vite-config.json b/client/tsconfig.vite-config.json
new file mode 100644
index 00000000..d20d8726
--- /dev/null
+++ b/client/tsconfig.vite-config.json
@@ -0,0 +1,8 @@
+{
+ "extends": "@vue/tsconfig/tsconfig.node.json",
+ "include": ["vite.config.*"],
+ "compilerOptions": {
+ "composite": true,
+ "types": ["node", "vitest"]
+ }
+}
diff --git a/client/tsconfig.vitest.json b/client/tsconfig.vitest.json
new file mode 100644
index 00000000..d080d611
--- /dev/null
+++ b/client/tsconfig.vitest.json
@@ -0,0 +1,9 @@
+{
+ "extends": "./tsconfig.app.json",
+ "exclude": [],
+ "compilerOptions": {
+ "composite": true,
+ "lib": [],
+ "types": ["node", "jsdom"]
+ }
+}
diff --git a/client/vite.config.ts b/client/vite.config.ts
new file mode 100644
index 00000000..116273fb
--- /dev/null
+++ b/client/vite.config.ts
@@ -0,0 +1,14 @@
+import { fileURLToPath, URL } from 'url'
+
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [vue()],
+ resolve: {
+ alias: {
+ '@': fileURLToPath(new URL('./src', import.meta.url))
+ }
+ }
+})