parsley/app/javascript/components/TaskItemList.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>