All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 2m34s
- Implemented ChoreAssignView for assigning chores to children. - Created ChoreConfirmDialog for confirming chore completion. - Developed KindnessAssignView for assigning kindness acts. - Added PenaltyAssignView for assigning penalties. - Introduced ChoreEditView and ChoreView for editing and viewing chores. - Created KindnessEditView and KindnessView for managing kindness acts. - Developed PenaltyEditView and PenaltyView for managing penalties. - Added TaskSubNav for navigation between chores, kindness acts, and penalties.
120 lines
3.0 KiB
Vue
120 lines
3.0 KiB
Vue
<template>
|
|
<div class="penalty-view">
|
|
<MessageBlock v-if="countRef === 0" message="No penalties">
|
|
<span> <button class="round-btn" @click="create">Create</button> a penalty </span>
|
|
</MessageBlock>
|
|
|
|
<ItemList
|
|
v-else
|
|
ref="listRef"
|
|
fetchUrl="/api/penalty/list"
|
|
itemKey="tasks"
|
|
:itemFields="TASK_FIELDS"
|
|
imageField="image_id"
|
|
deletable
|
|
@clicked="(item: Task) => $router.push({ name: 'EditPenalty', params: { id: item.id } })"
|
|
@delete="confirmDelete"
|
|
@loading-complete="(count) => (countRef = count)"
|
|
:getItemClass="() => ({ bad: true })"
|
|
>
|
|
<template #item="{ item }">
|
|
<img v-if="item.image_url" :src="item.image_url" />
|
|
<span class="name">{{ item.name }}</span>
|
|
<span class="value">{{ item.points }} pts</span>
|
|
</template>
|
|
</ItemList>
|
|
|
|
<FloatingActionButton aria-label="Create Penalty" @click="create" />
|
|
|
|
<DeleteModal
|
|
:show="showConfirm"
|
|
message="Are you sure you want to delete this penalty?"
|
|
@confirm="deleteItem"
|
|
@cancel="showConfirm = false"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, onMounted, onUnmounted } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import ItemList from '../shared/ItemList.vue'
|
|
import MessageBlock from '@/components/shared/MessageBlock.vue'
|
|
import FloatingActionButton from '../shared/FloatingActionButton.vue'
|
|
import DeleteModal from '../shared/DeleteModal.vue'
|
|
import type { Task } from '@/common/models'
|
|
import { TASK_FIELDS } from '@/common/models'
|
|
import { eventBus } from '@/common/eventBus'
|
|
|
|
const $router = useRouter()
|
|
const showConfirm = ref(false)
|
|
const itemToDelete = ref<string | null>(null)
|
|
const listRef = ref()
|
|
const countRef = ref<number>(-1)
|
|
|
|
function handleModified() {
|
|
listRef.value?.refresh()
|
|
}
|
|
|
|
onMounted(() => {
|
|
eventBus.on('task_modified', handleModified)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
eventBus.off('task_modified', handleModified)
|
|
})
|
|
|
|
function confirmDelete(id: string) {
|
|
itemToDelete.value = id
|
|
showConfirm.value = true
|
|
}
|
|
|
|
const deleteItem = async () => {
|
|
const id =
|
|
typeof itemToDelete.value === 'object' && itemToDelete.value !== null
|
|
? (itemToDelete.value as any).id
|
|
: itemToDelete.value
|
|
if (!id) return
|
|
try {
|
|
const resp = await fetch(`/api/penalty/${id}`, { method: 'DELETE' })
|
|
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
|
|
} catch (err) {
|
|
console.error('Failed to delete penalty:', err)
|
|
} finally {
|
|
showConfirm.value = false
|
|
itemToDelete.value = null
|
|
}
|
|
}
|
|
|
|
const create = () => {
|
|
$router.push({ name: 'CreatePenalty' })
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.penalty-view {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
flex: 1 1 auto;
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: 0;
|
|
min-height: 0;
|
|
}
|
|
.name {
|
|
flex: 1;
|
|
text-align: left;
|
|
font-weight: 600;
|
|
}
|
|
.value {
|
|
min-width: 60px;
|
|
text-align: right;
|
|
font-weight: 600;
|
|
}
|
|
:deep(.bad) {
|
|
border-color: var(--list-item-border-bad);
|
|
background: var(--list-item-bg-bad);
|
|
}
|
|
</style>
|