Compare commits
2 Commits
renovate/k
...
feature/lo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7aa481ff19 | ||
|
|
6f7b80e90f |
@@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div
|
||||
:class="{ 'is-loading': projectService.loading, 'is-archived': currentProject?.isArchived}"
|
||||
class="loader-container"
|
||||
:class="{
|
||||
'is-loading': isLoadingProject,
|
||||
'is-archived': currentProject?.isArchived,
|
||||
}"
|
||||
>
|
||||
<h1 class="project-title-print">
|
||||
{{ getProjectTitle(currentProject) }}
|
||||
@@ -37,43 +40,37 @@
|
||||
</Message>
|
||||
</CustomTransition>
|
||||
|
||||
<slot v-if="loadedProjectId" />
|
||||
<slot v-if="!isLoadingProject" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, ref, watch} from 'vue'
|
||||
import {useRoute} from 'vue-router'
|
||||
import {computed} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import Message from '@/components/misc/Message.vue'
|
||||
import CustomTransition from '@/components/misc/CustomTransition.vue'
|
||||
|
||||
import ProjectModel from '@/models/project'
|
||||
import ProjectService from '@/services/project'
|
||||
|
||||
import {getProjectTitle} from '@/helpers/getProjectTitle'
|
||||
import {saveProjectToHistory} from '@/modules/projectHistory'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
const props = defineProps<{
|
||||
isLoadingProject: boolean,
|
||||
projectId: IProject['id'],
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
const {t} = useI18n()
|
||||
|
||||
const baseStore = useBaseStore()
|
||||
const projectStore = useProjectStore()
|
||||
const projectService = ref(new ProjectService())
|
||||
const loadedProjectId = ref(0)
|
||||
|
||||
const currentProject = computed<IProject>(() => {
|
||||
return typeof baseStore.currentProject === 'undefined' ? {
|
||||
@@ -87,61 +84,6 @@ useTitle(() => currentProject.value?.id ? getProjectTitle(currentProject.value)
|
||||
|
||||
const views = computed(() => projectStore.projects[props.projectId]?.views)
|
||||
|
||||
// watchEffect would be called every time the prop would get a value assigned, even if that value was the same as before.
|
||||
// This resulted in loading and setting the project multiple times, even when navigating away from it.
|
||||
// This caused wired bugs where the project background would be set on the home page but only right after setting a new
|
||||
// project background and then navigating to home. It also highlighted the project in the menu and didn't allow changing any
|
||||
// of it, most likely due to the rights not being properly populated.
|
||||
watch(
|
||||
() => props.projectId,
|
||||
// loadProject
|
||||
async (projectIdToLoad: number) => {
|
||||
const projectData = {id: projectIdToLoad}
|
||||
saveProjectToHistory(projectData)
|
||||
|
||||
// Don't load the project if we either already loaded it or aren't dealing with a project at all currently and
|
||||
// the currently loaded project has the right set.
|
||||
if (
|
||||
(
|
||||
projectIdToLoad === loadedProjectId.value ||
|
||||
typeof projectIdToLoad === 'undefined' ||
|
||||
projectIdToLoad === currentProject.value?.id
|
||||
)
|
||||
&& typeof currentProject.value !== 'undefined' && currentProject.value.maxRight !== null
|
||||
) {
|
||||
loadedProjectId.value = projectIdToLoad
|
||||
return
|
||||
}
|
||||
|
||||
console.debug('Loading project, $route.params =', route.params, `, loadedProjectId = ${loadedProjectId.value}, currentProject = `, currentProject.value)
|
||||
|
||||
// Set the current project to the one we're about to load so that the title is already shown at the top
|
||||
loadedProjectId.value = 0
|
||||
const projectFromStore = projectStore.projects[projectData.id]
|
||||
if (projectFromStore) {
|
||||
baseStore.handleSetCurrentProject({project: projectFromStore, currentProjectViewId: props.viewId})
|
||||
}
|
||||
|
||||
// We create an extra project object instead of creating it in project.value because that would trigger a ui update which would result in bad ux.
|
||||
const project = new ProjectModel(projectData)
|
||||
try {
|
||||
const loadedProject = await projectService.value.get(project)
|
||||
baseStore.handleSetCurrentProject({project: loadedProject, currentProjectViewId: props.viewId})
|
||||
} finally {
|
||||
loadedProjectId.value = projectIdToLoad
|
||||
}
|
||||
},
|
||||
{immediate: true},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.viewId,
|
||||
() => {
|
||||
baseStore.setCurrentProjectViewId(props.viewId)
|
||||
},
|
||||
{immediate: true},
|
||||
)
|
||||
|
||||
function getViewTitle(view: IProjectView) {
|
||||
switch (view.title) {
|
||||
case 'List':
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<ProjectWrapper
|
||||
class="project-gantt"
|
||||
:is-loading-project="isLoadingProject"
|
||||
:project-id="filters.projectId"
|
||||
:view-id
|
||||
>
|
||||
@@ -95,6 +96,7 @@ import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
type Options = Flatpickr.Options.Options
|
||||
|
||||
const props = defineProps<{
|
||||
isLoadingProject: boolean,
|
||||
route: RouteLocationNormalized
|
||||
viewId: IProjectView['id']
|
||||
}>()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<ProjectWrapper
|
||||
class="project-kanban"
|
||||
:is-loading-project="isLoadingProject"
|
||||
:project-id="projectId"
|
||||
:view-id
|
||||
>
|
||||
@@ -315,6 +316,7 @@ import TaskBucketService from '@/services/taskBucket'
|
||||
import TaskBucketModel from '@/models/taskBucket'
|
||||
|
||||
const props = defineProps<{
|
||||
isLoadingProject: boolean,
|
||||
projectId: number,
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<ProjectWrapper
|
||||
class="project-list"
|
||||
:is-loading-project="isLoadingProject"
|
||||
:project-id="projectId"
|
||||
:view-id
|
||||
>
|
||||
@@ -123,6 +124,7 @@ import TaskPositionService from '@/services/taskPosition'
|
||||
import TaskPositionModel from '@/models/taskPosition'
|
||||
|
||||
const props = defineProps<{
|
||||
isLoadingProject: boolean,
|
||||
projectId: IProject['id'],
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<ProjectWrapper
|
||||
class="project-table"
|
||||
:is-loading-project="isLoadingProject"
|
||||
:project-id="projectId"
|
||||
:view-id
|
||||
>
|
||||
@@ -298,6 +299,7 @@ import { camelCase } from 'change-case'
|
||||
import {isSavedFilter} from '@/services/savedFilter'
|
||||
|
||||
const props = defineProps<{
|
||||
isLoadingProject: boolean,
|
||||
projectId: IProject['id'],
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, watch} from 'vue'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {computed, ref, shallowReactive, watch, watchEffect} from 'vue'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
|
||||
import {saveProjectView} from '@/helpers/projectView'
|
||||
import ProjectService from '@/services/project'
|
||||
|
||||
import ProjectList from '@/components/project/views/ProjectList.vue'
|
||||
import ProjectGantt from '@/components/project/views/ProjectGantt.vue'
|
||||
import ProjectTable from '@/components/project/views/ProjectTable.vue'
|
||||
import ProjectKanban from '@/components/project/views/ProjectKanban.vue'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
|
||||
import {DEFAULT_PROJECT_VIEW_SETTINGS} from '@/modelTypes/IProjectView'
|
||||
import {saveProjectToHistory} from '@/modules/projectHistory'
|
||||
|
||||
const props = defineProps<{
|
||||
projectId: number,
|
||||
@@ -17,8 +23,10 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
const router = useRouter()
|
||||
const baseStore = useBaseStore()
|
||||
const projectStore = useProjectStore()
|
||||
const authStore = useAuthStore()
|
||||
const route = useRoute()
|
||||
|
||||
const currentProject = computed(() => projectStore.projects[props.projectId])
|
||||
|
||||
@@ -26,19 +34,68 @@ const currentView = computed(() => {
|
||||
return currentProject.value?.views.find(v => v.id === props.viewId)
|
||||
})
|
||||
|
||||
const projectService = shallowReactive(new ProjectService())
|
||||
const isLoadingProject = computed(() => projectService.loading)
|
||||
const loadedProjectId = ref(0)
|
||||
|
||||
watch(
|
||||
() => props.projectId,
|
||||
// loadProject
|
||||
async (projectIdToLoad, oldProjectIdToLoad) => {
|
||||
|
||||
console.debug('Loading project, $route.params =', route.params, `, loadedProjectId = ${loadedProjectId.value}, currentProject = `, currentProject.value)
|
||||
|
||||
|
||||
if (projectIdToLoad !== oldProjectIdToLoad) {
|
||||
loadedProjectId.value = 0
|
||||
}
|
||||
|
||||
try {
|
||||
const loadedProject = await projectService.get({id: projectIdToLoad})
|
||||
|
||||
// Here, we only set the new project in the projectStore.
|
||||
// Setting that projet as the current one in the baseStore is handled by the watcher below.
|
||||
projectStore.setProject(loadedProject)
|
||||
} finally {
|
||||
loadedProjectId.value = projectIdToLoad
|
||||
}
|
||||
},
|
||||
{immediate: true},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => [currentProject.value, props.viewId],
|
||||
([newCurrentProject, newViewId]) => {
|
||||
if (!newCurrentProject) {
|
||||
baseStore.handleSetCurrentProject({project: null})
|
||||
return
|
||||
}
|
||||
|
||||
baseStore.handleSetCurrentProject({
|
||||
project: newCurrentProject,
|
||||
currentProjectViewId: newViewId,
|
||||
})
|
||||
}, {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
)
|
||||
|
||||
function redirectToDefaultViewIfNecessary() {
|
||||
if (props.viewId === 0 || !projectStore.projects[props.projectId]?.views.find(v => v.id === props.viewId)) {
|
||||
if (props.viewId === 0 || !currentView.value) {
|
||||
// Ideally, we would do that in the router redirect, but the projects (and therefore, the views)
|
||||
// are not always loaded then.
|
||||
|
||||
const defaultView = authStore.settings.frontendSettings.defaultView
|
||||
|
||||
let view
|
||||
if (authStore.settings.frontendSettings.defaultView !== DEFAULT_PROJECT_VIEW_SETTINGS.FIRST) {
|
||||
view = projectStore.projects[props.projectId]?.views.find(v => v.viewKind === authStore.settings.frontendSettings.defaultView)
|
||||
if (defaultView !== DEFAULT_PROJECT_VIEW_SETTINGS.FIRST) {
|
||||
view = currentProject.value?.views.find(v => v.viewKind === defaultView)
|
||||
}
|
||||
|
||||
// Use the first view as fallback if the default view is not available
|
||||
if (view === undefined && projectStore.projects[props.projectId]?.views?.length > 0) {
|
||||
view = projectStore.projects[props.projectId]?.views[0]
|
||||
if (view === undefined && currentProject.value?.views?.length > 0) {
|
||||
view = currentProject.value?.views[0]
|
||||
}
|
||||
|
||||
if (view) {
|
||||
@@ -60,39 +117,39 @@ watch(
|
||||
)
|
||||
|
||||
watch(
|
||||
() => projectStore.projects[props.projectId],
|
||||
currentProject,
|
||||
redirectToDefaultViewIfNecessary,
|
||||
)
|
||||
|
||||
// using a watcher instead of beforeEnter because beforeEnter is not called when only the viewId changes
|
||||
watch(
|
||||
() => [props.projectId, props.viewId],
|
||||
() => saveProjectView(props.projectId, props.viewId),
|
||||
{immediate: true},
|
||||
)
|
||||
watchEffect(() => saveProjectToHistory({id: props.projectId}))
|
||||
watchEffect(() => saveProjectView(props.projectId, props.viewId))
|
||||
|
||||
const route = useRoute()
|
||||
watchEffect(() => baseStore.setCurrentProjectViewId(props.viewId))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProjectList
|
||||
v-if="currentView?.viewKind === 'list'"
|
||||
:project-id="projectId"
|
||||
:is-loading-project="isLoadingProject"
|
||||
:view-id
|
||||
/>
|
||||
<ProjectGantt
|
||||
v-if="currentView?.viewKind === 'gantt'"
|
||||
:route
|
||||
:is-loading-project="isLoadingProject"
|
||||
:view-id
|
||||
/>
|
||||
<ProjectTable
|
||||
v-if="currentView?.viewKind === 'table'"
|
||||
:project-id="projectId"
|
||||
:is-loading-project="isLoadingProject"
|
||||
:view-id
|
||||
/>
|
||||
<ProjectKanban
|
||||
v-if="currentView?.viewKind === 'kanban'"
|
||||
:project-id="projectId"
|
||||
:is-loading-project="isLoadingProject"
|
||||
:view-id
|
||||
/>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user