feat: remove edit-task from list view (#2721)
Co-authored-by: Dominik Pschenitschni <mail@celement.de> Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/2721 Reviewed-by: konrad <k@knt.li> Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de> Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
This commit is contained in:
parent
8ef309243d
commit
45ec1623d5
@ -78,7 +78,7 @@ describe('List View List', () => {
|
|||||||
|
|
||||||
cy.get('.menu-list li .list-menu-link .color-bubble')
|
cy.get('.menu-list li .list-menu-link .color-bubble')
|
||||||
.should('have.css', 'background-color', 'rgb(0, 219, 96)')
|
.should('have.css', 'background-color', 'rgb(0, 219, 96)')
|
||||||
cy.get('.tasks-container .tasks .color-bubble')
|
cy.get('.tasks .color-bubble')
|
||||||
.should('not.exist')
|
.should('not.exist')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -90,9 +90,9 @@ describe('List View List', () => {
|
|||||||
})
|
})
|
||||||
cy.visit('/lists/1/list')
|
cy.visit('/lists/1/list')
|
||||||
|
|
||||||
cy.get('.tasks-container .tasks')
|
cy.get('.tasks')
|
||||||
.should('contain', tasks[1].title)
|
.should('contain', tasks[1].title)
|
||||||
cy.get('.tasks-container .tasks')
|
cy.get('.tasks')
|
||||||
.should('not.contain', tasks[99].title)
|
.should('not.contain', tasks[99].title)
|
||||||
|
|
||||||
cy.get('.card-content .pagination .pagination-link')
|
cy.get('.card-content .pagination .pagination-link')
|
||||||
@ -101,9 +101,9 @@ describe('List View List', () => {
|
|||||||
|
|
||||||
cy.url()
|
cy.url()
|
||||||
.should('contain', '?page=2')
|
.should('contain', '?page=2')
|
||||||
cy.get('.tasks-container .tasks')
|
cy.get('.tasks')
|
||||||
.should('contain', tasks[99].title)
|
.should('contain', tasks[99].title)
|
||||||
cy.get('.tasks-container .tasks')
|
cy.get('.tasks')
|
||||||
.should('not.contain', tasks[1].title)
|
.should('not.contain', tasks[1].title)
|
||||||
})
|
})
|
||||||
})
|
})
|
@ -1,187 +0,0 @@
|
|||||||
<template>
|
|
||||||
<card
|
|
||||||
class="taskedit"
|
|
||||||
:title="$t('list.list.editTask')"
|
|
||||||
@close="$emit('close')"
|
|
||||||
:has-close="true"
|
|
||||||
>
|
|
||||||
<form @submit.prevent="editTaskSubmit()">
|
|
||||||
<div class="field">
|
|
||||||
<label class="label" for="tasktext">{{ $t('task.attributes.title') }}</label>
|
|
||||||
<div class="control">
|
|
||||||
<input
|
|
||||||
:class="{ disabled: taskService.loading }"
|
|
||||||
:disabled="taskService.loading || undefined"
|
|
||||||
@change="editTaskSubmit()"
|
|
||||||
class="input"
|
|
||||||
id="tasktext"
|
|
||||||
type="text"
|
|
||||||
v-focus
|
|
||||||
v-model="taskEditTask.title"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label class="label" for="taskdescription">{{ $t('task.attributes.description') }}</label>
|
|
||||||
<div class="control">
|
|
||||||
<editor
|
|
||||||
:preview-is-default="false"
|
|
||||||
id="taskdescription"
|
|
||||||
:placeholder="$t('task.description.placeholder')"
|
|
||||||
v-if="editorActive"
|
|
||||||
v-model="taskEditTask.description"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<strong>{{ $t('task.attributes.reminders') }}</strong>
|
|
||||||
<reminders
|
|
||||||
v-model="taskEditTask.reminderDates"
|
|
||||||
@update:model-value="editTaskSubmit()"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">{{ $t('task.attributes.labels') }}</label>
|
|
||||||
<div class="control">
|
|
||||||
<edit-labels
|
|
||||||
:task-id="taskEditTask.id"
|
|
||||||
v-model="taskEditTask.labels"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">{{ $t('task.attributes.color') }}</label>
|
|
||||||
<div class="control">
|
|
||||||
<color-picker v-model="taskEditTask.hexColor" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<x-button
|
|
||||||
:loading="taskService.loading"
|
|
||||||
class="is-fullwidth"
|
|
||||||
@click="editTaskSubmit()"
|
|
||||||
>
|
|
||||||
{{ $t('misc.save') }}
|
|
||||||
</x-button>
|
|
||||||
|
|
||||||
<router-link
|
|
||||||
class="mt-2 has-text-centered is-block"
|
|
||||||
:to="taskDetailRoute"
|
|
||||||
>
|
|
||||||
{{ $t('task.openDetail') }}
|
|
||||||
</router-link>
|
|
||||||
</form>
|
|
||||||
</card>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import {ref, reactive, computed, shallowReactive, watch, nextTick, type PropType} from 'vue'
|
|
||||||
import {useRouter} from 'vue-router'
|
|
||||||
import {useI18n} from 'vue-i18n'
|
|
||||||
|
|
||||||
import Editor from '@/components/input/AsyncEditor'
|
|
||||||
|
|
||||||
import TaskService from '@/services/task'
|
|
||||||
import TaskModel from '@/models/task'
|
|
||||||
import type {ITask} from '@/modelTypes/ITask'
|
|
||||||
import EditLabels from './partials/editLabels.vue'
|
|
||||||
import Reminders from './partials/reminders.vue'
|
|
||||||
import ColorPicker from '../input/colorPicker.vue'
|
|
||||||
|
|
||||||
import {success} from '@/message'
|
|
||||||
|
|
||||||
const {t} = useI18n({useScope: 'global'})
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
task: {
|
|
||||||
type: Object as PropType<ITask | null>,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const taskService = shallowReactive(new TaskService())
|
|
||||||
|
|
||||||
const editorActive = ref(false)
|
|
||||||
let taskEditTask: ITask | undefined
|
|
||||||
|
|
||||||
|
|
||||||
// FIXME: this initialization should not be necessary here
|
|
||||||
function initTaskFields() {
|
|
||||||
taskEditTask.dueDate =
|
|
||||||
+new Date(props.task.dueDate) === 0 ? null : props.task.dueDate
|
|
||||||
taskEditTask.startDate =
|
|
||||||
+new Date(props.task.startDate) === 0
|
|
||||||
? null
|
|
||||||
: props.task.startDate
|
|
||||||
taskEditTask.endDate =
|
|
||||||
+new Date(props.task.endDate) === 0 ? null : props.task.endDate
|
|
||||||
// This makes the editor trigger its mounted function again which makes it forget every input
|
|
||||||
// it currently has in its textarea. This is a counter-hack to a hack inside of vue-easymde
|
|
||||||
// which made it impossible to detect change from the outside. Therefore the component would
|
|
||||||
// not update if new content from the outside was made available.
|
|
||||||
// See https://github.com/NikulinIlya/vue-easymde/issues/3
|
|
||||||
editorActive.value = false
|
|
||||||
nextTick(() => (editorActive.value = true))
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.task,
|
|
||||||
() => {
|
|
||||||
if (!taskEditTask) {
|
|
||||||
taskEditTask = reactive(props.task)
|
|
||||||
} else {
|
|
||||||
Object.assign(taskEditTask, new TaskModel(props.task))
|
|
||||||
}
|
|
||||||
initTaskFields()
|
|
||||||
},
|
|
||||||
{immediate: true },
|
|
||||||
)
|
|
||||||
const taskDetailRoute = computed(() => {
|
|
||||||
return {
|
|
||||||
name: 'task.detail',
|
|
||||||
params: { id: taskEditTask.id },
|
|
||||||
state: { backdropView: router.currentRoute.value.fullPath },
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
async function editTaskSubmit() {
|
|
||||||
const newTask = await taskService.update(taskEditTask)
|
|
||||||
Object.assign(taskEditTask, newTask)
|
|
||||||
initTaskFields()
|
|
||||||
success({message: t('task.detail.updateSuccess')})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.priority-select {
|
|
||||||
.select,
|
|
||||||
select {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.assingees {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
li {
|
|
||||||
padding: 0.5rem 0.5rem 0;
|
|
||||||
|
|
||||||
a {
|
|
||||||
float: right;
|
|
||||||
color: var(--danger);
|
|
||||||
transition: all $transition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -53,15 +53,13 @@
|
|||||||
class="loader-container is-max-width-desktop list-view"
|
class="loader-container is-max-width-desktop list-view"
|
||||||
>
|
>
|
||||||
<card :padding="false" :has-content="false" class="has-overflow">
|
<card :padding="false" :has-content="false" class="has-overflow">
|
||||||
<template
|
|
||||||
v-if="!list.isArchived && canWrite"
|
|
||||||
>
|
|
||||||
<add-task
|
<add-task
|
||||||
@taskAdded="updateTaskList"
|
v-if="!list.isArchived && canWrite"
|
||||||
|
class="list-view__add-task"
|
||||||
ref="addTaskRef"
|
ref="addTaskRef"
|
||||||
:default-position="firstNewPosition"
|
:default-position="firstNewPosition"
|
||||||
|
@taskAdded="updateTaskList"
|
||||||
/>
|
/>
|
||||||
</template>
|
|
||||||
|
|
||||||
<nothing v-if="ctaVisible && tasks.length === 0 && !loading">
|
<nothing v-if="ctaVisible && tasks.length === 0 && !loading">
|
||||||
{{ $t('list.list.empty') }}
|
{{ $t('list.list.empty') }}
|
||||||
@ -70,12 +68,9 @@
|
|||||||
</ButtonLink>
|
</ButtonLink>
|
||||||
</nothing>
|
</nothing>
|
||||||
|
|
||||||
<div class="tasks-container" :class="{ 'has-task-edit-open': isTaskEdit }">
|
|
||||||
<div
|
|
||||||
class="tasks mt-0"
|
|
||||||
v-if="tasks && tasks.length > 0"
|
|
||||||
>
|
|
||||||
<draggable
|
<draggable
|
||||||
|
v-if="tasks && tasks.length > 0"
|
||||||
v-bind="DRAG_OPTIONS"
|
v-bind="DRAG_OPTIONS"
|
||||||
v-model="tasks"
|
v-model="tasks"
|
||||||
group="tasks"
|
group="tasks"
|
||||||
@ -86,7 +81,10 @@
|
|||||||
item-key="id"
|
item-key="id"
|
||||||
tag="ul"
|
tag="ul"
|
||||||
:component-data="{
|
:component-data="{
|
||||||
class: { 'dragging-disabled': !canWrite || isAlphabeticalSorting },
|
class: {
|
||||||
|
tasks: true,
|
||||||
|
'dragging-disabled': !canWrite || isAlphabeticalSorting
|
||||||
|
},
|
||||||
type: 'transition-group'
|
type: 'transition-group'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
@ -102,27 +100,10 @@
|
|||||||
<span class="icon handle">
|
<span class="icon handle">
|
||||||
<icon icon="grip-lines"/>
|
<icon icon="grip-lines"/>
|
||||||
</span>
|
</span>
|
||||||
<BaseButton
|
|
||||||
@click="editTask(t.id)"
|
|
||||||
class="icon settings"
|
|
||||||
v-if="!list.isArchived"
|
|
||||||
>
|
|
||||||
<icon icon="pencil-alt"/>
|
|
||||||
</BaseButton>
|
|
||||||
</template>
|
</template>
|
||||||
</single-task-in-list>
|
</single-task-in-list>
|
||||||
</template>
|
</template>
|
||||||
</draggable>
|
</draggable>
|
||||||
</div>
|
|
||||||
<EditTask
|
|
||||||
v-if="isTaskEdit"
|
|
||||||
class="taskedit mt-0"
|
|
||||||
:title="$t('list.list.editTask')"
|
|
||||||
@close="closeTaskEditPane()"
|
|
||||||
:shadow="false"
|
|
||||||
:task="taskEditTask"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Pagination
|
<Pagination
|
||||||
:total-pages="totalPages"
|
:total-pages="totalPages"
|
||||||
@ -139,14 +120,12 @@ export default { name: 'List' }
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {ref, computed, toRef, nextTick, onMounted, type PropType, watch} from 'vue'
|
import {ref, computed, toRef, nextTick, onMounted, type PropType} from 'vue'
|
||||||
import draggable from 'zhyswan-vuedraggable'
|
import draggable from 'zhyswan-vuedraggable'
|
||||||
import {useRoute, useRouter} from 'vue-router'
|
import {useRoute, useRouter} from 'vue-router'
|
||||||
|
|
||||||
import ListWrapper from '@/components/list/ListWrapper.vue'
|
import ListWrapper from '@/components/list/ListWrapper.vue'
|
||||||
import BaseButton from '@/components/base/BaseButton.vue'
|
|
||||||
import ButtonLink from '@/components/misc/ButtonLink.vue'
|
import ButtonLink from '@/components/misc/ButtonLink.vue'
|
||||||
import EditTask from '@/components/tasks/edit-task.vue'
|
|
||||||
import AddTask from '@/components/tasks/add-task.vue'
|
import AddTask from '@/components/tasks/add-task.vue'
|
||||||
import SingleTaskInList from '@/components/tasks/partials/singleTaskInList.vue'
|
import SingleTaskInList from '@/components/tasks/partials/singleTaskInList.vue'
|
||||||
import FilterPopup from '@/components/list/partials/filter-popup.vue'
|
import FilterPopup from '@/components/list/partials/filter-popup.vue'
|
||||||
@ -196,23 +175,9 @@ const showTaskSearch = ref(false)
|
|||||||
const drag = ref(false)
|
const drag = ref(false)
|
||||||
const DRAG_OPTIONS = {
|
const DRAG_OPTIONS = {
|
||||||
animation: 100,
|
animation: 100,
|
||||||
ghostClass: 'ghost',
|
ghostClass: 'task-ghost',
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
|
||||||
const taskEditTask = ref<ITask | null>(null)
|
|
||||||
const isTaskEdit = ref(false)
|
|
||||||
|
|
||||||
function closeTaskEditPane() {
|
|
||||||
isTaskEdit.value = false
|
|
||||||
taskEditTask.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.listId,
|
|
||||||
closeTaskEditPane,
|
|
||||||
)
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
tasks,
|
tasks,
|
||||||
loading,
|
loading,
|
||||||
@ -296,11 +261,6 @@ function updateTaskList(task: ITask) {
|
|||||||
baseStore.setHasTasks(true)
|
baseStore.setHasTasks(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
function editTask(id: ITask['id']) {
|
|
||||||
taskEditTask.value = {...tasks.value.find(t => t.id === Number(id))}
|
|
||||||
isTaskEdit.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateTasks(updatedTask: ITask) {
|
function updateTasks(updatedTask: ITask) {
|
||||||
for (const t in tasks.value) {
|
for (const t in tasks.value) {
|
||||||
if (tasks.value[t].id === updatedTask.id) {
|
if (tasks.value[t].id === updatedTask.id) {
|
||||||
@ -339,26 +299,11 @@ function prepareFiltersAndLoadTasks() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.tasks-container {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&.has-task-edit-open {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
@media screen and (min-width: $tablet) {
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
.tasks {
|
.tasks {
|
||||||
width: 66%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tasks {
|
|
||||||
width: 100%;
|
|
||||||
padding: .5rem;
|
padding: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.ghost {
|
.task-ghost {
|
||||||
border-radius: $radius;
|
border-radius: $radius;
|
||||||
background: var(--grey-100);
|
background: var(--grey-100);
|
||||||
border: 2px dashed var(--grey-300);
|
border: 2px dashed var(--grey-300);
|
||||||
@ -367,26 +312,8 @@ function prepareFiltersAndLoadTasks() {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.taskedit {
|
.list-view__add-task {
|
||||||
width: 33%;
|
|
||||||
margin-right: 1rem;
|
|
||||||
margin-left: .5rem;
|
|
||||||
min-height: calc(100% - 1rem);
|
|
||||||
|
|
||||||
@media screen and (max-width: $tablet) {
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 0;
|
|
||||||
margin: 0;
|
|
||||||
border-left: 0;
|
|
||||||
border-right: 0;
|
|
||||||
border-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-view .task-add {
|
|
||||||
padding: 1rem 1rem 0;
|
padding: 1rem 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user