feat: rename list to project everywhere
fix: project table view fix: e2e tests fix: typo in readme fix: list view route fix: don't wait until background is loaded for list to show fix: rename component imports fix: lint fix: parse task text fix: use list card grid fix: use correct class names fix: i18n keys fix: load project fix: task overview fix: list view spacing fix: find project fix: setLoading when updating a project fix: loading saved filter fix: project store loading fix: color picker import fix: cypress tests feat: migrate old list settings chore: add const for project settings fix: wrong projecten rename from lists chore: rename unused variable fix: editor list fix: shortcut list class name fix: pagination list class name fix: notifications list class name fix: list view variable name chore: clarify comment fix: i18n keys fix: router imports fix: comment chore: remove debugging leftover fix: remove duplicate variables fix: change comment fix: list view variable name fix: list view css class name fix: list item property name fix: name update tasks function correctly fix: update comment fix: project create route fix: list view class names fix: list view component name fix: result list class name fix: animation class list name fix: change debug log fix: revert a few navigation changes fix: use @ for imports of all views fix: rename link share list class fix: remove unused css class fix: dynamically import project components again
This commit is contained in:
@ -14,36 +14,36 @@
|
||||
</router-link>
|
||||
</message>
|
||||
<add-task
|
||||
@taskAdded="updateTaskList"
|
||||
@taskAdded="updateTaskKey"
|
||||
class="is-max-width-desktop"
|
||||
/>
|
||||
<template v-if="!hasTasks && !loading">
|
||||
<template v-if="defaultNamespaceId > 0">
|
||||
<p class="mt-4">{{ $t('home.list.newText') }}</p>
|
||||
<p class="mt-4">{{ $t('home.project.newText') }}</p>
|
||||
<x-button
|
||||
:to="{ name: 'list.create', params: { namespaceId: defaultNamespaceId } }"
|
||||
:to="{ name: 'project.create', params: { namespaceId: defaultNamespaceId } }"
|
||||
:shadow="false"
|
||||
class="ml-2"
|
||||
>
|
||||
{{ $t('home.list.new') }}
|
||||
{{ $t('home.project.new') }}
|
||||
</x-button>
|
||||
</template>
|
||||
<p class="mt-4" v-if="migratorsEnabled">
|
||||
{{ $t('home.list.importText') }}
|
||||
{{ $t('home.project.importText') }}
|
||||
</p>
|
||||
<x-button
|
||||
v-if="migratorsEnabled"
|
||||
:to="{ name: 'migrate.start' }"
|
||||
:shadow="false">
|
||||
{{ $t('home.list.import') }}
|
||||
{{ $t('home.project.import') }}
|
||||
</x-button>
|
||||
</template>
|
||||
<div v-if="listHistory.length > 0" class="is-max-width-desktop has-text-left mt-4">
|
||||
<div v-if="projectHistory.length > 0" class="is-max-width-desktop has-text-left mt-4">
|
||||
<h3>{{ $t('home.lastViewed') }}</h3>
|
||||
<ListCardGrid :lists="listHistory" v-cy="'listCardGrid'" />
|
||||
<ProjectCardGrid :projects="projectHistory" v-cy="'projectCardGrid'" />
|
||||
</div>
|
||||
<ShowTasks
|
||||
v-if="hasLists"
|
||||
v-if="hasProjects"
|
||||
class="show-tasks"
|
||||
:key="showTasksKey"
|
||||
/>
|
||||
@ -55,21 +55,21 @@ import {ref, computed} from 'vue'
|
||||
|
||||
import Message from '@/components/misc/message.vue'
|
||||
import ShowTasks from '@/views/tasks/ShowTasks.vue'
|
||||
import ListCardGrid from '@/components/list/partials/ListCardGrid.vue'
|
||||
import ProjectCardGrid from '@/components/project/partials/ProjectCardGrid.vue'
|
||||
import AddTask from '@/components/tasks/add-task.vue'
|
||||
|
||||
import {getHistory} from '@/modules/listHistory'
|
||||
import {getHistory} from '@/modules/projectHistory'
|
||||
import {parseDateOrNull} from '@/helpers/parseDateOrNull'
|
||||
import {formatDateShort, formatDateSince} from '@/helpers/time/formatDate'
|
||||
import {useDaytimeSalutation} from '@/composables/useDaytimeSalutation'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useListStore} from '@/stores/lists'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {useConfigStore} from '@/stores/config'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
|
||||
const salutation = useDaytimeSalutation()
|
||||
|
||||
@ -77,24 +77,24 @@ const baseStore = useBaseStore()
|
||||
const authStore = useAuthStore()
|
||||
const configStore = useConfigStore()
|
||||
const namespaceStore = useNamespaceStore()
|
||||
const listStore = useListStore()
|
||||
const projectStore = useProjectStore()
|
||||
const taskStore = useTaskStore()
|
||||
|
||||
const listHistory = computed(() => {
|
||||
// If we don't check this, it tries to load the list background right after logging out
|
||||
const projectHistory = computed(() => {
|
||||
// If we don't check this, it tries to load the project background right after logging out
|
||||
if(!authStore.authenticated) {
|
||||
return []
|
||||
}
|
||||
|
||||
return getHistory()
|
||||
.map(l => listStore.getListById(l.id))
|
||||
.filter((l): l is IList => l !== null)
|
||||
.map(l => projectStore.getProjectById(l.id))
|
||||
.filter((l): l is IProject => l !== null)
|
||||
})
|
||||
|
||||
const migratorsEnabled = computed(() => configStore.availableMigrators?.length > 0)
|
||||
const hasTasks = computed(() => baseStore.hasTasks)
|
||||
const defaultNamespaceId = computed(() => namespaceStore.namespaces?.[0]?.id || 0)
|
||||
const hasLists = computed(() => namespaceStore.namespaces?.[0]?.lists.length > 0)
|
||||
const hasProjects = computed(() => namespaceStore.namespaces?.[0]?.projects.length > 0)
|
||||
const loading = computed(() => taskStore.isLoading)
|
||||
const deletionScheduledAt = computed(() => parseDateOrNull(authStore.info?.deletionScheduledAt))
|
||||
|
||||
@ -102,7 +102,7 @@ const deletionScheduledAt = computed(() => parseDateOrNull(authStore.info?.delet
|
||||
// FIXME: Should use pinia (somehow?)
|
||||
const showTasksKey = ref(0)
|
||||
|
||||
function updateTaskList() {
|
||||
function updateTaskKey() {
|
||||
showTasksKey.value++
|
||||
}
|
||||
</script>
|
||||
|
@ -15,10 +15,10 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import {toRef} from 'vue'
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
import {useSavedFilter} from '@/services/savedFilter'
|
||||
|
||||
const props = defineProps<{ listId: IList['id'] }>()
|
||||
const props = defineProps<{ projectId: IProject['id'] }>()
|
||||
|
||||
const {deleteFilter} = useSavedFilter(toRef(props, 'listId'))
|
||||
const {deleteFilter} = useSavedFilter(toRef(props, 'projectId'))
|
||||
</script>
|
||||
|
@ -5,7 +5,7 @@
|
||||
:primary-label="$t('misc.save')"
|
||||
@primary="saveFilterWithValidation"
|
||||
:tertiary="$t('misc.delete')"
|
||||
@tertiary="$router.push({ name: 'filter.settings.delete', params: { id: listId } })"
|
||||
@tertiary="$router.push({ name: 'filter.settings.delete', params: { id: projectId } })"
|
||||
>
|
||||
<form @submit.prevent="saveFilterWithValidation()">
|
||||
<div class="field">
|
||||
@ -58,13 +58,13 @@ import {toRef} from 'vue'
|
||||
|
||||
import Editor from '@/components/input/AsyncEditor'
|
||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||
import Filters from '@/components/list/partials/filters.vue'
|
||||
import Filters from '@/components/project/partials/filters.vue'
|
||||
|
||||
import {useSavedFilter} from '@/services/savedFilter'
|
||||
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
|
||||
const props = defineProps<{ listId: IList['id'] }>()
|
||||
const props = defineProps<{ projectId: IProject['id'] }>()
|
||||
|
||||
const {
|
||||
saveFilterWithValidation,
|
||||
@ -73,5 +73,5 @@ const {
|
||||
filterService,
|
||||
titleValid,
|
||||
validateTitleField,
|
||||
} = useSavedFilter(toRef(props, 'listId'))
|
||||
} = useSavedFilter(toRef(props, 'projectId'))
|
||||
</script>
|
||||
|
@ -66,7 +66,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import Editor from '@/components/input/AsyncEditor'
|
||||
import Filters from '@/components/list/partials/filters.vue'
|
||||
import Filters from '@/components/project/partials/filters.vue'
|
||||
|
||||
import {useSavedFilter} from '@/services/savedFilter'
|
||||
|
||||
|
@ -24,13 +24,13 @@
|
||||
|
||||
<section :key="`n${n.id}`" class="namespace" v-for="n in namespaces">
|
||||
<x-button
|
||||
v-if="n.id > 0 && n.lists.length > 0"
|
||||
:to="{name: 'list.create', params: {namespaceId: n.id}}"
|
||||
v-if="n.id > 0 && n.projects.length > 0"
|
||||
:to="{name: 'project.create', params: {namespaceId: n.id}}"
|
||||
class="is-pulled-right"
|
||||
variant="secondary"
|
||||
icon="plus"
|
||||
>
|
||||
{{ $t('list.create.header') }}
|
||||
{{ $t('project.create.header') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
v-if="n.isArchived"
|
||||
@ -49,15 +49,15 @@
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
<p v-if="n.lists.length === 0" class="has-text-centered has-text-grey mt-4 is-italic">
|
||||
{{ $t('namespace.noLists') }}
|
||||
<BaseButton :to="{name: 'list.create', params: {namespaceId: n.id}}">
|
||||
{{ $t('namespace.createList') }}
|
||||
<p v-if="n.projects.length === 0" class="has-text-centered has-text-grey mt-4 is-italic">
|
||||
{{ $t('namespace.noProjects') }}
|
||||
<BaseButton :to="{name: 'project.create', params: {namespaceId: n.id}}">
|
||||
{{ $t('namespace.createProject') }}
|
||||
</BaseButton>
|
||||
</p>
|
||||
|
||||
<ListCardGrid v-else
|
||||
:lists="n.lists"
|
||||
<ProjectCardGrid v-else
|
||||
:projects="n.projects"
|
||||
:show-archived="showArchived"
|
||||
/>
|
||||
</section>
|
||||
@ -70,7 +70,7 @@ import {useI18n} from 'vue-i18n'
|
||||
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
||||
import ListCardGrid from '@/components/list/partials/ListCardGrid.vue'
|
||||
import ProjectCardGrid from '@/components/project/partials/ProjectCardGrid.vue'
|
||||
|
||||
import {getNamespaceTitle} from '@/helpers/getNamespaceTitle'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
|
@ -1,31 +1,31 @@
|
||||
<template>
|
||||
<create-edit :title="$t('list.create.header')" @create="createNewList()" :primary-disabled="list.title === ''">
|
||||
<create-edit :title="$t('project.create.header')" @create="createNewProject()" :primary-disabled="project.title === ''">
|
||||
<div class="field">
|
||||
<label class="label" for="listTitle">{{ $t('list.title') }}</label>
|
||||
<label class="label" for="projectTitle">{{ $t('project.title') }}</label>
|
||||
<div
|
||||
:class="{ 'is-loading': listService.loading }"
|
||||
:class="{ 'is-loading': projectService.loading }"
|
||||
class="control"
|
||||
>
|
||||
<input
|
||||
:class="{ disabled: listService.loading }"
|
||||
@keyup.enter="createNewList()"
|
||||
:class="{ disabled: projectService.loading }"
|
||||
@keyup.enter="createNewProject()"
|
||||
@keyup.esc="$router.back()"
|
||||
class="input"
|
||||
:placeholder="$t('list.create.titlePlaceholder')"
|
||||
:placeholder="$t('project.create.titlePlaceholder')"
|
||||
type="text"
|
||||
name="listTitle"
|
||||
name="projectTitle"
|
||||
v-focus
|
||||
v-model="list.title"
|
||||
v-model="project.title"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="showError && list.title === ''">
|
||||
{{ $t('list.create.addTitleRequired') }}
|
||||
<p class="help is-danger" v-if="showError && project.title === ''">
|
||||
{{ $t('project.create.addTitleRequired') }}
|
||||
</p>
|
||||
<div class="field">
|
||||
<label class="label">{{ $t('list.color') }}</label>
|
||||
<label class="label">{{ $t('project.color') }}</label>
|
||||
<div class="control">
|
||||
<color-picker v-model="list.hexColor" />
|
||||
<color-picker v-model="project.hexColor" />
|
||||
</div>
|
||||
</div>
|
||||
</create-edit>
|
||||
@ -36,39 +36,39 @@ import {ref, reactive, shallowReactive} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {useRouter, useRoute} from 'vue-router'
|
||||
|
||||
import ListService from '@/services/list'
|
||||
import ListModel from '@/models/list'
|
||||
import ProjectService from '@/services/project'
|
||||
import ProjectModel from '@/models/project'
|
||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||
import ColorPicker from '@/components/input/ColorPicker.vue'
|
||||
|
||||
import {success} from '@/message'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
import {useListStore} from '@/stores/lists'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
useTitle(() => t('list.create.header'))
|
||||
useTitle(() => t('project.create.header'))
|
||||
|
||||
const showError = ref(false)
|
||||
const list = reactive(new ListModel())
|
||||
const listService = shallowReactive(new ListService())
|
||||
const listStore = useListStore()
|
||||
const project = reactive(new ProjectModel())
|
||||
const projectService = shallowReactive(new ProjectService())
|
||||
const projectStore = useProjectStore()
|
||||
|
||||
async function createNewList() {
|
||||
if (list.title === '') {
|
||||
async function createNewProject() {
|
||||
if (project.title === '') {
|
||||
showError.value = true
|
||||
return
|
||||
}
|
||||
showError.value = false
|
||||
|
||||
list.namespaceId = Number(route.params.namespaceId as string)
|
||||
const newList = await listStore.createList(list)
|
||||
project.namespaceId = Number(route.params.namespaceId as string)
|
||||
const newProject = await projectStore.createProject(project)
|
||||
await router.push({
|
||||
name: 'list.index',
|
||||
params: { listId: newList.id },
|
||||
name: 'project.index',
|
||||
params: { projectId: newProject.id },
|
||||
})
|
||||
success({message: t('list.create.createdSuccess') })
|
||||
success({message: t('project.create.createdSuccess') })
|
||||
}
|
||||
</script>
|
@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<ListWrapper class="list-gantt" :list-id="filters.listId" viewName="gantt">
|
||||
<ProjectWrapper class="project-gantt" :project-id="filters.projectId" viewName="gantt">
|
||||
<template #header>
|
||||
<card :has-content="false">
|
||||
<div class="gantt-options">
|
||||
<div class="field">
|
||||
<label class="label" for="range">{{ $t('list.gantt.range') }}</label>
|
||||
<label class="label" for="range">{{ $t('project.gantt.range') }}</label>
|
||||
<div class="control">
|
||||
<Foo
|
||||
ref="flatPickerEl"
|
||||
:config="flatPickerConfig"
|
||||
class="input"
|
||||
id="range"
|
||||
:placeholder="$t('list.gantt.range')"
|
||||
:placeholder="$t('project.gantt.range')"
|
||||
v-model="flatPickerDateRange"
|
||||
/>
|
||||
</div>
|
||||
@ -23,7 +23,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<fancycheckbox class="is-block" v-model="filters.showTasksWithoutDates">
|
||||
{{ $t('list.gantt.showTasksWithoutDates') }}
|
||||
{{ $t('project.gantt.showTasksWithoutDates') }}
|
||||
</fancycheckbox>
|
||||
</div>
|
||||
</card>
|
||||
@ -44,7 +44,7 @@
|
||||
</card>
|
||||
</div>
|
||||
</template>
|
||||
</ListWrapper>
|
||||
</ProjectWrapper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -57,7 +57,7 @@ import {useBaseStore} from '@/stores/base'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
|
||||
import Foo from '@/components/misc/flatpickr/Flatpickr.vue'
|
||||
import ListWrapper from '@/components/list/ListWrapper.vue'
|
||||
import ProjectWrapper from '@/components/project/ProjectWrapper.vue'
|
||||
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
||||
import TaskForm from '@/components/tasks/TaskForm.vue'
|
||||
|
||||
@ -75,7 +75,7 @@ const GanttChart = createAsyncComponent(() => import('@/components/tasks/GanttCh
|
||||
const props = defineProps<{route: RouteLocationNormalized}>()
|
||||
|
||||
const baseStore = useBaseStore()
|
||||
const canWrite = computed(() => baseStore.currentList.maxRight > RIGHTS.READ)
|
||||
const canWrite = computed(() => baseStore.currentProject.maxRight > RIGHTS.READ)
|
||||
|
||||
const {route} = toRefs(props)
|
||||
const {
|
||||
@ -101,7 +101,7 @@ const defaultTaskEndDate: DateISO = new Date(new Date(
|
||||
async function addGanttTask(title: ITask['title']) {
|
||||
return await addTask({
|
||||
title,
|
||||
listId: filters.value.listId,
|
||||
projectId: filters.value.projectId,
|
||||
startDate: defaultTaskStartDate,
|
||||
endDate: defaultTaskEndDate,
|
||||
})
|
@ -3,11 +3,11 @@
|
||||
@close="$router.back()"
|
||||
>
|
||||
<card
|
||||
:title="list.title"
|
||||
:title="project.title"
|
||||
>
|
||||
<div class="has-text-left" v-html="htmlDescription" v-if="htmlDescription !== ''"></div>
|
||||
<p v-else class="is-italic">
|
||||
{{ $t('list.noDescriptionAvailable') }}
|
||||
{{ $t('project.noDescriptionAvailable') }}
|
||||
</p>
|
||||
</card>
|
||||
</modal>
|
||||
@ -19,19 +19,19 @@ import {setupMarkdownRenderer} from '@/helpers/markdownRenderer'
|
||||
import {marked} from 'marked'
|
||||
import DOMPurify from 'dompurify'
|
||||
import {createRandomID} from '@/helpers/randomId'
|
||||
import {useListStore} from '@/stores/lists'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
|
||||
const props = defineProps({
|
||||
listId: {
|
||||
projectId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const listStore = useListStore()
|
||||
const list = computed(() => listStore.getListById(props.listId))
|
||||
const projectStore = useProjectStore()
|
||||
const project = computed(() => projectStore.getProjectById(props.projectId))
|
||||
const htmlDescription = computed(() => {
|
||||
const description = list.value?.description || ''
|
||||
const description = project.value?.description || ''
|
||||
if (description === '') {
|
||||
return ''
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<ListWrapper
|
||||
class="list-kanban"
|
||||
:list-id="listId"
|
||||
<ProjectWrapper
|
||||
class="project-kanban"
|
||||
:project-id="projectId"
|
||||
viewName="kanban"
|
||||
>
|
||||
<template #header>
|
||||
<div class="filter-container" v-if="!isSavedFilter(list)">
|
||||
<div class="filter-container" v-if="!isSavedFilter(project)">
|
||||
<div class="items">
|
||||
<filter-popup v-model="params" />
|
||||
</div>
|
||||
@ -39,7 +39,7 @@
|
||||
<span
|
||||
v-if="bucket.isDoneBucket"
|
||||
class="icon is-small has-text-success mr-2"
|
||||
v-tooltip="$t('list.kanban.doneBucketHint')"
|
||||
v-tooltip="$t('project.kanban.doneBucketHint')"
|
||||
>
|
||||
<icon icon="check-double"/>
|
||||
</span>
|
||||
@ -90,29 +90,29 @@
|
||||
</div>
|
||||
<template v-else>
|
||||
{{
|
||||
$t('list.kanban.limit', {limit: bucket.limit > 0 ? bucket.limit : $t('list.kanban.noLimit')})
|
||||
$t('project.kanban.limit', {limit: bucket.limit > 0 ? bucket.limit : $t('project.kanban.noLimit')})
|
||||
}}
|
||||
</template>
|
||||
</dropdown-item>
|
||||
<dropdown-item
|
||||
@click.stop="toggleDoneBucket(bucket)"
|
||||
v-tooltip="$t('list.kanban.doneBucketHintExtended')"
|
||||
v-tooltip="$t('project.kanban.doneBucketHintExtended')"
|
||||
>
|
||||
<span class="icon is-small" :class="{'has-text-success': bucket.isDoneBucket}">
|
||||
<icon icon="check-double"/>
|
||||
</span>
|
||||
{{ $t('list.kanban.doneBucket') }}
|
||||
{{ $t('project.kanban.doneBucket') }}
|
||||
</dropdown-item>
|
||||
<dropdown-item
|
||||
@click.stop="() => collapseBucket(bucket)"
|
||||
>
|
||||
{{ $t('list.kanban.collapse') }}
|
||||
{{ $t('project.kanban.collapse') }}
|
||||
</dropdown-item>
|
||||
<dropdown-item
|
||||
:class="{'is-disabled': buckets.length <= 1}"
|
||||
@click.stop="() => deleteBucketModal(bucket.id)"
|
||||
class="has-text-danger"
|
||||
v-tooltip="buckets.length <= 1 ? $t('list.kanban.deleteLast') : ''"
|
||||
v-tooltip="buckets.length <= 1 ? $t('project.kanban.deleteLast') : ''"
|
||||
>
|
||||
<span class="icon is-small">
|
||||
<icon icon="trash-alt"/>
|
||||
@ -146,14 +146,14 @@
|
||||
@focusin="() => newTaskInputFocused = true"
|
||||
@keyup.enter="addTaskToBucket(bucket.id)"
|
||||
@keyup.esc="toggleShowNewTaskInput(bucket.id)"
|
||||
:placeholder="$t('list.kanban.addTaskPlaceholder')"
|
||||
:placeholder="$t('project.kanban.addTaskPlaceholder')"
|
||||
type="text"
|
||||
v-focus.always
|
||||
v-model="newTaskText"
|
||||
/>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="newTaskError[bucket.id] && newTaskText === ''">
|
||||
{{ $t('list.create.addTitleRequired') }}
|
||||
{{ $t('project.create.addTitleRequired') }}
|
||||
</p>
|
||||
</div>
|
||||
<x-button
|
||||
@ -164,7 +164,7 @@
|
||||
icon="plus"
|
||||
variant="secondary"
|
||||
>
|
||||
{{ bucket.tasks.length === 0 ? $t('list.kanban.addTask') : $t('list.kanban.addAnotherTask') }}
|
||||
{{ bucket.tasks.length === 0 ? $t('project.kanban.addTask') : $t('project.kanban.addAnotherTask') }}
|
||||
</x-button>
|
||||
</div>
|
||||
</template>
|
||||
@ -187,7 +187,7 @@
|
||||
@keyup.enter="createNewBucket"
|
||||
@keyup.esc="($event.target as HTMLInputElement).blur()"
|
||||
class="input"
|
||||
:placeholder="$t('list.kanban.addBucketPlaceholder')"
|
||||
:placeholder="$t('project.kanban.addBucketPlaceholder')"
|
||||
type="text"
|
||||
v-focus.always
|
||||
v-if="showNewBucketInput"
|
||||
@ -201,7 +201,7 @@
|
||||
variant="secondary"
|
||||
icon="plus"
|
||||
>
|
||||
{{ $t('list.kanban.addBucket') }}
|
||||
{{ $t('project.kanban.addBucket') }}
|
||||
</x-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -211,16 +211,16 @@
|
||||
@close="showBucketDeleteModal = false"
|
||||
@submit="deleteBucket()"
|
||||
>
|
||||
<template #header><span>{{ $t('list.kanban.deleteHeaderBucket') }}</span></template>
|
||||
<template #header><span>{{ $t('project.kanban.deleteHeaderBucket') }}</span></template>
|
||||
|
||||
<template #text>
|
||||
<p>{{ $t('list.kanban.deleteBucketText1') }}<br/>
|
||||
{{ $t('list.kanban.deleteBucketText2') }}</p>
|
||||
<p>{{ $t('project.kanban.deleteBucketText1') }}<br/>
|
||||
{{ $t('project.kanban.deleteBucketText2') }}</p>
|
||||
</template>
|
||||
</modal>
|
||||
</div>
|
||||
</template>
|
||||
</ListWrapper>
|
||||
</ProjectWrapper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -233,15 +233,15 @@ import {RIGHTS as Rights} from '@/constants/rights'
|
||||
import BucketModel from '@/models/bucket'
|
||||
|
||||
import type {IBucket} from '@/modelTypes/IBucket'
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
import {useKanbanStore} from '@/stores/kanban'
|
||||
|
||||
import ListWrapper from '@/components/list/ListWrapper.vue'
|
||||
import FilterPopup from '@/components/list/partials/filter-popup.vue'
|
||||
import ProjectWrapper from '@/components/project/ProjectWrapper.vue'
|
||||
import FilterPopup from '@/components/project/partials/filter-popup.vue'
|
||||
import KanbanCard from '@/components/tasks/partials/kanban-card.vue'
|
||||
import Dropdown from '@/components/misc/dropdown.vue'
|
||||
import DropdownItem from '@/components/misc/dropdown-item.vue'
|
||||
@ -264,8 +264,8 @@ const DRAG_OPTIONS = {
|
||||
const MIN_SCROLL_HEIGHT_PERCENT = 0.25
|
||||
|
||||
const props = defineProps({
|
||||
listId: {
|
||||
type: Number as PropType<IList['id']>,
|
||||
projectId: {
|
||||
type: Number as PropType<IProject['id']>,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
@ -311,7 +311,7 @@ const params = ref({
|
||||
const getTaskDraggableTaskComponentData = computed(() => (bucket: IBucket) => {
|
||||
return {
|
||||
ref: (el: HTMLElement) => setTaskContainerRef(bucket.id, el),
|
||||
onScroll: (event: Event) => handleTaskContainerScroll(bucket.id, bucket.listId, event.target as HTMLElement),
|
||||
onScroll: (event: Event) => handleTaskContainerScroll(bucket.id, bucket.projectId, event.target as HTMLElement),
|
||||
type: 'transition-group',
|
||||
name: !drag.value ? 'move-card' : null,
|
||||
class: [
|
||||
@ -330,8 +330,8 @@ const bucketDraggableComponentData = computed(() => ({
|
||||
],
|
||||
}))
|
||||
|
||||
const canWrite = computed(() => baseStore.currentList.maxRight > Rights.READ)
|
||||
const list = computed(() => baseStore.currentList)
|
||||
const canWrite = computed(() => baseStore.currentProject.maxRight > Rights.READ)
|
||||
const project = computed(() => baseStore.currentProject)
|
||||
|
||||
const buckets = computed(() => kanbanStore.buckets)
|
||||
const loading = computed(() => kanbanStore.isLoading)
|
||||
@ -340,15 +340,15 @@ const taskLoading = computed(() => taskStore.isLoading)
|
||||
|
||||
watch(
|
||||
() => ({
|
||||
listId: props.listId,
|
||||
projectId: props.projectId,
|
||||
params: params.value,
|
||||
}),
|
||||
({listId, params}) => {
|
||||
if (listId === undefined) {
|
||||
({projectId, params}) => {
|
||||
if (projectId === undefined) {
|
||||
return
|
||||
}
|
||||
collapsedBuckets.value = getCollapsedBucketState(listId)
|
||||
kanbanStore.loadBucketsForList({listId, params})
|
||||
collapsedBuckets.value = getCollapsedBucketState(projectId)
|
||||
kanbanStore.loadBucketsForProject({projectId, params})
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
@ -361,7 +361,7 @@ function setTaskContainerRef(id: IBucket['id'], el: HTMLElement) {
|
||||
taskContainerRefs.value[id] = el
|
||||
}
|
||||
|
||||
function handleTaskContainerScroll(id: IBucket['id'], listId: IList['id'], el: HTMLElement) {
|
||||
function handleTaskContainerScroll(id: IBucket['id'], projectId: IProject['id'], el: HTMLElement) {
|
||||
if (!el) {
|
||||
return
|
||||
}
|
||||
@ -372,7 +372,7 @@ function handleTaskContainerScroll(id: IBucket['id'], listId: IList['id'], el: H
|
||||
}
|
||||
|
||||
kanbanStore.loadNextTasksForBucket({
|
||||
listId: listId,
|
||||
projectId: projectId,
|
||||
params: params.value,
|
||||
bucketId: id,
|
||||
})
|
||||
@ -462,7 +462,7 @@ async function addTaskToBucket(bucketId: IBucket['id']) {
|
||||
const task = await taskStore.createNewTask({
|
||||
title: newTaskText.value,
|
||||
bucketId,
|
||||
listId: props.listId,
|
||||
projectId: props.projectId,
|
||||
})
|
||||
newTaskText.value = ''
|
||||
kanbanStore.addTaskToBucket(task)
|
||||
@ -484,7 +484,7 @@ async function createNewBucket() {
|
||||
|
||||
await kanbanStore.createBucket(new BucketModel({
|
||||
title: newBucketTitle.value,
|
||||
listId: props.listId,
|
||||
projectId: props.projectId,
|
||||
}))
|
||||
newBucketTitle.value = ''
|
||||
showNewBucketInput.value = false
|
||||
@ -504,11 +504,11 @@ async function deleteBucket() {
|
||||
await kanbanStore.deleteBucket({
|
||||
bucket: new BucketModel({
|
||||
id: bucketToDelete.value,
|
||||
listId: props.listId,
|
||||
projectId: props.projectId,
|
||||
}),
|
||||
params: params.value,
|
||||
})
|
||||
success({message: t('list.kanban.deleteBucketSuccess')})
|
||||
success({message: t('project.kanban.deleteBucketSuccess')})
|
||||
} finally {
|
||||
showBucketDeleteModal.value = false
|
||||
}
|
||||
@ -562,7 +562,7 @@ async function setBucketLimit(bucketId: IBucket['id'], limit: number) {
|
||||
...kanbanStore.getBucketById(bucketId),
|
||||
limit,
|
||||
})
|
||||
success({message: t('list.kanban.bucketLimitSavedSuccess')})
|
||||
success({message: t('project.kanban.bucketLimitSavedSuccess')})
|
||||
}
|
||||
|
||||
function shouldAcceptDrop(bucket: IBucket) {
|
||||
@ -586,12 +586,12 @@ async function toggleDoneBucket(bucket: IBucket) {
|
||||
...bucket,
|
||||
isDoneBucket: !bucket.isDoneBucket,
|
||||
})
|
||||
success({message: t('list.kanban.doneBucketSavedSuccess')})
|
||||
success({message: t('project.kanban.doneBucketSavedSuccess')})
|
||||
}
|
||||
|
||||
function collapseBucket(bucket: IBucket) {
|
||||
collapsedBuckets.value[bucket.id] = true
|
||||
saveCollapsedBucketState(props.listId, collapsedBuckets.value)
|
||||
saveCollapsedBucketState(props.projectId, collapsedBuckets.value)
|
||||
}
|
||||
|
||||
function unCollapseBucket(bucket: IBucket) {
|
||||
@ -600,7 +600,7 @@ function unCollapseBucket(bucket: IBucket) {
|
||||
}
|
||||
|
||||
collapsedBuckets.value[bucket.id] = false
|
||||
saveCollapsedBucketState(props.listId, collapsedBuckets.value)
|
||||
saveCollapsedBucketState(props.projectId, collapsedBuckets.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -615,7 +615,7 @@ $crazy-height-calculation-tasks: '#{$crazy-height-calculation} - 1rem - 2.5rem -
|
||||
$filter-container-height: '1rem - #{$switch-view-height}';
|
||||
|
||||
// FIXME:
|
||||
.app-content.list\.kanban, .app-content.task\.detail {
|
||||
.app-content.project\.kanban, .app-content.task\.detail {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<ListWrapper class="list-list" :list-id="listId" viewName="list">
|
||||
<ProjectWrapper class="project-list" :project-id="projectId" viewName="project">
|
||||
<template #header>
|
||||
<div
|
||||
class="filter-container"
|
||||
v-if="!isSavedFilter(list)"
|
||||
v-if="!isSavedFilter(project)"
|
||||
>
|
||||
<div class="items">
|
||||
<div class="search">
|
||||
@ -54,7 +54,7 @@
|
||||
>
|
||||
<card :padding="false" :has-content="false" class="has-overflow">
|
||||
<add-task
|
||||
v-if="!list.isArchived && canWrite"
|
||||
v-if="!project.isArchived && canWrite"
|
||||
class="list-view__add-task"
|
||||
ref="addTaskRef"
|
||||
:default-position="firstNewPosition"
|
||||
@ -62,9 +62,9 @@
|
||||
/>
|
||||
|
||||
<nothing v-if="ctaVisible && tasks.length === 0 && !loading">
|
||||
{{ $t('list.list.empty') }}
|
||||
{{ $t('project.list.empty') }}
|
||||
<ButtonLink @click="focusNewTaskInput()">
|
||||
{{ $t('list.list.newTaskCta') }}
|
||||
{{ $t('project.list.newTaskCta') }}
|
||||
</ButtonLink>
|
||||
</nothing>
|
||||
|
||||
@ -89,10 +89,10 @@
|
||||
}"
|
||||
>
|
||||
<template #item="{element: t}">
|
||||
<single-task-in-list
|
||||
<single-task-in-project
|
||||
:show-list-color="false"
|
||||
:disabled="!canWrite"
|
||||
:can-mark-as-done="canWrite || isSavedFilter(list)"
|
||||
:can-mark-as-done="canWrite || isSavedFilter(project)"
|
||||
:the-task="t"
|
||||
@taskUpdated="updateTasks"
|
||||
>
|
||||
@ -101,7 +101,7 @@
|
||||
<icon icon="grip-lines"/>
|
||||
</span>
|
||||
</template>
|
||||
</single-task-in-list>
|
||||
</single-task-in-project>
|
||||
</template>
|
||||
</draggable>
|
||||
|
||||
@ -112,7 +112,7 @@
|
||||
</card>
|
||||
</div>
|
||||
</template>
|
||||
</ListWrapper>
|
||||
</ProjectWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -124,14 +124,14 @@ import {ref, computed, toRef, nextTick, onMounted, type PropType} from 'vue'
|
||||
import draggable from 'zhyswan-vuedraggable'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
|
||||
import ListWrapper from '@/components/list/ListWrapper.vue'
|
||||
import ProjectWrapper from '@/components/project/ProjectWrapper.vue'
|
||||
import ButtonLink from '@/components/misc/ButtonLink.vue'
|
||||
import AddTask from '@/components/tasks/add-task.vue'
|
||||
import SingleTaskInList from '@/components/tasks/partials/singleTaskInList.vue'
|
||||
import FilterPopup from '@/components/list/partials/filter-popup.vue'
|
||||
import SingleTaskInProject from '@/components/tasks/partials/singleTaskInProject.vue'
|
||||
import FilterPopup from '@/components/project/partials/filter-popup.vue'
|
||||
import Nothing from '@/components/misc/nothing.vue'
|
||||
import Pagination from '@/components/misc/pagination.vue'
|
||||
import {ALPHABETICAL_SORT} from '@/components/list/partials/filters.vue'
|
||||
import {ALPHABETICAL_SORT} from '@/components/project/partials/filters.vue'
|
||||
|
||||
import {useTaskList} from '@/composables/useTaskList'
|
||||
import {RIGHTS as Rights} from '@/constants/rights'
|
||||
@ -142,7 +142,7 @@ import {isSavedFilter} from '@/services/savedFilter'
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
|
||||
function sortTasks(tasks: ITask[]) {
|
||||
if (tasks === null || Array.isArray(tasks) && tasks.length === 0) {
|
||||
@ -163,8 +163,8 @@ function sortTasks(tasks: ITask[]) {
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
listId: {
|
||||
type: Number as PropType<IList['id']>,
|
||||
projectId: {
|
||||
type: Number as PropType<IProject['id']>,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
@ -187,7 +187,7 @@ const {
|
||||
searchTerm,
|
||||
params,
|
||||
sortByParam,
|
||||
} = useTaskList(toRef(props, 'listId'), {position: 'asc' })
|
||||
} = useTaskList(toRef(props, 'projectId'), {position: 'asc' })
|
||||
|
||||
|
||||
const isAlphabeticalSorting = computed(() => {
|
||||
@ -204,10 +204,10 @@ const firstNewPosition = computed(() => {
|
||||
|
||||
const taskStore = useTaskStore()
|
||||
const baseStore = useBaseStore()
|
||||
const list = computed(() => baseStore.currentList)
|
||||
const project = computed(() => baseStore.currentProject)
|
||||
|
||||
const canWrite = computed(() => {
|
||||
return list.value.maxRight > Rights.READ && list.value.id > 0
|
||||
return project.value.maxRight > Rights.READ && project.value.id > 0
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
@ -225,7 +225,7 @@ function searchTasks() {
|
||||
}
|
||||
|
||||
router.push({
|
||||
name: 'list.list',
|
||||
name: 'project.list',
|
||||
query: {search: searchTerm.value},
|
||||
})
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<ListWrapper class="list-table" :list-id="listId" viewName="table">
|
||||
<ProjectWrapper class="project-table" :project-id="projectId" viewName="table">
|
||||
<template #header>
|
||||
<div class="filter-container">
|
||||
<div class="items">
|
||||
@ -10,7 +10,7 @@
|
||||
icon="th"
|
||||
variant="secondary"
|
||||
>
|
||||
{{ $t('list.table.columns') }}
|
||||
{{ $t('project.table.columns') }}
|
||||
</x-button>
|
||||
</template>
|
||||
<template #content="{isOpen}">
|
||||
@ -176,7 +176,7 @@
|
||||
</card>
|
||||
</div>
|
||||
</template>
|
||||
</ListWrapper>
|
||||
</ProjectWrapper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -184,7 +184,7 @@ import {toRef, computed, type Ref} from 'vue'
|
||||
|
||||
import {useStorage} from '@vueuse/core'
|
||||
|
||||
import ListWrapper from '@/components/list/ListWrapper.vue'
|
||||
import ProjectWrapper from '@/components/project/ProjectWrapper.vue'
|
||||
import Done from '@/components/misc/Done.vue'
|
||||
import User from '@/components/misc/user.vue'
|
||||
import PriorityLabel from '@/components/tasks/partials/priorityLabel.vue'
|
||||
@ -192,7 +192,7 @@ import Labels from '@/components/tasks/partials/labels.vue'
|
||||
import DateTableCell from '@/components/tasks/partials/date-table-cell.vue'
|
||||
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
||||
import Sort from '@/components/tasks/partials/sort.vue'
|
||||
import FilterPopup from '@/components/list/partials/filter-popup.vue'
|
||||
import FilterPopup from '@/components/project/partials/filter-popup.vue'
|
||||
import Pagination from '@/components/misc/pagination.vue'
|
||||
import Popup from '@/components/misc/popup.vue'
|
||||
|
||||
@ -216,7 +216,7 @@ const ACTIVE_COLUMNS_DEFAULT = {
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
listId: {
|
||||
projectId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
@ -229,7 +229,7 @@ const SORT_BY_DEFAULT: SortBy = {
|
||||
const activeColumns = useStorage('tableViewColumns', {...ACTIVE_COLUMNS_DEFAULT})
|
||||
const sortBy = useStorage<SortBy>('tableViewSortBy', {...SORT_BY_DEFAULT})
|
||||
|
||||
const taskList = useTaskList(toRef(props, 'listId'), sortBy.value)
|
||||
const taskList = useTaskList(toRef(props, 'projectId'), sortBy.value)
|
||||
|
||||
const {
|
||||
loading,
|
@ -7,7 +7,7 @@ import {parseBooleanProp} from '@/helpers/time/parseBooleanProp'
|
||||
import {useRouteFilters} from '@/composables/useRouteFilters'
|
||||
import {useGanttTaskList} from './useGanttTaskList'
|
||||
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
import type {GetAllTasksParams} from '@/services/taskCollection'
|
||||
|
||||
import type {DateISO} from '@/types/DateISO'
|
||||
@ -15,7 +15,7 @@ import type {DateKebab} from '@/types/DateKebab'
|
||||
|
||||
// convenient internal filter object
|
||||
export interface GanttFilters {
|
||||
listId: IList['id']
|
||||
projectId: IProject['id']
|
||||
dateFrom: DateISO
|
||||
dateTo: DateISO
|
||||
showTasksWithoutDates: boolean
|
||||
@ -40,7 +40,7 @@ function getDefaultDateTo() {
|
||||
function ganttRouteToFilters(route: Partial<RouteLocationNormalized>): GanttFilters {
|
||||
const ganttRoute = route
|
||||
return {
|
||||
listId: Number(ganttRoute.params?.listId),
|
||||
projectId: Number(ganttRoute.params?.projectId),
|
||||
dateFrom: parseDateProp(ganttRoute.query?.dateFrom as DateKebab) || getDefaultDateFrom(),
|
||||
dateTo: parseDateProp(ganttRoute.query?.dateTo as DateKebab) || getDefaultDateTo(),
|
||||
showTasksWithoutDates: parseBooleanProp(ganttRoute.query?.showTasksWithoutDates as string) || DEFAULT_SHOW_TASKS_WITHOUT_DATES,
|
||||
@ -48,7 +48,7 @@ function ganttRouteToFilters(route: Partial<RouteLocationNormalized>): GanttFilt
|
||||
}
|
||||
|
||||
function ganttGetDefaultFilters(route: Partial<RouteLocationNormalized>): GanttFilters {
|
||||
return ganttRouteToFilters({params: {listId: route.params?.listId as string}})
|
||||
return ganttRouteToFilters({params: {projectId: route.params?.projectId as string}})
|
||||
}
|
||||
|
||||
// FIXME: use zod for this
|
||||
@ -69,8 +69,8 @@ function ganttFiltersToRoute(filters: GanttFilters): RouteLocationRaw {
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'list.gantt',
|
||||
params: {listId: filters.listId},
|
||||
name: 'project.gantt',
|
||||
params: {projectId: filters.projectId},
|
||||
query,
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ export function useGanttTaskList<F extends Filters>(
|
||||
const tasks = ref<Map<ITask['id'], ITask>>(new Map())
|
||||
|
||||
async function fetchTasks(params: GetAllTasksParams, page = 1): Promise<ITask[]> {
|
||||
const tasks = await taskCollectionService.getAll({listId: filters.value.listId}, params, page) as ITask[]
|
||||
const tasks = await taskCollectionService.getAll({projectId: filters.value.projectId}, params, page) as ITask[]
|
||||
if (options.loadAll && page < taskCollectionService.totalPages) {
|
||||
const nextTasks = await fetchTasks(params, page + 1)
|
||||
return tasks.concat(nextTasks)
|
@ -1,18 +1,18 @@
|
||||
<template>
|
||||
<modal
|
||||
@close="$router.back()"
|
||||
@submit="archiveList()"
|
||||
@submit="archiveProject()"
|
||||
>
|
||||
<template #header><span>{{ list.isArchived ? $t('list.archive.unarchive') : $t('list.archive.archive') }}</span></template>
|
||||
<template #header><span>{{ project.isArchived ? $t('project.archive.unarchive') : $t('project.archive.archive') }}</span></template>
|
||||
|
||||
<template #text>
|
||||
<p>{{ list.isArchived ? $t('list.archive.unarchiveText') : $t('list.archive.archiveText') }}</p>
|
||||
<p>{{ project.isArchived ? $t('project.archive.unarchiveText') : $t('project.archive.archiveText') }}</p>
|
||||
</template>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {name: 'list-setting-archive'}
|
||||
export default {name: 'project-setting-archive'}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -24,24 +24,24 @@ import {success} from '@/message'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useListStore} from '@/stores/lists'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
const listStore = useListStore()
|
||||
const projectStore = useProjectStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const list = computed(() => listStore.getListById(route.params.listId))
|
||||
useTitle(() => t('list.archive.title', {list: list.value.title}))
|
||||
const project = computed(() => projectStore.getProjectById(route.params.projectId))
|
||||
useTitle(() => t('project.archive.title', {project: project.value.title}))
|
||||
|
||||
async function archiveList() {
|
||||
async function archiveProject() {
|
||||
try {
|
||||
const newList = await listStore.updateList({
|
||||
...list.value,
|
||||
isArchived: !list.value.isArchived,
|
||||
const newProject = await projectStore.updateProject({
|
||||
...project.value,
|
||||
isArchived: !project.value.isArchived,
|
||||
})
|
||||
useBaseStore().setCurrentList(newList)
|
||||
success({message: t('list.archive.success')})
|
||||
useBaseStore().setCurrentProject(newProject)
|
||||
success({message: t('project.archive.success')})
|
||||
} finally {
|
||||
router.back()
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<create-edit
|
||||
v-if="uploadBackgroundEnabled || unsplashBackgroundEnabled"
|
||||
:title="$t('list.background.title')"
|
||||
:title="$t('project.background.title')"
|
||||
:loading="backgroundService.loading"
|
||||
class="list-background-setting"
|
||||
class="project-background-setting"
|
||||
:wide="true"
|
||||
>
|
||||
<div class="mb-4" v-if="uploadBackgroundEnabled">
|
||||
@ -19,7 +19,7 @@
|
||||
@click="backgroundUploadInput?.click()"
|
||||
variant="primary"
|
||||
>
|
||||
{{ $t('list.background.upload') }}
|
||||
{{ $t('project.background.upload') }}
|
||||
</x-button>
|
||||
</div>
|
||||
<template v-if="unsplashBackgroundEnabled">
|
||||
@ -27,13 +27,13 @@
|
||||
:class="{'is-loading': backgroundService.loading}"
|
||||
@keyup="debounceNewBackgroundSearch()"
|
||||
class="input is-expanded"
|
||||
:placeholder="$t('list.background.searchPlaceholder')"
|
||||
:placeholder="$t('project.background.searchPlaceholder')"
|
||||
type="text"
|
||||
v-model="backgroundSearchTerm"
|
||||
/>
|
||||
|
||||
<p class="unsplash-credit">
|
||||
<BaseButton class="unsplash-credit__link" href="https://unsplash.com">{{ $t('list.background.poweredByUnsplash') }}</BaseButton>
|
||||
<BaseButton class="unsplash-credit__link" href="https://unsplash.com">{{ $t('project.background.poweredByUnsplash') }}</BaseButton>
|
||||
</p>
|
||||
|
||||
<ul class="image-search__result-list">
|
||||
@ -69,7 +69,7 @@
|
||||
:shadow="false"
|
||||
variant="secondary"
|
||||
>
|
||||
{{ backgroundService.loading ? $t('misc.loading') : $t('list.background.loadMore') }}
|
||||
{{ backgroundService.loading ? $t('misc.loading') : $t('project.background.loadMore') }}
|
||||
</x-button>
|
||||
</template>
|
||||
|
||||
@ -81,7 +81,7 @@
|
||||
class="is-danger"
|
||||
@click.prevent.stop="removeBackground"
|
||||
>
|
||||
{{ $t('list.background.remove') }}
|
||||
{{ $t('project.background.remove') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
variant="secondary"
|
||||
@ -94,7 +94,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default { name: 'list-setting-background' }
|
||||
export default { name: 'project-setting-background' }
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -107,13 +107,13 @@ import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import CustomTransition from '@/components/misc/CustomTransition.vue'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useListStore} from '@/stores/lists'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
import {useConfigStore} from '@/stores/config'
|
||||
|
||||
import BackgroundUnsplashService from '@/services/backgroundUnsplash'
|
||||
import BackgroundUploadService from '@/services/backgroundUpload'
|
||||
import ListService from '@/services/list'
|
||||
import ProjectService from '@/services/project'
|
||||
import type BackgroundImageModel from '@/models/backgroundImage'
|
||||
|
||||
import {getBlobFromBlurHash} from '@/helpers/getBlobFromBlurHash'
|
||||
@ -129,7 +129,7 @@ const baseStore = useBaseStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
useTitle(() => t('list.background.title'))
|
||||
useTitle(() => t('project.background.title'))
|
||||
|
||||
const backgroundService = shallowReactive(new BackgroundUnsplashService())
|
||||
const backgroundSearchTerm = ref('')
|
||||
@ -144,14 +144,14 @@ const debounceNewBackgroundSearch = debounce(newBackgroundSearch, SEARCH_DEBOUNC
|
||||
})
|
||||
|
||||
const backgroundUploadService = ref(new BackgroundUploadService())
|
||||
const listService = ref(new ListService())
|
||||
const listStore = useListStore()
|
||||
const projectService = ref(new ProjectService())
|
||||
const projectStore = useProjectStore()
|
||||
const namespaceStore = useNamespaceStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
const unsplashBackgroundEnabled = computed(() => configStore.enabledBackgroundProviders.includes('unsplash'))
|
||||
const uploadBackgroundEnabled = computed(() => configStore.enabledBackgroundProviders.includes('upload'))
|
||||
const currentList = computed(() => baseStore.currentList)
|
||||
const currentProject = computed(() => baseStore.currentProject)
|
||||
const hasBackground = computed(() => baseStore.background !== null)
|
||||
|
||||
// Show the default collection of backgrounds
|
||||
@ -190,14 +190,14 @@ async function setBackground(backgroundId: string) {
|
||||
return
|
||||
}
|
||||
|
||||
const list = await backgroundService.update({
|
||||
const project = await backgroundService.update({
|
||||
id: backgroundId,
|
||||
listId: route.params.listId,
|
||||
projectId: route.params.projectId,
|
||||
})
|
||||
await baseStore.handleSetCurrentList({list, forceUpdate: true})
|
||||
namespaceStore.setListInNamespaceById(list)
|
||||
listStore.setList(list)
|
||||
success({message: t('list.background.success')})
|
||||
await baseStore.handleSetCurrentProject({project, forceUpdate: true})
|
||||
namespaceStore.setProjectInNamespaceById(project)
|
||||
projectStore.setProject(project)
|
||||
success({message: t('project.background.success')})
|
||||
}
|
||||
|
||||
const backgroundUploadInput = ref<HTMLInputElement | null>(null)
|
||||
@ -206,22 +206,22 @@ async function uploadBackground() {
|
||||
return
|
||||
}
|
||||
|
||||
const list = await backgroundUploadService.value.create(
|
||||
route.params.listId,
|
||||
const project = await backgroundUploadService.value.create(
|
||||
route.params.projectId,
|
||||
backgroundUploadInput.value?.files[0],
|
||||
)
|
||||
await baseStore.handleSetCurrentList({list, forceUpdate: true})
|
||||
namespaceStore.setListInNamespaceById(list)
|
||||
listStore.setList(list)
|
||||
success({message: t('list.background.success')})
|
||||
await baseStore.handleSetCurrentProject({project, forceUpdate: true})
|
||||
namespaceStore.setProjectInNamespaceById(project)
|
||||
projectStore.setProject(project)
|
||||
success({message: t('project.background.success')})
|
||||
}
|
||||
|
||||
async function removeBackground() {
|
||||
const list = await listService.value.removeBackground(currentList.value)
|
||||
await baseStore.handleSetCurrentList({list, forceUpdate: true})
|
||||
namespaceStore.setListInNamespaceById(list)
|
||||
listStore.setList(list)
|
||||
success({message: t('list.background.removeSuccess')})
|
||||
const project = await projectService.value.removeBackground(currentProject.value)
|
||||
await baseStore.handleSetCurrentProject({project, forceUpdate: true})
|
||||
namespaceStore.setProjectInNamespaceById(project)
|
||||
projectStore.setProject(project)
|
||||
success({message: t('project.background.removeSuccess')})
|
||||
router.back()
|
||||
}
|
||||
</script>
|
@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<modal
|
||||
@close="$router.back()"
|
||||
@submit="deleteList()"
|
||||
@submit="deleteProject()"
|
||||
>
|
||||
<template #header><span>{{ $t('list.delete.header') }}</span></template>
|
||||
<template #header><span>{{ $t('project.delete.header') }}</span></template>
|
||||
|
||||
<template #text>
|
||||
<p>
|
||||
{{ $t('list.delete.text1') }}
|
||||
{{ $t('project.delete.text1') }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong v-if="totalTasks !== null" class="has-text-white">
|
||||
{{
|
||||
totalTasks > 0 ? $t('list.delete.tasksToDelete', {count: totalTasks}) : $t('list.delete.noTasksToDelete')
|
||||
totalTasks > 0 ? $t('project.delete.tasksToDelete', {count: totalTasks}) : $t('project.delete.noTasksToDelete')
|
||||
}}
|
||||
</strong>
|
||||
<Loading v-else class="is-loading-small"/>
|
||||
@ -34,39 +34,39 @@ import {useRoute, useRouter} from 'vue-router'
|
||||
import {success} from '@/message'
|
||||
import TaskCollectionService from '@/services/taskCollection'
|
||||
import Loading from '@/components/misc/loading.vue'
|
||||
import {useListStore} from '@/stores/lists'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
const listStore = useListStore()
|
||||
const projectStore = useProjectStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const totalTasks = ref<number | null>(null)
|
||||
|
||||
const list = computed(() => listStore.getListById(route.params.listId))
|
||||
const project = computed(() => projectStore.getProjectById(route.params.projectId))
|
||||
|
||||
watchEffect(
|
||||
() => {
|
||||
if (!route.params.listId) {
|
||||
if (!route.params.projectId) {
|
||||
return
|
||||
}
|
||||
|
||||
const taskCollectionService = new TaskCollectionService()
|
||||
taskCollectionService.getAll({listId: route.params.listId}).then(() => {
|
||||
taskCollectionService.getAll({projectId: route.params.projectId}).then(() => {
|
||||
totalTasks.value = taskCollectionService.totalPages * taskCollectionService.resultCount
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
useTitle(() => t('list.delete.title', {list: list?.value?.title}))
|
||||
useTitle(() => t('project.delete.title', {project: project?.value?.title}))
|
||||
|
||||
async function deleteList() {
|
||||
if (!list.value) {
|
||||
async function deleteProject() {
|
||||
if (!project.value) {
|
||||
return
|
||||
}
|
||||
|
||||
await listStore.deleteList(list.value)
|
||||
success({message: t('list.delete.success')})
|
||||
await projectStore.deleteProject(project.value)
|
||||
success({message: t('project.delete.success')})
|
||||
router.push({name: 'home'})
|
||||
}
|
||||
</script>
|
@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<create-edit
|
||||
:title="$t('list.duplicate.title')"
|
||||
:title="$t('project.duplicate.title')"
|
||||
primary-icon="paste"
|
||||
:primary-label="$t('list.duplicate.label')"
|
||||
@primary="duplicateList"
|
||||
:loading="listDuplicateService.loading"
|
||||
:primary-label="$t('project.duplicate.label')"
|
||||
@primary="duplicateProject"
|
||||
:loading="projectDuplicateService.loading"
|
||||
>
|
||||
<p>{{ $t('list.duplicate.text') }}</p>
|
||||
<p>{{ $t('project.duplicate.text') }}</p>
|
||||
|
||||
<Multiselect
|
||||
:placeholder="$t('namespace.search')"
|
||||
@ -24,21 +24,21 @@ import {ref, shallowReactive} from 'vue'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import ListDuplicateService from '@/services/listDuplicateService'
|
||||
import ProjectDuplicateService from '@/services/projectDuplicateService'
|
||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||
import Multiselect from '@/components/input/multiselect.vue'
|
||||
|
||||
import ListDuplicateModel from '@/models/listDuplicateModel'
|
||||
import ProjectDuplicateModel from '@/models/projectDuplicateModel'
|
||||
import type {INamespace} from '@/modelTypes/INamespace'
|
||||
|
||||
import {success} from '@/message'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
import {useNamespaceSearch} from '@/composables/useNamespaceSearch'
|
||||
import {useListStore} from '@/stores/lists'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
useTitle(() => t('list.duplicate.title'))
|
||||
useTitle(() => t('project.duplicate.title'))
|
||||
|
||||
const {
|
||||
namespaces,
|
||||
@ -53,23 +53,23 @@ function selectNamespace(namespace: INamespace) {
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const listStore = useListStore()
|
||||
const projectStore = useProjectStore()
|
||||
const namespaceStore = useNamespaceStore()
|
||||
|
||||
const listDuplicateService = shallowReactive(new ListDuplicateService())
|
||||
const projectDuplicateService = shallowReactive(new ProjectDuplicateService())
|
||||
|
||||
async function duplicateList() {
|
||||
const listDuplicate = new ListDuplicateModel({
|
||||
async function duplicateProject() {
|
||||
const projectDuplicate = new ProjectDuplicateModel({
|
||||
// FIXME: should be parameter
|
||||
listId: route.params.listId,
|
||||
projectId: route.params.projectId,
|
||||
namespaceId: selectedNamespace.value?.id,
|
||||
})
|
||||
|
||||
const duplicate = await listDuplicateService.create(listDuplicate)
|
||||
const duplicate = await projectDuplicateService.create(projectDuplicate)
|
||||
|
||||
namespaceStore.addListToNamespace(duplicate.list)
|
||||
listStore.setList(duplicate.list)
|
||||
success({message: t('list.duplicate.success')})
|
||||
router.push({name: 'list.index', params: {listId: duplicate.list.id}})
|
||||
namespaceStore.addProjectToNamespace(duplicate.project)
|
||||
projectStore.setProject(duplicate.project)
|
||||
success({message: t('project.duplicate.success')})
|
||||
router.push({name: 'project.index', params: {projectId: duplicate.project.id}})
|
||||
}
|
||||
</script>
|
@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<create-edit
|
||||
:title="$t('list.edit.header')"
|
||||
:title="$t('project.edit.header')"
|
||||
primary-icon=""
|
||||
:primary-label="$t('misc.save')"
|
||||
@primary="save"
|
||||
:tertiary="$t('misc.delete')"
|
||||
@tertiary="$router.push({ name: 'list.settings.delete', params: { id: listId } })"
|
||||
@tertiary="$router.push({ name: 'project.settings.delete', params: { id: projectId } })"
|
||||
>
|
||||
<div class="field">
|
||||
<label class="label" for="title">{{ $t('list.title') }}</label>
|
||||
<label class="label" for="title">{{ $t('project.title') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
:class="{ 'disabled': isLoading}"
|
||||
@ -16,18 +16,18 @@
|
||||
@keyup.enter="save"
|
||||
class="input"
|
||||
id="title"
|
||||
:placeholder="$t('list.edit.titlePlaceholder')"
|
||||
:placeholder="$t('project.edit.titlePlaceholder')"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="list.title"/>
|
||||
v-model="project.title"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label
|
||||
class="label"
|
||||
for="identifier"
|
||||
v-tooltip="$t('list.edit.identifierTooltip')">
|
||||
{{ $t('list.edit.identifier') }}
|
||||
v-tooltip="$t('project.edit.identifierTooltip')">
|
||||
{{ $t('project.edit.identifier') }}
|
||||
</label>
|
||||
<div class="control">
|
||||
<input
|
||||
@ -36,29 +36,29 @@
|
||||
@keyup.enter="save"
|
||||
class="input"
|
||||
id="identifier"
|
||||
:placeholder="$t('list.edit.identifierPlaceholder')"
|
||||
:placeholder="$t('project.edit.identifierPlaceholder')"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="list.identifier"/>
|
||||
v-model="project.identifier"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="listdescription">{{ $t('list.edit.description') }}</label>
|
||||
<label class="label" for="projectdescription">{{ $t('project.edit.description') }}</label>
|
||||
<div class="control">
|
||||
<Editor
|
||||
:class="{ 'disabled': isLoading}"
|
||||
:disabled="isLoading"
|
||||
:previewIsDefault="false"
|
||||
id="listdescription"
|
||||
:placeholder="$t('list.edit.descriptionPlaceholder')"
|
||||
v-model="list.description"
|
||||
id="projectdescription"
|
||||
:placeholder="$t('project.edit.descriptionPlaceholder')"
|
||||
v-model="project.description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">{{ $t('list.edit.color') }}</label>
|
||||
<label class="label">{{ $t('project.edit.color') }}</label>
|
||||
<div class="control">
|
||||
<color-picker v-model="list.hexColor"/>
|
||||
<color-picker v-model="project.hexColor"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -66,7 +66,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default { name: 'list-setting-edit' }
|
||||
export default { name: 'project-setting-edit' }
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -78,16 +78,16 @@ import Editor from '@/components/input/AsyncEditor'
|
||||
import ColorPicker from '@/components/input/ColorPicker.vue'
|
||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useList} from '@/stores/lists'
|
||||
import {useProject} from '@/stores/projects'
|
||||
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
|
||||
const props = defineProps({
|
||||
listId: {
|
||||
type: Number as PropType<IList['id']>,
|
||||
projectId: {
|
||||
type: Number as PropType<IProject['id']>,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
@ -96,13 +96,13 @@ const router = useRouter()
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
const {list, save: saveList, isLoading} = useList(props.listId)
|
||||
const {project, save: saveProject, isLoading} = useProject(props.projectId)
|
||||
|
||||
useTitle(() => list?.title ? t('list.edit.title', {list: list.title}) : '')
|
||||
useTitle(() => project?.title ? t('project.edit.title', {project: project.title}) : '')
|
||||
|
||||
async function save() {
|
||||
await saveList()
|
||||
await useBaseStore().handleSetCurrentList({list})
|
||||
await saveProject()
|
||||
await useBaseStore().handleSetCurrentProject({project})
|
||||
router.back()
|
||||
}
|
||||
</script>
|
@ -1,29 +1,29 @@
|
||||
<template>
|
||||
<create-edit
|
||||
:title="$t('list.share.header')"
|
||||
:title="$t('project.share.header')"
|
||||
:has-primary-action="false"
|
||||
>
|
||||
<template v-if="list">
|
||||
<template v-if="project">
|
||||
<userTeam
|
||||
:id="list.id"
|
||||
:id="project.id"
|
||||
:userIsAdmin="userIsAdmin"
|
||||
shareType="user"
|
||||
type="list"
|
||||
type="project"
|
||||
/>
|
||||
<userTeam
|
||||
:id="list.id"
|
||||
:id="project.id"
|
||||
:userIsAdmin="userIsAdmin"
|
||||
shareType="team"
|
||||
type="list"
|
||||
type="project"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<link-sharing :list-id="listId" v-if="linkSharingEnabled" class="mt-4"/>
|
||||
<link-sharing :project-id="projectId" v-if="linkSharingEnabled" class="mt-4"/>
|
||||
</create-edit>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {name: 'list-setting-share'}
|
||||
export default {name: 'project-setting-share'}
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@ -32,9 +32,9 @@ import {useRoute} from 'vue-router'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {useTitle} from '@vueuse/core'
|
||||
|
||||
import ListService from '@/services/list'
|
||||
import ListModel from '@/models/list'
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
import ProjectService from '@/services/project'
|
||||
import ProjectModel from '@/models/project'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
import {RIGHTS} from '@/constants/rights'
|
||||
|
||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||
@ -46,9 +46,9 @@ import {useConfigStore} from '@/stores/config'
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
const list = ref<IList>()
|
||||
const title = computed(() => list.value?.title
|
||||
? t('list.share.title', {list: list.value.title})
|
||||
const project = ref<IProject>()
|
||||
const title = computed(() => project.value?.title
|
||||
? t('project.share.title', {project: project.value.title})
|
||||
: '',
|
||||
)
|
||||
useTitle(title)
|
||||
@ -56,19 +56,19 @@ useTitle(title)
|
||||
const configStore = useConfigStore()
|
||||
|
||||
const linkSharingEnabled = computed(() => configStore.linkSharingEnabled)
|
||||
const userIsAdmin = computed(() => list?.value?.maxRight === RIGHTS.ADMIN)
|
||||
const userIsAdmin = computed(() => project?.value?.maxRight === RIGHTS.ADMIN)
|
||||
|
||||
async function loadList(listId: number) {
|
||||
const listService = new ListService()
|
||||
const newList = await listService.get(new ListModel({id: listId}))
|
||||
await useBaseStore().handleSetCurrentList({list: newList})
|
||||
list.value = newList
|
||||
async function loadProject(projectId: number) {
|
||||
const projectService = new ProjectService()
|
||||
const newProject = await projectService.get(new ProjectModel({id: projectId}))
|
||||
await useBaseStore().handleSetCurrentProject({project: newProject})
|
||||
project.value = newProject
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
const listId = computed(() => route.params.listId !== undefined
|
||||
? parseInt(route.params.listId as string)
|
||||
const projectId = computed(() => route.params.projectId !== undefined
|
||||
? parseInt(route.params.projectId as string)
|
||||
: undefined,
|
||||
)
|
||||
watchEffect(() => listId.value !== undefined && loadList(listId.value))
|
||||
watchEffect(() => projectId.value !== undefined && loadProject(projectId.value))
|
||||
</script>
|
||||
|
@ -39,7 +39,7 @@ import {useI18n} from 'vue-i18n'
|
||||
import {useTitle} from '@vueuse/core'
|
||||
|
||||
import Message from '@/components/misc/message.vue'
|
||||
import {LIST_VIEWS, type ListView} from '@/types/ListView'
|
||||
import {PROJECT_VIEWS, type ProjectView} from '@/types/ProjectView'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
@ -65,7 +65,7 @@ function useAuth() {
|
||||
errorMessage.value = ''
|
||||
|
||||
if (authLinkShare.value) {
|
||||
// FIXME: push to 'list.list' since authenticated?
|
||||
// FIXME: push to 'project.list' since authenticated?
|
||||
return
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ function useAuth() {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const {list_id: listId} = await authStore.linkShareAuth({
|
||||
const {project_id: projectId} = await authStore.linkShareAuth({
|
||||
hash: route.params.share,
|
||||
password: password.value,
|
||||
})
|
||||
@ -83,11 +83,11 @@ function useAuth() {
|
||||
: true
|
||||
baseStore.setLogoVisible(logoVisible)
|
||||
|
||||
const view = route.query.view && Object.values(LIST_VIEWS).includes(route.query.view as ListView)
|
||||
const view = route.query.view && Object.values(PROJECT_VIEWS).includes(route.query.view as ProjectView)
|
||||
? route.query.view
|
||||
: 'list'
|
||||
: 'project'
|
||||
|
||||
router.push({name: `list.${view}`, params: {listId}})
|
||||
router.push({name: `project.${view}`, params: {projectId}})
|
||||
} catch (e: any) {
|
||||
if (e.response?.data?.code === 13001) {
|
||||
authenticateWithPassword.value = true
|
||||
|
@ -31,10 +31,10 @@
|
||||
:loading="loading"
|
||||
>
|
||||
<div class="p-2">
|
||||
<single-task-in-list
|
||||
<single-task-in-project
|
||||
v-for="t in tasks"
|
||||
:key="t.id"
|
||||
:show-list="true"
|
||||
:show-project="true"
|
||||
:the-task="t"
|
||||
@taskUpdated="updateTasks"/>
|
||||
</div>
|
||||
@ -52,7 +52,7 @@ import {formatDate} from '@/helpers/time/formatDate'
|
||||
import {setTitle} from '@/helpers/setTitle'
|
||||
|
||||
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
||||
import SingleTaskInList from '@/components/tasks/partials/singleTaskInList.vue'
|
||||
import SingleTaskInProject from '@/components/tasks/partials/singleTaskInProject.vue'
|
||||
import DatepickerWithRange from '@/components/date/datepickerWithRange.vue'
|
||||
import {DATE_RANGES} from '@/components/date/dateRanges'
|
||||
import LlamaCool from '@/assets/llama-cool.svg?component'
|
||||
|
@ -13,10 +13,10 @@
|
||||
:can-write="canWrite"
|
||||
ref="heading"
|
||||
/>
|
||||
<h6 class="subtitle" v-if="parent && parent.namespace && parent.list">
|
||||
<h6 class="subtitle" v-if="parent && parent.namespace && parent.project">
|
||||
{{ getNamespaceTitle(parent.namespace) }} ›
|
||||
<router-link :to="{ name: 'list.index', params: { listId: parent.list.id } }">
|
||||
{{ getListTitle(parent.list) }}
|
||||
<router-link :to="{ name: 'project.index', params: { projectId: parent.project.id } }">
|
||||
{{ getProjectTitle(parent.project) }}
|
||||
</router-link>
|
||||
</h6>
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
</div>
|
||||
<edit-assignees
|
||||
:disabled="!canWrite"
|
||||
:list-id="task.listId"
|
||||
:project-id="task.projectId"
|
||||
:task-id="task.id"
|
||||
:ref="e => setFieldRef('assignees', e)"
|
||||
v-model="task.assignees"
|
||||
@ -254,7 +254,7 @@
|
||||
<related-tasks
|
||||
:edit-enabled="canWrite"
|
||||
:initial-related-tasks="task.relatedTasks"
|
||||
:list-id="task.listId"
|
||||
:project-id="task.projectId"
|
||||
:show-no-relations-notice="true"
|
||||
:task-id="taskId"
|
||||
:ref="e => setFieldRef('relatedTasks', e)"
|
||||
@ -262,7 +262,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Move Task -->
|
||||
<div class="content details" v-if="activeFields.moveList">
|
||||
<div class="content details" v-if="activeFields.moveProject">
|
||||
<h3>
|
||||
<span class="icon is-grey">
|
||||
<icon icon="list"/>
|
||||
@ -271,9 +271,9 @@
|
||||
</h3>
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<list-search
|
||||
@update:modelValue="changeList"
|
||||
:ref="e => setFieldRef('moveList', e)"
|
||||
<project-search
|
||||
@update:modelValue="changeProject"
|
||||
:ref="e => setFieldRef('moveProject', e)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -386,12 +386,12 @@
|
||||
{{ $t('task.detail.actions.relatedTasks') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="setFieldActive('moveList')"
|
||||
@click="setFieldActive('moveProject')"
|
||||
variant="secondary"
|
||||
icon="list"
|
||||
v-shortcut="'m'"
|
||||
>
|
||||
{{ $t('task.detail.actions.moveList') }}
|
||||
{{ $t('task.detail.actions.moveProject') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="setFieldActive('color')"
|
||||
@ -455,7 +455,7 @@ import TaskService from '@/services/task'
|
||||
import TaskModel, {TASK_DEFAULT_COLOR} from '@/models/task'
|
||||
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
|
||||
import {PRIORITIES, type Priority} from '@/constants/priorities'
|
||||
import {RIGHTS} from '@/constants/rights'
|
||||
@ -473,7 +473,7 @@ import Description from '@/components/tasks/partials/description.vue'
|
||||
import EditAssignees from '@/components/tasks/partials/editAssignees.vue'
|
||||
import EditLabels from '@/components/tasks/partials/editLabels.vue'
|
||||
import Heading from '@/components/tasks/partials/heading.vue'
|
||||
import ListSearch from '@/components/tasks/partials/listSearch.vue'
|
||||
import ProjectSearch from '@/components/tasks/partials/projectSearch.vue'
|
||||
import PercentDoneSelect from '@/components/tasks/partials/percentDoneSelect.vue'
|
||||
import PrioritySelect from '@/components/tasks/partials/prioritySelect.vue'
|
||||
import RelatedTasks from '@/components/tasks/partials/relatedTasks.vue'
|
||||
@ -484,7 +484,7 @@ import CustomTransition from '@/components/misc/CustomTransition.vue'
|
||||
|
||||
import {uploadFile} from '@/helpers/attachments'
|
||||
import {getNamespaceTitle} from '@/helpers/getNamespaceTitle'
|
||||
import {getListTitle} from '@/helpers/getListTitle'
|
||||
import {getProjectTitle} from '@/helpers/getProjectTitle'
|
||||
import {scrollIntoView} from '@/helpers/scrollIntoView'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
@ -536,26 +536,26 @@ const visible = ref(false)
|
||||
const taskId = toRef(props, 'taskId')
|
||||
|
||||
const parent = computed(() => {
|
||||
if (!task.listId) {
|
||||
if (!task.projectId) {
|
||||
return {
|
||||
namespace: null,
|
||||
list: null,
|
||||
project: null,
|
||||
}
|
||||
}
|
||||
|
||||
if (!namespaceStore.getListAndNamespaceById) {
|
||||
if (!namespaceStore.getProjectAndNamespaceById) {
|
||||
return null
|
||||
}
|
||||
|
||||
return namespaceStore.getListAndNamespaceById(task.listId)
|
||||
return namespaceStore.getProjectAndNamespaceById(task.projectId)
|
||||
})
|
||||
|
||||
watch(
|
||||
parent,
|
||||
(parent) => {
|
||||
const parentList = parent !== null ? parent.list : null
|
||||
if (parentList !== null) {
|
||||
baseStore.handleSetCurrentList({list: parentList})
|
||||
const parentProject = parent !== null ? parent.project : null
|
||||
if (parentProject !== null) {
|
||||
baseStore.handleSetCurrentProject({project: parentProject})
|
||||
}
|
||||
},
|
||||
{immediate: true},
|
||||
@ -616,7 +616,7 @@ type FieldType =
|
||||
| 'dueDate'
|
||||
| 'endDate'
|
||||
| 'labels'
|
||||
| 'moveList'
|
||||
| 'moveProject'
|
||||
| 'percentDone'
|
||||
| 'priority'
|
||||
| 'relatedTasks'
|
||||
@ -631,7 +631,7 @@ const activeFields : {[type in FieldType]: boolean} = reactive({
|
||||
dueDate: false,
|
||||
endDate: false,
|
||||
labels: false,
|
||||
moveList: false,
|
||||
moveProject: false,
|
||||
percentDone: false,
|
||||
priority: false,
|
||||
relatedTasks: false,
|
||||
@ -666,7 +666,7 @@ const activeFieldElements : {[id in FieldType]: HTMLElement | null} = reactive({
|
||||
dueDate: null,
|
||||
endDate: null,
|
||||
labels: null,
|
||||
moveList: null,
|
||||
moveProject: null,
|
||||
percentDone: null,
|
||||
priority: null,
|
||||
relatedTasks: null,
|
||||
@ -743,7 +743,7 @@ const showDeleteModal = ref(false)
|
||||
async function deleteTask() {
|
||||
await taskStore.delete(task)
|
||||
success({message: t('task.detail.deleteSuccess')})
|
||||
router.push({name: 'list.index', params: {listId: task.listId}})
|
||||
router.push({name: 'project.index', params: {projectId: task.projectId}})
|
||||
}
|
||||
|
||||
function toggleTaskDone() {
|
||||
@ -758,12 +758,12 @@ function toggleTaskDone() {
|
||||
})
|
||||
}
|
||||
|
||||
async function changeList(list: IList) {
|
||||
async function changeProject(project: IProject) {
|
||||
kanbanStore.removeTaskInBucket(task)
|
||||
await saveTask({
|
||||
task: {
|
||||
...task,
|
||||
listId: list.id,
|
||||
projectId: project.id,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -14,9 +14,9 @@
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">
|
||||
{{ $t('user.settings.general.defaultList') }}
|
||||
{{ $t('user.settings.general.defaultProject') }}
|
||||
</label>
|
||||
<list-search v-model="defaultList"/>
|
||||
<project-search v-model="defaultProject"/>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="checkbox">
|
||||
@ -156,7 +156,7 @@ import {useI18n} from 'vue-i18n'
|
||||
|
||||
import {PrefixMode} from '@/modules/parseTaskText'
|
||||
|
||||
import ListSearch from '@/components/tasks/partials/listSearch.vue'
|
||||
import ProjectSearch from '@/components/tasks/partials/projectSearch.vue'
|
||||
|
||||
import {SUPPORTED_LOCALES} from '@/i18n'
|
||||
import {playSoundWhenDoneKey, playPopSound} from '@/helpers/playPop'
|
||||
@ -168,13 +168,13 @@ import {AuthenticatedHTTPFactory} from '@/helpers/fetcher'
|
||||
import {useColorScheme} from '@/composables/useColorScheme'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
|
||||
import {useListStore} from '@/stores/lists'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
useTitle(() => `${t('user.settings.general.title')} - ${t('user.settings.title')}`)
|
||||
|
||||
const DEFAULT_LIST_ID = 0
|
||||
const DEFAULT_PROJECT_ID = 0
|
||||
|
||||
function useColorSchemeSetting() {
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
@ -243,11 +243,11 @@ watch(
|
||||
{immediate: true},
|
||||
)
|
||||
|
||||
const listStore = useListStore()
|
||||
const defaultList = computed({
|
||||
get: () => listStore.getListById(settings.value.defaultListId) || undefined,
|
||||
const projectStore = useProjectStore()
|
||||
const defaultProject = computed({
|
||||
get: () => projectStore.getProjectById(settings.value.defaultProjectId) || undefined,
|
||||
set(l) {
|
||||
settings.value.defaultListId = l ? l.id : DEFAULT_LIST_ID
|
||||
settings.value.defaultProjectId = l ? l.id : DEFAULT_PROJECT_ID
|
||||
},
|
||||
})
|
||||
const loading = computed(() => authStore.isLoadingGeneralSettings)
|
||||
|
Reference in New Issue
Block a user