feat: add setting for default bucket

Reviewed-on: vikunja/frontend#3735
This commit is contained in:
konrad 2023-09-03 15:14:44 +00:00
commit 04ba1011cc
8 changed files with 56 additions and 25 deletions

View File

@ -2,6 +2,7 @@ import {library} from '@fortawesome/fontawesome-svg-core'
import { import {
faAlignLeft, faAlignLeft,
faAngleRight, faAngleRight,
faAnglesUp,
faArchive, faArchive,
faArrowLeft, faArrowLeft,
faArrowUpFromBracket, faArrowUpFromBracket,
@ -142,6 +143,7 @@ library.add(faUser)
library.add(faUsers) library.add(faUsers)
library.add(faArrowUpFromBracket) library.add(faArrowUpFromBracket)
library.add(faX) library.add(faX)
library.add(faAnglesUp)
// overwriting the wrong types // overwriting the wrong types
export default FontAwesomeIcon as unknown as FontAwesomeIconFixedTypes export default FontAwesomeIcon as unknown as FontAwesomeIconFixedTypes

View File

@ -1,21 +1,26 @@
<template> <template>
<BaseButton class="dropdown-item"> <BaseButton class="dropdown-item">
<span class="icon" v-if="icon"> <span
v-if="icon"
class="icon is-small"
:class="iconClass"
>
<Icon :icon="icon"/> <Icon :icon="icon"/>
</span> </span>
<span> <span>
<slot /> <slot/>
</span> </span>
</BaseButton> </BaseButton>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import BaseButton, { type BaseButtonProps } from '@/components/base//BaseButton.vue' import BaseButton, {type BaseButtonProps} from '@/components/base//BaseButton.vue'
import Icon from '@/components/misc/Icon' import Icon from '@/components/misc/Icon'
import type { IconProp } from '@fortawesome/fontawesome-svg-core' import type {IconProp} from '@fortawesome/fontawesome-svg-core'
export interface DropDownItemProps extends /* @vue-ignore */ BaseButtonProps { export interface DropDownItemProps extends /* @vue-ignore */ BaseButtonProps {
icon?: IconProp, icon?: IconProp,
iconClass?: object | string,
} }
defineProps<DropDownItemProps>() defineProps<DropDownItemProps>()
@ -24,7 +29,6 @@ defineProps<DropDownItemProps>()
<style scoped lang="scss"> <style scoped lang="scss">
.dropdown-item { .dropdown-item {
color: var(--text); color: var(--text);
display: block;
font-size: 0.875rem; font-size: 0.875rem;
line-height: 1.5; line-height: 1.5;
padding: $item-padding; padding: $item-padding;
@ -52,10 +56,7 @@ defineProps<DropDownItemProps>()
.icon { .icon {
padding-right: .5rem; padding-right: .5rem;
color: var(--grey-300);
&:not(.has-text-success) {
color: var(--grey-300) !important;
}
} }
.has-text-danger .icon { .has-text-danger .icon {

View File

@ -334,6 +334,9 @@
"doneBucketHint": "All tasks moved into this bucket will automatically marked as done.", "doneBucketHint": "All tasks moved into this bucket will automatically marked as done.",
"doneBucketHintExtended": "All tasks moved into the done bucket will be marked as done automatically. All tasks marked as done from elsewhere will be moved as well.", "doneBucketHintExtended": "All tasks moved into the done bucket will be marked as done automatically. All tasks marked as done from elsewhere will be moved as well.",
"doneBucketSavedSuccess": "The done bucket has been saved successfully.", "doneBucketSavedSuccess": "The done bucket has been saved successfully.",
"defaultBucket": "Default bucket",
"defaultBucketHint": "When creating tasks without specifying a bucket, they will be added to this bucket.",
"defaultBucketSavedSuccess": "The default bucket has been saved successfully.",
"deleteLast": "You cannot remove the last bucket.", "deleteLast": "You cannot remove the last bucket.",
"addTaskPlaceholder": "Enter the new task title…", "addTaskPlaceholder": "Enter the new task title…",
"addTask": "Add a task", "addTask": "Add a task",

View File

@ -8,7 +8,6 @@ export interface IBucket extends IAbstract {
projectId: number projectId: number
limit: number limit: number
tasks: ITask[] tasks: ITask[]
isDoneBucket: boolean
position: number position: number
count: number count: number

View File

@ -19,6 +19,8 @@ export interface IProject extends IAbstract {
position: number position: number
backgroundBlurHash: string backgroundBlurHash: string
parentProjectId: number parentProjectId: number
doneBucketId: number
defaultBucketId: number
created: Date created: Date
updated: Date updated: Date

View File

@ -12,7 +12,6 @@ export default class BucketModel extends AbstractModel<IBucket> implements IBuck
projectId = '' projectId = ''
limit = 0 limit = 0
tasks: ITask[] = [] tasks: ITask[] = []
isDoneBucket = false
position = 0 position = 0
count = 0 count = 0

View File

@ -23,6 +23,8 @@ export default class ProjectModel extends AbstractModel<IProject> implements IPr
position = 0 position = 0
backgroundBlurHash = '' backgroundBlurHash = ''
parentProjectId = 0 parentProjectId = 0
doneBucketId = 0
defaultBucketId = 0
created: Date = null created: Date = null
updated: Date = null updated: Date = null

View File

@ -37,7 +37,7 @@
> >
<div class="bucket-header" @click="() => unCollapseBucket(bucket)"> <div class="bucket-header" @click="() => unCollapseBucket(bucket)">
<span <span
v-if="bucket.isDoneBucket" v-if="project.doneBucketId === bucket.id"
class="icon is-small has-text-success mr-2" class="icon is-small has-text-success mr-2"
v-tooltip="$t('project.kanban.doneBucketHint')" v-tooltip="$t('project.kanban.doneBucketHint')"
> >
@ -97,26 +97,32 @@
<dropdown-item <dropdown-item
@click.stop="toggleDoneBucket(bucket)" @click.stop="toggleDoneBucket(bucket)"
v-tooltip="$t('project.kanban.doneBucketHintExtended')" v-tooltip="$t('project.kanban.doneBucketHintExtended')"
:icon-class="{'has-text-success': bucket.id === project.doneBucketId}"
icon="check-double"
> >
<span class="icon is-small" :class="{'has-text-success': bucket.isDoneBucket}">
<icon icon="check-double"/>
</span>
{{ $t('project.kanban.doneBucket') }} {{ $t('project.kanban.doneBucket') }}
</dropdown-item> </dropdown-item>
<dropdown-item
@click.stop="toggleDefaultBucket(bucket)"
v-tooltip="$t('project.kanban.defaultBucketHint')"
:icon-class="{'has-text-primary': bucket.id === project.defaultBucketId}"
icon="th"
>
{{ $t('project.kanban.defaultBucket') }}
</dropdown-item>
<dropdown-item <dropdown-item
@click.stop="() => collapseBucket(bucket)" @click.stop="() => collapseBucket(bucket)"
icon="angles-up"
> >
{{ $t('project.kanban.collapse') }} {{ $t('project.kanban.collapse') }}
</dropdown-item> </dropdown-item>
<dropdown-item <dropdown-item
:class="{'is-disabled': buckets.length <= 1}" :class="{'is-disabled': buckets.length <= 1}"
@click.stop="() => deleteBucketModal(bucket.id)" @click.stop="() => deleteBucketModal(bucket.id)"
class="has-text-danger"
v-tooltip="buckets.length <= 1 ? $t('project.kanban.deleteLast') : ''" v-tooltip="buckets.length <= 1 ? $t('project.kanban.deleteLast') : ''"
icon-class="has-text-danger"
icon="trash-alt"
> >
<span class="icon is-small">
<icon icon="trash-alt"/>
</span>
{{ $t('misc.delete') }} {{ $t('misc.delete') }}
</dropdown-item> </dropdown-item>
</dropdown> </dropdown>
@ -251,6 +257,7 @@ import {calculateItemPosition} from '@/helpers/calculateItemPosition'
import {isSavedFilter} from '@/services/savedFilter' import {isSavedFilter} from '@/services/savedFilter'
import {success} from '@/message' import {success} from '@/message'
import {useProjectStore} from '@/stores/projects'
const DRAG_OPTIONS = { const DRAG_OPTIONS = {
// sortable options // sortable options
@ -268,6 +275,7 @@ const {t} = useI18n({useScope: 'global'})
const baseStore = useBaseStore() const baseStore = useBaseStore()
const kanbanStore = useKanbanStore() const kanbanStore = useKanbanStore()
const taskStore = useTaskStore() const taskStore = useTaskStore()
const projectStore = useProjectStore()
const taskContainerRefs = ref<{[id: IBucket['id']]: HTMLElement}>({}) const taskContainerRefs = ref<{[id: IBucket['id']]: HTMLElement}>({})
@ -422,10 +430,9 @@ async function updateTaskPosition(e) {
) )
if ( if (
oldBucket !== undefined && // This shouldn't actually be `undefined`, but let's play it safe. oldBucket !== undefined && // This shouldn't actually be `undefined`, but let's play it safe.
newBucket.id !== oldBucket.id && newBucket.id !== oldBucket.id
newBucket.isDoneBucket !== oldBucket.isDoneBucket
) { ) {
newTask.done = newBucket.isDoneBucket newTask.done = project.value.doneBucketId === newBucket.id
} }
if ( if (
oldBucket !== undefined && // This shouldn't actually be `undefined`, but let's play it safe. oldBucket !== undefined && // This shouldn't actually be `undefined`, but let's play it safe.
@ -596,10 +603,26 @@ function dragstart(bucket: IBucket) {
sourceBucket.value = bucket.id sourceBucket.value = bucket.id
} }
async function toggleDefaultBucket(bucket: IBucket) {
const defaultBucketId = project.value.defaultBucketId === bucket.id
? 0
: bucket.id
await projectStore.updateProject({
...project.value,
defaultBucketId,
})
success({message: t('project.kanban.defaultBucketSavedSuccess')})
}
async function toggleDoneBucket(bucket: IBucket) { async function toggleDoneBucket(bucket: IBucket) {
await kanbanStore.updateBucket({ const doneBucketId = project.value.doneBucketId === bucket.id
...bucket, ? 0
isDoneBucket: !bucket.isDoneBucket, : bucket.id
await projectStore.updateProject({
...project.value,
doneBucketId,
}) })
success({message: t('project.kanban.doneBucketSavedSuccess')}) success({message: t('project.kanban.doneBucketSavedSuccess')})
} }