feat(quick actions): show labels as labels and tasks with all of their details

This commit is contained in:
kolaente 2023-08-29 12:33:43 +02:00
parent 99d8fbdfa7
commit d57e1909c4
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
4 changed files with 244 additions and 24 deletions

View File

@ -44,10 +44,18 @@
@keyup.prevent.enter="doAction(r.type, i)"
@keyup.prevent.esc="searchInput?.focus()"
>
<span v-if="i.identifier" class="has-text-grey-light">
{{ i.identifier }}
</span>
{{ i.title }}
<template v-if="r.type === ACTION_TYPE.LABELS">
<x-label :label="i"/>
</template>
<template v-else-if="r.type === ACTION_TYPE.TASK">
<single-task-inline-readonly
:task="i"
:show-project="true"
/>
</template>
<template v-else>
{{ i.title }}
</template>
</BaseButton>
</div>
</div>
@ -69,6 +77,8 @@ import ProjectModel from '@/models/project'
import BaseButton from '@/components/base/BaseButton.vue'
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue'
import XLabel from '@/components/tasks/partials/label.vue'
import SingleTaskInlineReadonly from '@/components/tasks/partials/singleTaskInlineReadonly.vue'
import {useBaseStore} from '@/stores/base'
import {useProjectStore} from '@/stores/projects'
@ -83,7 +93,6 @@ import {success} from '@/message'
import type {ITeam} from '@/modelTypes/ITeam'
import type {ITask} from '@/modelTypes/ITask'
import type {IProject} from '@/modelTypes/IProject'
import type {ILabel} from '@/modelTypes/ILabel'
const {t} = useI18n({useScope: 'global'})
const router = useRouter()
@ -159,11 +168,11 @@ const foundLabels = computed(() => {
if (text === '' && labels.length === 0) {
return []
}
if (labels.length > 0) {
return labelStore.filterLabelsByQuery([], labels[0])
}
return labelStore.filterLabelsByQuery([], text)
})
@ -393,10 +402,6 @@ function searchTasks() {
const r = await taskService.getAll({}, params) as DoAction<ITask>[]
foundTasks.value = r.map((t) => {
t.type = ACTION_TYPE.TASK
const project = projectStore.projects[t.projectId]
if (project !== null) {
t.title = `${t.title} (${project.title})`
}
return t
})
}, 150)
@ -468,7 +473,7 @@ async function doAction(type: ACTION_TYPE, item: DoAction) {
searchInput.value?.focus()
break
case ACTION_TYPE.LABELS:
query.value = '*'+item.title
query.value = '*' + item.title
searchInput.value?.focus()
searchTasks()
break

View File

@ -0,0 +1,25 @@
<script setup lang="ts">
import type {ILabel} from '@/modelTypes/ILabel'
defineProps<{
label: ILabel
}>()
</script>
<template>
<span
:key="label.id"
:style="{'background': label.hexColor, 'color': label.textColor}"
class="tag"
>
<span>{{ label.title }}</span>
</span>
</template>
<style scoped lang="scss">
.tag {
& + & {
margin-left: 0.5rem;
}
}
</style>

View File

@ -1,12 +1,10 @@
<template>
<div class="label-wrapper">
<span
<XLabel
v-for="label in labels"
:label="label"
:key="label.id"
:style="{'background': label.hexColor, 'color': label.textColor}"
class="tag"
v-for="label in labels">
<span>{{ label.title }}</span>
</span>
/>
</div>
</template>
@ -14,6 +12,8 @@
import type {PropType} from 'vue'
import type {ILabel} from '@/modelTypes/ILabel'
import XLabel from '@/components/tasks/partials/label.vue'
defineProps({
labels: {
type: Array as PropType<ILabel[]>,
@ -26,10 +26,4 @@ defineProps({
.label-wrapper {
display: inline;
}
.tag {
& + & {
margin-left: 0.5rem;
}
}
</style>

View File

@ -0,0 +1,196 @@
<template>
<div class="task">
<span>
<span
v-if="showProject && typeof project !== 'undefined'"
class="task-project"
:class="{'mr-2': task.hexColor !== ''}"
v-tooltip="$t('task.detail.belongsToProject', {project: project.title})"
>
{{ project.title }}
</span>
<ColorBubble
v-if="task.hexColor !== ''"
:color="getHexColor(task.hexColor)"
class="mr-1"
/>
<!-- Show any parent tasks to make it clear this task is a sub task of something -->
<span class="parent-tasks" v-if="typeof task.relatedTasks?.parenttask !== 'undefined'">
<template v-for="(pt, i) in task.relatedTasks.parenttask">
{{ pt.title }}<template v-if="(i + 1) < task.relatedTasks.parenttask.length">,&nbsp;</template>
</template>
&rsaquo;
</span>
{{ task.title }}
</span>
<labels
v-if="task.labels.length > 0"
class="labels ml-2 mr-1"
:labels="task.labels"
/>
<User
v-for="(a, i) in task.assignees"
:avatar-size="20"
:key="task.id + 'assignee' + a.id + i"
:show-username="false"
:user="a"
class="avatar"
/>
<span
v-if="+new Date(task.dueDate) > 0"
class="dueDate"
v-tooltip="formatDateLong(task.dueDate)"
>
<time
:datetime="formatISO(task.dueDate)"
:class="{'overdue': task.dueDate <= new Date() && !task.done}"
class="is-italic"
>
{{ $t('task.detail.due', {at: formatDateSince(task.dueDate)}) }}
</time>
</span>
<priority-label :priority="task.priority" :done="task.done"/>
<span>
<span class="project-task-icon" v-if="task.attachments.length > 0">
<icon icon="paperclip"/>
</span>
<span class="project-task-icon" v-if="task.description">
<icon icon="align-left"/>
</span>
<span class="project-task-icon" v-if="task.repeatAfter.amount > 0">
<icon icon="history"/>
</span>
</span>
<checklist-summary :task="task"/>
<progress
class="progress is-small"
v-if="task.percentDone > 0"
:value="task.percentDone * 100" max="100"
>
{{ task.percentDone * 100 }}%
</progress>
</div>
</template>
<script setup lang="ts">
import {computed} from 'vue'
import {useI18n} from 'vue-i18n'
import {getHexColor} from '@/models/task'
import type {ITask} from '@/modelTypes/ITask'
import PriorityLabel from '@/components/tasks/partials/priorityLabel.vue'
import Labels from '@/components/tasks/partials//labels.vue'
import ChecklistSummary from '@/components/tasks/partials/checklist-summary.vue'
import User from '@/components/misc/user.vue'
import ColorBubble from '@/components/misc/colorBubble.vue'
import {formatDateSince, formatISO, formatDateLong} from '@/helpers/time/formatDate'
import {useProjectStore} from '@/stores/projects'
const {
task,
showProject = false,
} = defineProps<{
task: ITask,
showProject?: boolean,
}>()
const {t} = useI18n({useScope: 'global'})
const projectStore = useProjectStore()
const project = computed(() => projectStore.projects[task.projectId])
const projectColor = computed(() => project.value ? project.value?.hexColor : '')
</script>
<style lang="scss" scoped>
.task {
display: flex;
flex-wrap: wrap;
transition: background-color $transition;
align-items: center;
cursor: pointer;
border-radius: $radius;
border: 2px solid transparent;
text-overflow: ellipsis;
word-wrap: break-word;
word-break: break-word;
//display: -webkit-box;
hyphens: auto;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
overflow: hidden;
//flex: 1 0 50%;
.dueDate {
display: inline-block;
margin-left: 5px;
}
.overdue {
color: var(--danger);
}
.task-project {
width: auto;
color: var(--grey-400);
font-size: .9rem;
white-space: nowrap;
}
.avatar {
border-radius: 50%;
vertical-align: bottom;
margin-left: .5rem;
height: 21px;
width: 21px;
}
.project-task-icon {
margin-left: 6px;
&:not(:first-of-type) {
margin-left: 8px;
}
}
a {
color: var(--text);
transition: color ease $transition-duration;
&:hover {
color: var(--grey-900);
}
}
.tasktext.done {
text-decoration: line-through;
color: var(--grey-500);
}
span.parent-tasks {
color: var(--grey-500);
width: auto;
}
.progress {
margin-bottom: 0;
}
}
</style>