193 lines
5.3 KiB
Vue
193 lines
5.3 KiB
Vue
<template>
|
|
<div>
|
|
|
|
<div class="panel">
|
|
<p class="panel-heading">
|
|
{{taskList.name}} ({{completedItemCount}} / {{taskList.task_items.length}})
|
|
</p>
|
|
|
|
<div class="panel-block">
|
|
<button class="button is-fullwidth is-primary" @click="toggleShowAddItem">{{ showAddItem ? 'Done' : 'New Item' }}</button>
|
|
</div>
|
|
|
|
<app-expand-transition>
|
|
<div class="panel-block" v-if="showAddItem">
|
|
<task-item-edit @save="save" :task-item="newItem" ref="itemEdit"></task-item-edit>
|
|
</div>
|
|
</app-expand-transition>
|
|
|
|
<transition-group tag="div" name="list-item-move">
|
|
<a v-for="i in taskItems" :key="i.id" @click="toggleItem(i)" class="panel-block">
|
|
<div class="check">
|
|
<app-icon v-if="i.completed" icon="check"></app-icon>
|
|
<span class="icon" v-else></span>
|
|
</div>
|
|
<span>{{ i.quantity }} {{ i.name }}</span>
|
|
</a>
|
|
</transition-group>
|
|
|
|
<app-expand-transition>
|
|
<div class="panel-block" v-if="uncompletedItemCount > 0">
|
|
<button class="button is-fullwidth is-link" @click="completeAllItems">
|
|
<span class="check">
|
|
<app-icon icon="check"></app-icon>
|
|
</span>
|
|
<span>Check All</span>
|
|
</button>
|
|
</div>
|
|
</app-expand-transition>
|
|
<app-expand-transition>
|
|
<div class="panel-block" v-if="completedItemCount > 0">
|
|
<button class="button is-fullwidth is-link" @click="unCompleteAllItems">
|
|
<span class="check">
|
|
<span class="icon"></span>
|
|
</span>
|
|
<span>Uncheck All</span>
|
|
</button>
|
|
</div>
|
|
</app-expand-transition>
|
|
<app-expand-transition>
|
|
<div class="panel-block" v-if="completedItemCount > 0">
|
|
<button class="button is-fullwidth is-link" @click="deleteCompletedItems">
|
|
<app-icon icon="x" class="is-text-danger"></app-icon>
|
|
<span>Clear Completed</span>
|
|
</button>
|
|
</div>
|
|
</app-expand-transition>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
|
|
import { computed, ref, useTemplateRef } from "vue";
|
|
import * as Errors from '../lib/Errors';
|
|
import { useTaskStore } from "../stores/task";
|
|
import { useLoadResource } from "../lib/useLoadResource";
|
|
import TaskItemEdit from "./TaskItemEdit";
|
|
|
|
const { loadResource } = useLoadResource();
|
|
const taskStore = useTaskStore();
|
|
const itemEditElement = useTemplateRef("itemEdit");
|
|
|
|
const props = defineProps({
|
|
taskList: {
|
|
required: true,
|
|
type: Object
|
|
}
|
|
});
|
|
|
|
const showAddItem = ref(false);
|
|
const newItem = ref(null);
|
|
const newItemValidationErrors = ref({});
|
|
|
|
const completedTaskItems = computed(() => (props.taskList ? props.taskList.task_items : []).filter(i => i.completed));
|
|
const uncompletedTaskItems = computed(() => (props.taskList ? props.taskList.task_items : []).filter(i => !i.completed));
|
|
const completedItemCount = computed(() => completedTaskItems.value.length);
|
|
const uncompletedItemCount = computed(() => uncompletedTaskItems.value.length);
|
|
const taskItems = computed(() => uncompletedTaskItems.value.concat(completedTaskItems.value));
|
|
|
|
function newItemTemplate() {
|
|
return {
|
|
task_list_id: null,
|
|
name: '',
|
|
quantity: '',
|
|
completed: false
|
|
};
|
|
}
|
|
|
|
function save() {
|
|
newItem.value.task_list_id = props.taskList.id;
|
|
loadResource(
|
|
taskStore.createTaskItem(newItem.value)
|
|
.then(() => {
|
|
newItem.value = newItemTemplate();
|
|
itemEditElement.value.focus();
|
|
})
|
|
.catch(Errors.onlyFor(Errors.ApiValidationError, err => newItemValidationErrors.value = err.validationErrors()))
|
|
)
|
|
}
|
|
|
|
function toggleItem(i) {
|
|
loadResource(
|
|
taskStore.completeTaskItems({
|
|
taskList: props.taskList,
|
|
taskItems: [i],
|
|
completed: !i.completed
|
|
})
|
|
);
|
|
}
|
|
|
|
function toggleShowAddItem() {
|
|
newItem.value = newItemTemplate();
|
|
showAddItem.value = !showAddItem.value;
|
|
}
|
|
|
|
function completeAllItems() {
|
|
const toComplete = props.taskList.task_items.filter(i => !i.completed);
|
|
loadResource(
|
|
taskStore.completeTaskItems({
|
|
taskList: props.taskList,
|
|
taskItems: toComplete,
|
|
completed: true
|
|
})
|
|
)
|
|
}
|
|
|
|
function unCompleteAllItems() {
|
|
const toUnComplete = props.taskList.task_items.filter(i => i.completed);
|
|
loadResource(
|
|
taskStore.completeTaskItems({
|
|
taskList: props.taskList,
|
|
taskItems: toUnComplete,
|
|
completed: false
|
|
})
|
|
)
|
|
}
|
|
|
|
function deleteCompletedItems() {
|
|
loadResource(
|
|
taskStore.deleteTaskItems({
|
|
taskList: props.taskList,
|
|
taskItems: props.taskList.task_items.filter(i => i.completed)
|
|
})
|
|
);
|
|
}
|
|
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
.columns {
|
|
margin-top: 0;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.column {
|
|
padding-top: 0;
|
|
padding-bottom: 0;
|
|
margin-top: 0;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.check {
|
|
display: inline-flex;
|
|
margin-right: 1.5rem;
|
|
|
|
.icon {
|
|
position: relative;
|
|
&:after {
|
|
content: '';
|
|
position: absolute;
|
|
top: -3px;
|
|
left: -3px;
|
|
bottom: -3px;
|
|
right: -3px;
|
|
border: 2px solid currentColor;
|
|
}
|
|
}
|
|
}
|
|
|
|
</style> |