checkpoint

This commit is contained in:
2025-12-13 20:23:29 +01:00
parent 879db7e23b
commit 0d56d26afa
15 changed files with 250 additions and 35 deletions

View File

@@ -5,11 +5,13 @@ import { RouterLink, RouterView } from 'vue-router'
<template> <template>
<div class="flex flex-col h-screen"> <div class="flex flex-col h-screen">
<header> <header>
<div class="navbar bg-base-100 shadow-sm flex justify-center"> <div class="navbar bg-base-200 shadow-sm flex justify-center">
<RouterLink to="/" class="btn btn-ghost text-xl font-mono">teilweise</RouterLink> <RouterLink to="/" class="btn btn-ghost text-xl font-mono">teilweise</RouterLink>
</div> </div>
</header> </header>
<RouterView /> <main class="h-full">
<RouterView />
</main>
</div> </div>
</template> </template>

View File

@@ -2,7 +2,6 @@
@plugin "daisyui" { @plugin "daisyui" {
themes: themes:
bumblebee --default, bumblebee --default,
abyss --prefersdark;
root: '#app'; root: '#app';
} }

View File

@@ -2,18 +2,36 @@
import IconUser from '@/components/icons/IconUser.vue' import IconUser from '@/components/icons/IconUser.vue'
import IconInfo from '@/components/icons/IconInfo.vue' import IconInfo from '@/components/icons/IconInfo.vue'
import IconStats from '@/components/icons/IconStats.vue' import IconStats from '@/components/icons/IconStats.vue'
import { useRoute } from 'vue-router'
import { computed } from 'vue'
const route = useRoute()
const InfoPath = computed(() => '/boards/' + route.params.boardId)
const UsersPath = computed(() => '/boards/' + route.params.boardId + '/users')
const SpendingsPath = computed(() => '/boards/' + route.params.boardId + '/spendings')
</script> </script>
<template> <template>
<ul class="menu menu-horizontal bg-base-200 rounded-box"> <ul class="menu menu-horizontal bg-base-200 rounded-box">
<li> <li>
<RouterLink to="/liste" class="menu-active"><IconUser /></RouterLink> <RouterLink
:to="InfoPath"
:class="[{ 'menu-active': route.path == InfoPath || route.path == InfoPath + '/' }]"
><IconInfo
/></RouterLink>
</li> </li>
<li> <li>
<RouterLink to="/"><IconStats /></RouterLink> <RouterLink :to="UsersPath" :class="[{ 'menu-active': route.path.startsWith(UsersPath) }]"
><IconUser
/></RouterLink>
</li> </li>
<li> <li>
<RouterLink to="/"><IconInfo /></RouterLink> <RouterLink
:to="SpendingsPath"
:class="[{ 'menu-active': route.path.startsWith(SpendingsPath) }]"
><IconStats
/></RouterLink>
</li> </li>
</ul> </ul>
</template> </template>

View File

@@ -5,7 +5,7 @@ const props = defineProps({
</script> </script>
<template> <template>
<ul class="list w-fit min-w-80 bg-base-100 rounded-box shadow-md"> <ul class="list w-fit min-w-80 bg-base-100 border border-base-200 rounded-box shadow-md">
<li class="p-4 pb-2 text-xs opacity-60 tracking-wide">{{ props.title }}</li> <li class="p-4 pb-2 text-xs opacity-60 tracking-wide">{{ props.title }}</li>
<slot></slot> <slot></slot>
</ul> </ul>

View File

@@ -1,30 +1,28 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import IconPlus from './icons/IconPlusCircle.vue'
const props = defineProps({ const props = defineProps({
name: String, icon: String,
title: String,
subtitle: String, subtitle: String,
subtitleClass: String, subtitleClass: String,
}) })
const pic_url = computed(() => { const pic_url = computed(() => {
return 'https://ui-avatars.com/api/?name=' + props.name + '&size=256' return 'https://ui-avatars.com/api/?name=' + props.icon + '&size=256'
}) })
</script> </script>
<template> <template>
<li class="list-row"> <li class="list-row">
<div> <div v-if="icon">
<img class="size-10 rounded-box" :src="pic_url" /> <img class="size-10 rounded-box" :src="pic_url" />
</div> </div>
<div> <div>
<div>{{ props.name }}</div> <div>{{ props.title }}</div>
<div :class="['text-xs uppercase font-semibold font-mono opacity-60', subtitleClass]"> <div :class="['text-xs font-semibold font-mono opacity-60', subtitleClass]">
{{ props.subtitle }} {{ props.subtitle }}
</div> </div>
</div> </div>
<button class="btn btn-square btn-ghost"> <slot></slot>
<IconPlus />
</button>
</li> </li>
</template> </template>

View File

@@ -1,6 +1,8 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue' import HomeView from '../views/HomeView.vue'
import BoardUsersView from '@/views/BoardUsersView.vue' import BoardUsersView from '@/views/BoardUsersView.vue'
import BoardInfoView from '@/views/BoardInfoView.vue'
import BoardSpendingsView from '@/views/BoardSpendingsView.vue'
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
@@ -11,8 +13,8 @@ const router = createRouter({
component: HomeView, component: HomeView,
}, },
{ {
path: '/liste', path: '/boards/:boardId',
name: 'liste', name: 'boards',
// route level code-splitting // route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route // this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited. // which is lazy-loaded when the route is visited.
@@ -20,9 +22,19 @@ const router = createRouter({
children: [ children: [
{ {
path: '', path: '',
name: 'BoardInfo',
component: BoardInfoView,
},
{
path: 'users',
name: 'BoardUsers', name: 'BoardUsers',
component: BoardUsersView, component: BoardUsersView,
}, },
{
path: 'spendings',
name: 'BoardEntries',
component: BoardSpendingsView,
},
], ],
}, },
], ],

View File

@@ -0,0 +1,20 @@
import { User } from './User'
import { v4 as uuidv4 } from 'uuid'
export class Board {
name: string
users: Array<User>
creationDate: Date
guid: string
constructor(name: string) {
this.name = name
this.users = Array<User>()
this.creationDate = new Date()
this.guid = uuidv4()
}
addUser(user: User): void {
this.users.push(user)
}
}

View File

@@ -0,0 +1,18 @@
import type { User } from './User'
export class Spending {
name: string
amountCt: number
creationDate: Date
spender: User | undefined
constructor(name: string, amountCt: number) {
this.name = name
this.amountCt = amountCt
this.creationDate = new Date()
}
setSpender(user: User) {
this.spender = user
}
}

View File

@@ -0,0 +1,43 @@
import type { Spending } from './Spending'
export class User {
name: string
spendings: Array<Spending>
constructor(name: string) {
this.name = name
this.spendings = Array<Spending>()
}
addSpending(spending: Spending): void {
spending.setSpender(this)
this.spendings.push(spending)
}
getTotalSpending(): number {
return this.spendings.map((w) => w.amountCt).reduce((acc, num) => acc + num, 0)
}
getInitials(): string {
if (!this.name) return ''
const words = this.name
.trim()
.split(/\s+/)
.filter((word) => word.length > 0)
if (words.length === 0) return ''
if (words.length === 1) {
const firstWord = words[0]
if (!firstWord || firstWord.length === 0) return ''
return firstWord.substring(0, 2).toUpperCase()
}
const firstWord = words[0]
const secondWord = words[1]
if (!firstWord || !secondWord || firstWord.length === 0 || secondWord.length === 0) return ''
return (firstWord[0]! + secondWord[0]!).toUpperCase()
}
}

View File

@@ -0,0 +1,44 @@
import { defineStore } from 'pinia'
import { useRoute } from 'vue-router'
import { Board } from '@/services/Board'
import { User } from '@/services/User'
import { Spending } from '@/services/Spending'
export const useBoardStore = defineStore('boardStore', () => {
const route = useRoute()
const boards = new Map<string, Board>()
function createBoard(name: string): Board {
const newBoard = new Board(name)
boards.set(newBoard.guid, newBoard)
return newBoard
}
function getBoard(guid: string): Board {
const board = boards.get(guid)
if (board != undefined) {
return board
} else {
const testBoard = new Board('Grill and Chill')
const elias = new User('Elias')
elias.addSpending(new Spending('Burger', 1230))
elias.addSpending(new Spending('Kaffee', 510))
testBoard.addUser(elias)
const max = new User('Max')
max.addSpending(new Spending('Omlett', 1822))
max.addSpending(new Spending('Kaffee', 3073))
testBoard.addUser(max)
return testBoard
}
}
function getCurrentBoard(): Board {
if (typeof route.params.boardId === 'string') {
return getBoard(route.params.boardId)
} else {
return getBoard('')
}
}
return { createBoard, getBoard, getCurrentBoard }
})

View File

@@ -1,12 +0,0 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})

View File

@@ -0,0 +1,27 @@
<script setup lang="ts">
import ItemList from '@/components/ItemList.vue'
import ItemListElement from '@/components/ItemListElement.vue'
import { useBoardStore } from '@/stores/boardStore'
import { ref } from 'vue'
const store = useBoardStore()
const board = ref(store.getCurrentBoard())
</script>
<template>
<ItemList title="Informationen">
<ItemListElement title="Name" :subtitle="board.name" />
<ItemListElement title="Guid" :subtitle="board.guid" />
<ItemListElement
title="Erstellt am"
:subtitle="
board.creationDate.toLocaleDateString('de-DE', {
day: 'numeric',
month: '2-digit',
year: 'numeric',
})
"
/>
</ItemList>
</template>

View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
import ItemList from '@/components/ItemList.vue'
import ItemListElement from '@/components/ItemListElement.vue'
import { useBoardStore } from '@/stores/boardStore'
import { ref } from 'vue'
const store = useBoardStore()
const spendings = ref(store.getCurrentBoard().users.flatMap((u) => u.spendings))
</script>
<template>
<ItemList title="Ausgaben">
<ItemListElement
v-for="spending in spendings"
:key="spending.creationDate.getTime()"
:icon="spending.spender?.getInitials()"
:title="spending.name"
:subtitle="
(spending.amountCt / 100).toFixed(2) +
'€ - ' +
spending.creationDate.toLocaleDateString('de-DE', {
day: 'numeric',
month: '2-digit',
year: 'numeric',
})
"
/>
</ItemList>
</template>

View File

@@ -1,12 +1,28 @@
<script setup lang="ts"> <script setup lang="ts">
import ItemList from '@/components/ItemList.vue' import ItemList from '@/components/ItemList.vue'
import ItemListElement from '@/components/ItemListElement.vue' import ItemListElement from '@/components/ItemListElement.vue'
import IconPlus from '@/components/icons/IconPlusCircle.vue'
import { useBoardStore } from '@/stores/boardStore'
import { ref } from 'vue'
const store = useBoardStore()
const users = ref(store.getCurrentBoard().users)
</script> </script>
<template> <template>
<ItemList title="Mitglieder"> <ItemList title="Mitglieder">
<ItemListElement name="Elias" subtitle="7.07€" subtitle-class="text-green-700" /> <ItemListElement
<ItemListElement name="Max" subtitle="-16.27€" subtitle-class="text-red-700" /> v-for="user in users"
<ItemListElement name="Daniel" subtitle="9.20€" subtitle-class="text-green-700" /> :key="user.name"
:icon="user.getInitials()"
:title="user.name"
:subtitle="(user.getTotalSpending() / 100).toFixed(2) + '€'"
subtitle-class="text-green-700"
>
<button class="btn btn-square btn-ghost">
<IconPlus />
</button>
</ItemListElement>
</ItemList> </ItemList>
</template> </template>

View File

@@ -3,7 +3,7 @@ import CreateNewList from '../components/CreateNewList.vue'
</script> </script>
<template> <template>
<main class="grid h-full place-content-center"> <div class="grid h-full place-content-center">
<CreateNewList></CreateNewList> <CreateNewList></CreateNewList>
</main> </div>
</template> </template>