checkpoint
This commit is contained in:
@@ -5,11 +5,13 @@ import { RouterLink, RouterView } from 'vue-router'
|
||||
<template>
|
||||
<div class="flex flex-col h-screen">
|
||||
<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>
|
||||
</div>
|
||||
</header>
|
||||
<main class="h-full">
|
||||
<RouterView />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
@plugin "daisyui" {
|
||||
themes:
|
||||
bumblebee --default,
|
||||
abyss --prefersdark;
|
||||
root: '#app';
|
||||
}
|
||||
|
||||
|
||||
@@ -2,18 +2,36 @@
|
||||
import IconUser from '@/components/icons/IconUser.vue'
|
||||
import IconInfo from '@/components/icons/IconInfo.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>
|
||||
|
||||
<template>
|
||||
<ul class="menu menu-horizontal bg-base-200 rounded-box">
|
||||
<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>
|
||||
<RouterLink to="/"><IconStats /></RouterLink>
|
||||
<RouterLink :to="UsersPath" :class="[{ 'menu-active': route.path.startsWith(UsersPath) }]"
|
||||
><IconUser
|
||||
/></RouterLink>
|
||||
</li>
|
||||
<li>
|
||||
<RouterLink to="/"><IconInfo /></RouterLink>
|
||||
<RouterLink
|
||||
:to="SpendingsPath"
|
||||
:class="[{ 'menu-active': route.path.startsWith(SpendingsPath) }]"
|
||||
><IconStats
|
||||
/></RouterLink>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
@@ -5,7 +5,7 @@ const props = defineProps({
|
||||
</script>
|
||||
|
||||
<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>
|
||||
<slot></slot>
|
||||
</ul>
|
||||
|
||||
@@ -1,30 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import IconPlus from './icons/IconPlusCircle.vue'
|
||||
|
||||
const props = defineProps({
|
||||
name: String,
|
||||
icon: String,
|
||||
title: String,
|
||||
subtitle: String,
|
||||
subtitleClass: String,
|
||||
})
|
||||
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>
|
||||
|
||||
<template>
|
||||
<li class="list-row">
|
||||
<div>
|
||||
<div v-if="icon">
|
||||
<img class="size-10 rounded-box" :src="pic_url" />
|
||||
</div>
|
||||
<div>
|
||||
<div>{{ props.name }}</div>
|
||||
<div :class="['text-xs uppercase font-semibold font-mono opacity-60', subtitleClass]">
|
||||
<div>{{ props.title }}</div>
|
||||
<div :class="['text-xs font-semibold font-mono opacity-60', subtitleClass]">
|
||||
{{ props.subtitle }}
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-square btn-ghost">
|
||||
<IconPlus />
|
||||
</button>
|
||||
<slot></slot>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
import BoardUsersView from '@/views/BoardUsersView.vue'
|
||||
import BoardInfoView from '@/views/BoardInfoView.vue'
|
||||
import BoardSpendingsView from '@/views/BoardSpendingsView.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
@@ -11,8 +13,8 @@ const router = createRouter({
|
||||
component: HomeView,
|
||||
},
|
||||
{
|
||||
path: '/liste',
|
||||
name: 'liste',
|
||||
path: '/boards/:boardId',
|
||||
name: 'boards',
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (About.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
@@ -20,9 +22,19 @@ const router = createRouter({
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'BoardInfo',
|
||||
component: BoardInfoView,
|
||||
},
|
||||
{
|
||||
path: 'users',
|
||||
name: 'BoardUsers',
|
||||
component: BoardUsersView,
|
||||
},
|
||||
{
|
||||
path: 'spendings',
|
||||
name: 'BoardEntries',
|
||||
component: BoardSpendingsView,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
20
frontend/src/services/Board.ts
Normal file
20
frontend/src/services/Board.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
18
frontend/src/services/Spending.ts
Normal file
18
frontend/src/services/Spending.ts
Normal 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
|
||||
}
|
||||
}
|
||||
43
frontend/src/services/User.ts
Normal file
43
frontend/src/services/User.ts
Normal 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()
|
||||
}
|
||||
}
|
||||
44
frontend/src/stores/boardStore.ts
Normal file
44
frontend/src/stores/boardStore.ts
Normal 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 }
|
||||
})
|
||||
@@ -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 }
|
||||
})
|
||||
27
frontend/src/views/BoardInfoView.vue
Normal file
27
frontend/src/views/BoardInfoView.vue
Normal 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>
|
||||
30
frontend/src/views/BoardSpendingsView.vue
Normal file
30
frontend/src/views/BoardSpendingsView.vue
Normal 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>
|
||||
@@ -1,12 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import ItemList from '@/components/ItemList.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>
|
||||
|
||||
<template>
|
||||
<ItemList title="Mitglieder">
|
||||
<ItemListElement name="Elias" subtitle="7.07€" subtitle-class="text-green-700" />
|
||||
<ItemListElement name="Max" subtitle="-16.27€" subtitle-class="text-red-700" />
|
||||
<ItemListElement name="Daniel" subtitle="9.20€" subtitle-class="text-green-700" />
|
||||
<ItemListElement
|
||||
v-for="user in users"
|
||||
: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>
|
||||
</template>
|
||||
|
||||
@@ -3,7 +3,7 @@ import CreateNewList from '../components/CreateNewList.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="grid h-full place-content-center">
|
||||
<div class="grid h-full place-content-center">
|
||||
<CreateNewList></CreateNewList>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user