Compare commits

...

10 Commits

Author SHA1 Message Date
cef98c003c fix(deps): update dependency pinia to v2.3.1 2026-02-13 08:06:31 +00:00
Dominik Pschenitschni
cbbc4c0372 fix: i18n missing translation key 2025-01-19 20:10:22 +00:00
Dominik Pschenitschni
c7e708cf7d fix: deprecated import in useTitle 2025-01-19 20:07:55 +00:00
Dominik Pschenitschni
6d3a30c799 fix: postcss-easing-gradient types 2025-01-19 19:58:16 +00:00
Dominik Pschenitschni
4c972e1bc4 feat: load project in project view 2025-01-19 19:56:07 +00:00
Dominik Pschenitschni
144571e448 feat: simplify ProjectView 2025-01-19 19:56:07 +00:00
Dominik Pschenitschni
a24c64da8f fix: vite config linting 2025-01-19 19:39:40 +00:00
renovate
2faa03757c fix(deps): update module github.com/wneessen/go-mail to v0.6.1 2025-01-19 16:30:07 +00:00
kolaente
9cef2c4c97 chore: add Bug type to bug issue template 2025-01-19 16:48:44 +01:00
renovate
ea6b141d42 chore(deps): update dependency caniuse-lite to v1.0.30001695 2025-01-19 03:07:01 +00:00
16 changed files with 155 additions and 132 deletions

View File

@@ -1,6 +1,6 @@
name: Bug Report
description: Found something you weren't expecting? Report it here!
labels: kind/bug
type: Bug
body:
- type: markdown
attributes:
@@ -49,4 +49,4 @@ body:
id: screenshots
attributes:
label: Screenshots
description: If this issue involves the Web Interface, please provide one or more screenshots
description: If this issue involves the Web Interface, please provide one or more screenshots

View File

@@ -1,4 +1 @@
declare module 'postcss-easing-gradients' {
import postcssEasingGradients from 'postcss-easing-gradients'
export default postcssEasingGradients
}
declare module 'postcss-easing-gradients';

View File

@@ -94,7 +94,7 @@
"is-touch-device": "1.0.1",
"klona": "2.0.6",
"lowlight": "3.3.0",
"pinia": "2.3.0",
"pinia": "2.3.1",
"register-service-worker": "1.7.2",
"sortablejs": "1.15.6",
"tailwindcss": "3.4.17",
@@ -130,7 +130,7 @@
"@vue/tsconfig": "0.7.0",
"autoprefixer": "10.4.20",
"browserslist": "4.24.4",
"caniuse-lite": "1.0.30001692",
"caniuse-lite": "1.0.30001695",
"csstype": "3.1.3",
"cypress": "14.0.0",
"esbuild": "0.24.2",

View File

@@ -45,7 +45,7 @@ importers:
version: 7.120.3
'@sentry/vue':
specifier: 8.50.0
version: 8.50.0(pinia@2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))
version: 8.50.0(pinia@2.3.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))
'@tiptap/core':
specifier: 2.11.2
version: 2.11.2(@tiptap/pm@2.11.2)
@@ -152,8 +152,8 @@ importers:
specifier: 3.3.0
version: 3.3.0
pinia:
specifier: 2.3.0
version: 2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3))
specifier: 2.3.1
version: 2.3.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3))
register-service-worker:
specifier: 1.7.2
version: 1.7.2
@@ -255,8 +255,8 @@ importers:
specifier: 4.24.4
version: 4.24.4
caniuse-lite:
specifier: 1.0.30001692
version: 1.0.30001692
specifier: 1.0.30001695
version: 1.0.30001695
csstype:
specifier: 3.1.3
version: 3.1.3
@@ -1535,6 +1535,7 @@ packages:
'@faker-js/faker@9.4.0':
resolution: {integrity: sha512-85+k0AxaZSTowL0gXp8zYWDIrWclTbRPg/pm/V0dSFZ6W6D4lhcG3uuZl4zLsEKfEvs69xDbLN2cHQudwp95JA==}
engines: {node: '>=18.0.0', npm: '>=9.0.0'}
deprecated: Please update to a newer version
'@floating-ui/core@1.6.7':
resolution: {integrity: sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==}
@@ -1643,22 +1644,26 @@ packages:
resolution: {integrity: sha512-NAmhw1l/llM0HZRpagR/ChJTNymW4ll6/4EDSJML5c8L5Hl/+k6UyF8EIgE6DeHpfheQujkSRngauViHqq6jJQ==}
engines: {node: '>= 16'}
'@intlify/message-compiler@11.0.0-rc.1':
resolution: {integrity: sha512-TGw2uBfuTFTegZf/BHtUQBEKxl7Q/dVGLoqRIdw8lFsp9g/53sYn5iD+0HxIzdYjbWL6BTJMXCPUHp9PxDTRPw==}
engines: {node: '>= 16'}
'@intlify/message-compiler@11.0.1':
resolution: {integrity: sha512-5RFH8x+Mn3mbjcHXnb6KCXGiczBdiQkWkv99iiA0JpKrNuTAQeW59Pjq/uObMB0eR0shnKYGTkIJxum+DbL3sw==}
engines: {node: '>= 16'}
'@intlify/shared@11.0.0-rc.1':
resolution: {integrity: sha512-8tR1xe7ZEbkabTuE/tNhzpolygUn9OaYp9yuYAF4MgDNZg06C3Qny80bes2/e9/Wm3aVkPUlCw6WgU7mQd0yEg==}
'@intlify/message-compiler@12.0.0-alpha.3':
resolution: {integrity: sha512-mDDTN3gfYOHhBnpnlby19UHyvMaOnzdlpsIrxUfs44R/vCATfn8pMOkE8PXD2t410xkocEj3FpDcC9XC/0v4Dg==}
engines: {node: '>= 16'}
'@intlify/shared@11.0.1':
resolution: {integrity: sha512-lH164+aDDptHZ3dBDbIhRa1dOPQUp+83iugpc+1upTOWCnwyC1PVis6rSWNMMJ8VQxvtHQB9JMib48K55y0PvQ==}
engines: {node: '>= 16'}
'@intlify/shared@11.2.8':
resolution: {integrity: sha512-l6e4NZyUgv8VyXXH4DbuucFOBmxLF56C/mqh2tvApbzl2Hrhi1aTDcuv5TKdxzfHYmpO3UB0Cz04fgDT9vszfw==}
engines: {node: '>= 16'}
'@intlify/shared@12.0.0-alpha.3':
resolution: {integrity: sha512-ryaNYBvxQjyJUmVuBBg+HHUsmGnfxcEUPR0NCeG4/K9N2qtyFE35C80S15IN6iYFE2MGWLN7HfOSyg0MXZIc9w==}
engines: {node: '>= 16'}
'@intlify/unplugin-vue-i18n@6.0.3':
resolution: {integrity: sha512-9ZDjBlhUHtgjRl23TVcgfJttgu8cNepwVhWvOv3mUMRDAhjW0pur1mWKEUKr1I8PNwE4Gvv2IQ1xcl4RL0nG0g==}
engines: {node: '>= 18'}
@@ -2947,8 +2952,8 @@ packages:
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
engines: {node: '>=6'}
caniuse-lite@1.0.30001692:
resolution: {integrity: sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==}
caniuse-lite@1.0.30001695:
resolution: {integrity: sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==}
capital-case@1.0.4:
resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==}
@@ -3883,11 +3888,12 @@ packages:
glob@10.4.5:
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Glob versions prior to v9 are no longer supported
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
global-dirs@2.1.0:
resolution: {integrity: sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==}
@@ -4691,6 +4697,7 @@ packages:
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
deprecated: Use your platform's native DOMException instead
node-fetch@2.6.7:
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
@@ -4959,8 +4966,8 @@ packages:
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
engines: {node: '>=0.10.0'}
pinia@2.3.0:
resolution: {integrity: sha512-ohZj3jla0LL0OH5PlLTDMzqKiVw2XARmC1XYLdLWIPBMdhDW/123ZWr4zVAhtJm+aoSkFa13pYXskAvAscIkhQ==}
pinia@2.3.1:
resolution: {integrity: sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==}
peerDependencies:
typescript: '>=4.4.4'
vue: ^2.7.0 || ^3.5.11
@@ -5313,7 +5320,7 @@ packages:
puppeteer@13.7.0:
resolution: {integrity: sha512-U1uufzBjz3+PkpCxFrWzh4OrMIdIb2ztzCu0YEPfRHjHswcSwHZswnK+WdsOQJsRV8WeTg3jLhJR4D867+fjsA==}
engines: {node: '>=10.18.1'}
deprecated: < 22.8.2 is no longer supported
deprecated: < 24.15.0 is no longer supported
qs@6.13.0:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
@@ -5753,6 +5760,7 @@ packages:
source-map@0.8.0-beta.0:
resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
engines: {node: '>= 8'}
deprecated: The work that was done in this beta branch won't be included in future versions
sourcemap-codec@1.4.8:
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
@@ -6436,6 +6444,7 @@ packages:
vue-i18n@11.0.1:
resolution: {integrity: sha512-pWAT8CusK8q9/EpN7V3oxwHwxWm6+Kp2PeTZmRGvdZTkUzMQDpbbmHp0TwQ8xw04XKm23cr6B4GL72y3W8Yekg==}
engines: {node: '>= 16'}
deprecated: This version is NOT deprecated. Previous deprecation was a mistake.
peerDependencies:
vue: ^3.0.0
@@ -6511,6 +6520,7 @@ packages:
whatwg-encoding@2.0.0:
resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
engines: {node: '>=12'}
deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation
whatwg-mimetype@3.0.0:
resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
@@ -8105,8 +8115,8 @@ snapshots:
'@intlify/bundle-utils@10.0.0(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))':
dependencies:
'@intlify/message-compiler': 11.0.0-rc.1
'@intlify/shared': 11.0.0-rc.1
'@intlify/message-compiler': 12.0.0-alpha.3
'@intlify/shared': 12.0.0-alpha.3
acorn: 8.14.0
escodegen: 2.1.0
estree-walker: 2.0.2
@@ -8122,26 +8132,28 @@ snapshots:
'@intlify/message-compiler': 11.0.1
'@intlify/shared': 11.0.1
'@intlify/message-compiler@11.0.0-rc.1':
dependencies:
'@intlify/shared': 11.0.0-rc.1
source-map-js: 1.2.1
'@intlify/message-compiler@11.0.1':
dependencies:
'@intlify/shared': 11.0.1
source-map-js: 1.2.1
'@intlify/shared@11.0.0-rc.1': {}
'@intlify/message-compiler@12.0.0-alpha.3':
dependencies:
'@intlify/shared': 12.0.0-alpha.3
source-map-js: 1.2.1
'@intlify/shared@11.0.1': {}
'@intlify/shared@11.2.8': {}
'@intlify/shared@12.0.0-alpha.3': {}
'@intlify/unplugin-vue-i18n@6.0.3(@vue/compiler-dom@3.5.13)(eslint@9.18.0(jiti@1.21.6))(rollup@4.30.1)(typescript@5.7.3)(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))':
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.18.0(jiti@1.21.6))
'@intlify/bundle-utils': 10.0.0(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))
'@intlify/shared': 11.0.1
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.0.1)(@vue/compiler-dom@3.5.13)(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))
'@intlify/shared': 11.2.8
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.2.8)(@vue/compiler-dom@3.5.13)(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))
'@rollup/pluginutils': 5.1.3(rollup@4.30.1)
'@typescript-eslint/scope-manager': 8.18.2
'@typescript-eslint/typescript-estree': 8.18.2(typescript@5.7.3)
@@ -8163,11 +8175,11 @@ snapshots:
- supports-color
- typescript
'@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.0.1)(@vue/compiler-dom@3.5.13)(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))':
'@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.2.8)(@vue/compiler-dom@3.5.13)(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))':
dependencies:
'@babel/parser': 7.26.1
optionalDependencies:
'@intlify/shared': 11.0.1
'@intlify/shared': 11.2.8
'@vue/compiler-dom': 3.5.13
vue: 3.5.13(typescript@5.7.3)
vue-i18n: 11.0.1(vue@3.5.13(typescript@5.7.3))
@@ -8529,13 +8541,13 @@ snapshots:
dependencies:
'@sentry/types': 7.120.3
'@sentry/vue@8.50.0(pinia@2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))':
'@sentry/vue@8.50.0(pinia@2.3.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))':
dependencies:
'@sentry/browser': 8.50.0
'@sentry/core': 8.50.0
vue: 3.5.13(typescript@5.7.3)
optionalDependencies:
pinia: 2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3))
pinia: 2.3.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3))
'@sideway/address@4.1.5':
dependencies:
@@ -9349,7 +9361,7 @@ snapshots:
autoprefixer@10.4.20(postcss@8.5.1):
dependencies:
browserslist: 4.24.4
caniuse-lite: 1.0.30001692
caniuse-lite: 1.0.30001695
fraction.js: 4.3.7
normalize-range: 0.1.2
picocolors: 1.0.1
@@ -9450,7 +9462,7 @@ snapshots:
browserslist@4.24.4:
dependencies:
caniuse-lite: 1.0.30001692
caniuse-lite: 1.0.30001695
electron-to-chromium: 1.5.73
node-releases: 2.0.19
update-browserslist-db: 1.1.1(browserslist@4.24.4)
@@ -9513,7 +9525,7 @@ snapshots:
camelcase@5.3.1: {}
caniuse-lite@1.0.30001692: {}
caniuse-lite@1.0.30001695: {}
capital-case@1.0.4:
dependencies:
@@ -11705,7 +11717,7 @@ snapshots:
pify@2.3.0: {}
pinia@2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)):
pinia@2.3.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)):
dependencies:
'@vue/devtools-api': 6.6.4
vue: 3.5.13(typescript@5.7.3)

View File

@@ -63,7 +63,7 @@ const motd = computed(() => configStore.motd)
const route = useRoute()
const {t} = useI18n({useScope: 'global'})
const title = computed(() => t(route.meta?.title as string || ''))
const title = computed(() => route.meta?.title ? t(route.meta.title as string) : '')
useTitle(() => title.value)
</script>

View File

@@ -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':

View File

@@ -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']
}>()

View File

@@ -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'],
}>()

View File

@@ -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'],
}>()

View File

@@ -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'],
}>()

View File

@@ -1,6 +1,6 @@
import {computed} from 'vue'
import {computed, toValue} from 'vue'
import {useTitle as useTitleVueUse, toValue, type UseTitleOptions, type ReadonlyRefOrGetter, type MaybeRef, type MaybeRefOrGetter} from '@vueuse/core'
import {useTitle as useTitleVueUse, type UseTitleOptions, type ReadonlyRefOrGetter, type MaybeRef, type MaybeRefOrGetter} from '@vueuse/core'
export function useTitle(
newTitle:

View File

@@ -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>

View File

@@ -3,7 +3,12 @@
"@tsconfig/node22/tsconfig.json",
"@vue/tsconfig/tsconfig.json"
],
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*"],
"include": [
"env.config.d.ts",
"vite.config.*",
"vitest.config.*",
"cypress.config.*"
],
"compilerOptions": {
"composite": true,
"noEmit": true,

View File

@@ -184,7 +184,7 @@ export default defineConfig(({mode}) => {
},
}),
vueDevTools({
launchEditor: env.VUE_DEVTOOLS_LAUNCH_EDITOR || 'code'
launchEditor: env.VUE_DEVTOOLS_LAUNCH_EDITOR || 'code',
}),
viteSentry(getSentryConfig(env)),
],

2
go.mod
View File

@@ -67,7 +67,7 @@ require (
github.com/tkuchiki/go-timezone v0.2.3
github.com/typesense/typesense-go/v2 v2.0.0
github.com/ulule/limiter/v3 v3.11.2
github.com/wneessen/go-mail v0.6.0
github.com/wneessen/go-mail v0.6.1
github.com/yuin/goldmark v1.7.8
golang.org/x/crypto v0.32.0
golang.org/x/image v0.23.0

2
go.sum
View File

@@ -1377,6 +1377,8 @@ github.com/wneessen/go-mail v0.5.2 h1:MZKwgHJoRboLJ+EHMLuHpZc95wo+u1xViL/4XSswDT
github.com/wneessen/go-mail v0.5.2/go.mod h1:kRroJvEq2hOSEPFRiKjN7Csrz0G1w+RpiGR3b6yo+Ck=
github.com/wneessen/go-mail v0.6.0 h1:wO7EeJ8RL6DD+aycFGntil6b11g3FNQpQQQC1gkm97Y=
github.com/wneessen/go-mail v0.6.0/go.mod h1:G702XlFhzHV0Z4w9j2VsH5K9dJDvj0hx+yOOp1oX9vc=
github.com/wneessen/go-mail v0.6.1 h1:cDGqlGuEEhdILRe53VFzmM9WBk8Xh/QMvbO0oxrNJB4=
github.com/wneessen/go-mail v0.6.1/go.mod h1:G702XlFhzHV0Z4w9j2VsH5K9dJDvj0hx+yOOp1oX9vc=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=