checkpoint
This commit is contained in:
84
frontend/src/components/AddSpendingModal.vue
Normal file
84
frontend/src/components/AddSpendingModal.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<script setup lang="ts">
|
||||
import IconPlus from '@/components/icons/IconPlusCircle.vue'
|
||||
import IconX from '@/components/icons/IconX.vue'
|
||||
import { useBoardStore } from '@/stores/boardStore'
|
||||
|
||||
import { computed, ref } from 'vue'
|
||||
import InputField from './InputField.vue'
|
||||
|
||||
const props = defineProps({
|
||||
userName: String,
|
||||
})
|
||||
|
||||
const store = useBoardStore()
|
||||
const isOpen = ref(false)
|
||||
const spendingNameInput = ref('')
|
||||
const spendingNameError = computed(() => {
|
||||
if (spendingNameInput.value.trim().length == 0) return 'Required'
|
||||
return ''
|
||||
})
|
||||
const spendingAmountInput = ref('')
|
||||
const spendingAmount = computed(() => {
|
||||
return Number(parseFloat(spendingAmountInput.value.trim().replace('€', '')).toFixed(2))
|
||||
})
|
||||
const spendingAmountError = computed(() => {
|
||||
if (spendingAmountInput.value.trim().length == 0) return 'Required'
|
||||
if (isNaN(spendingAmount.value)) return 'Invalid'
|
||||
return ''
|
||||
})
|
||||
const forceValidate = ref(false)
|
||||
|
||||
function submit() {
|
||||
forceValidate.value = true
|
||||
if (spendingAmountError.value == '' && spendingNameError.value == '') {
|
||||
store
|
||||
.getCurrentBoard()
|
||||
.addSpendingByUserName(
|
||||
props.userName ?? '',
|
||||
spendingNameInput.value,
|
||||
spendingAmount.value * 100,
|
||||
)
|
||||
spendingNameInput.value = ''
|
||||
spendingAmountInput.value = ''
|
||||
isOpen.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="btn btn-square btn-ghost" @click="isOpen = true">
|
||||
<IconPlus />
|
||||
</button>
|
||||
<dialog class="modal" :class="{ 'modal-open': isOpen }">
|
||||
<div class="modal-box fieldset p-4">
|
||||
<form method="dialog" @submit.prevent="submit">
|
||||
<button
|
||||
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
|
||||
@click="isOpen = false"
|
||||
>
|
||||
<IconX></IconX>
|
||||
</button>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<h3 class="text-lg font-bold">Hinzufügen von Ausgabe für {{ props.userName }}</h3>
|
||||
|
||||
<InputField
|
||||
label="Bezeichnung"
|
||||
v-model:input-text="spendingNameInput"
|
||||
:input-error="spendingNameError"
|
||||
:force-validate="forceValidate"
|
||||
></InputField>
|
||||
<InputField
|
||||
label="Betrag"
|
||||
v-model:input-text="spendingAmountInput"
|
||||
:input-error="spendingAmountError"
|
||||
:force-validate="forceValidate"
|
||||
placeholder="0,00€"
|
||||
></InputField>
|
||||
|
||||
<button class="btn btn-neutral mt-5" type="submit">Hinzufügen</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</dialog>
|
||||
</template>
|
||||
@@ -1,11 +1,42 @@
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import InputField from './InputField.vue'
|
||||
import { useBoardStore } from '@/stores/boardStore'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const route = useRouter()
|
||||
const store = useBoardStore()
|
||||
|
||||
const boardNameInput = ref('')
|
||||
const boardNameError = computed(() => {
|
||||
if (boardNameInput.value.trim().length == 0) return 'Required'
|
||||
return ''
|
||||
})
|
||||
const forceValidate = ref(false)
|
||||
|
||||
function submit() {
|
||||
forceValidate.value = true
|
||||
if (boardNameError.value == '') {
|
||||
const newBoard = store.createBoard(boardNameInput.value)
|
||||
route.push('/boards/' + newBoard.guid)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<fieldset class="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4">
|
||||
<legend class="fieldset-legend">Neue Liste Erstellen</legend>
|
||||
<div class="join">
|
||||
<input type="text" class="input join-item" placeholder="Listenname" />
|
||||
<button class="btn join-item">Erstellen</button>
|
||||
<form class="bg-base-200 border-base-300 rounded-box w-xs border p-4" @submit.prevent="submit">
|
||||
<div class="flex flex-col gap-2">
|
||||
<h3 class="font-bold">Neue Liste Erstellen</h3>
|
||||
|
||||
<InputField
|
||||
label=""
|
||||
placeholder="Listenname"
|
||||
v-model:input-text="boardNameInput"
|
||||
:input-error="boardNameError"
|
||||
:force-validate="forceValidate"
|
||||
></InputField>
|
||||
|
||||
<button class="btn btn-neutral mt-5" type="submit">Erstellen</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
32
frontend/src/components/InputField.vue
Normal file
32
frontend/src/components/InputField.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
label: String,
|
||||
placeholder: String,
|
||||
inputError: String,
|
||||
forceValidate: Boolean,
|
||||
})
|
||||
const inputText = defineModel('inputText')
|
||||
|
||||
const touched = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<label class="label">{{ props.label }}</label>
|
||||
<input
|
||||
:class="[
|
||||
'input w-full',
|
||||
{ 'input-error': (touched || props.forceValidate) && props.inputError !== '' },
|
||||
]"
|
||||
:placeholder="props.placeholder"
|
||||
v-model="inputText"
|
||||
@blur="touched = true"
|
||||
/>
|
||||
<div
|
||||
v-if="(touched || props.forceValidate) && props.inputError !== ''"
|
||||
class="text-error text-sm mt-1"
|
||||
>
|
||||
{{ props.inputError }}
|
||||
</div>
|
||||
</template>
|
||||
12
frontend/src/components/icons/IconX.vue
Normal file
12
frontend/src/components/icons/IconX.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-4"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</template>
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Spending } from './Spending'
|
||||
import { User } from './User'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
@@ -17,4 +18,9 @@ export class Board {
|
||||
addUser(user: User): void {
|
||||
this.users.push(user)
|
||||
}
|
||||
|
||||
addSpendingByUserName(userName: string, spendingName: string, amountCt: number) {
|
||||
const user = this.users.filter((u) => u.name === userName)[0]
|
||||
user?.addSpending(new Spending(spendingName, amountCt))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Spending } from '@/services/Spending'
|
||||
export const useBoardStore = defineStore('boardStore', () => {
|
||||
const route = useRoute()
|
||||
const boards = new Map<string, Board>()
|
||||
let testBoard: Board | undefined
|
||||
|
||||
function createBoard(name: string): Board {
|
||||
const newBoard = new Board(name)
|
||||
@@ -19,15 +20,18 @@ export const useBoardStore = defineStore('boardStore', () => {
|
||||
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)
|
||||
if (testBoard === undefined) {
|
||||
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
|
||||
}
|
||||
return testBoard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import AddSpendingModal from '@/components/AddSpendingModal.vue'
|
||||
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'
|
||||
@@ -20,9 +20,7 @@ const users = ref(store.getCurrentBoard().users)
|
||||
:subtitle="(user.getTotalSpending() / 100).toFixed(2) + '€'"
|
||||
subtitle-class="text-green-700"
|
||||
>
|
||||
<button class="btn btn-square btn-ghost">
|
||||
<IconPlus />
|
||||
</button>
|
||||
<AddSpendingModal :user-name="user.name"></AddSpendingModal>
|
||||
</ItemListElement>
|
||||
</ItemList>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user