Compare commits
11 Commits
feature/un
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbbc4c0372 | ||
|
|
c7e708cf7d | ||
|
|
6d3a30c799 | ||
|
|
4c972e1bc4 | ||
|
|
144571e448 | ||
|
|
a24c64da8f | ||
|
|
2faa03757c | ||
|
|
9cef2c4c97 | ||
|
|
ea6b141d42 | ||
|
|
c0877dd0ab | ||
|
|
5c31ccaddd |
4
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -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
|
||||
5
frontend/env.config.d.ts
vendored
5
frontend/env.config.d.ts
vendored
@@ -1,4 +1 @@
|
||||
declare module 'postcss-easing-gradients' {
|
||||
import postcssEasingGradients from 'postcss-easing-gradients'
|
||||
export default postcssEasingGradients
|
||||
}
|
||||
declare module 'postcss-easing-gradients';
|
||||
|
||||
@@ -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",
|
||||
@@ -152,7 +152,7 @@
|
||||
"vite-plugin-sentry": "1.4.0",
|
||||
"vite-plugin-vue-devtools": "7.7.0",
|
||||
"vite-svg-loader": "5.1.0",
|
||||
"vitest": "3.0.1",
|
||||
"vitest": "3.0.2",
|
||||
"vue-tsc": "2.2.0",
|
||||
"wait-on": "8.0.2",
|
||||
"workbox-cli": "7.3.0"
|
||||
|
||||
106
frontend/pnpm-lock.yaml
generated
106
frontend/pnpm-lock.yaml
generated
@@ -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
|
||||
@@ -321,8 +321,8 @@ importers:
|
||||
specifier: 5.1.0
|
||||
version: 5.1.0(vue@3.5.13(typescript@5.7.3))
|
||||
vitest:
|
||||
specifier: 3.0.1
|
||||
version: 3.0.1(@types/node@22.10.7)(happy-dom@16.6.0)(jiti@1.21.6)(jsdom@20.0.3)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0)
|
||||
specifier: 3.0.2
|
||||
version: 3.0.2(@types/node@22.10.7)(happy-dom@16.6.0)(jiti@1.21.6)(jsdom@20.0.3)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0)
|
||||
vue-tsc:
|
||||
specifier: 2.2.0
|
||||
version: 2.2.0(typescript@5.7.3)
|
||||
@@ -2514,11 +2514,11 @@ packages:
|
||||
vite: ^5.0.0 || ^6.0.0
|
||||
vue: ^3.2.25
|
||||
|
||||
'@vitest/expect@3.0.1':
|
||||
resolution: {integrity: sha512-oPrXe8dwvQdzUxQFWwibY97/smQ6k8iPVeSf09KEvU1yWzu40G6naHExY0lUgjnTPWMRGQOJnhMBb8lBu48feg==}
|
||||
'@vitest/expect@3.0.2':
|
||||
resolution: {integrity: sha512-dKSHLBcoZI+3pmP5hiZ7I5grNru2HRtEW8Z5Zp4IXog8QYcxhlox7JUPyIIFWfN53+3HW3KPLIl6nSzUGgKSuQ==}
|
||||
|
||||
'@vitest/mocker@3.0.1':
|
||||
resolution: {integrity: sha512-5letLsVdFhReCPws/SNwyekBCyi4w2IusycV4T7eVdt2mfellS2yKDrEmnE5KPCHr0Ez5xCZVJbJws3ckuNNgQ==}
|
||||
'@vitest/mocker@3.0.2':
|
||||
resolution: {integrity: sha512-Hr09FoBf0jlwwSyzIF4Xw31OntpO3XtZjkccpcBf8FeVW3tpiyKlkeUzxS/txzHqpUCNIX157NaTySxedyZLvA==}
|
||||
peerDependencies:
|
||||
msw: ^2.4.9
|
||||
vite: ^5.0.0 || ^6.0.0
|
||||
@@ -2528,20 +2528,20 @@ packages:
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
'@vitest/pretty-format@3.0.1':
|
||||
resolution: {integrity: sha512-FnyGQ9eFJ/Dnqg3jCvq9O6noXtxbZhOlSvNLZsCGJxhsGiZ5LDepmsTCizRfyGJt4Q6pJmZtx7rO/qqr9R9gDA==}
|
||||
'@vitest/pretty-format@3.0.2':
|
||||
resolution: {integrity: sha512-yBohcBw/T/p0/JRgYD+IYcjCmuHzjC3WLAKsVE4/LwiubzZkE8N49/xIQ/KGQwDRA8PaviF8IRO8JMWMngdVVQ==}
|
||||
|
||||
'@vitest/runner@3.0.1':
|
||||
resolution: {integrity: sha512-LfVbbYOduTVx8PnYFGH98jpgubHBefIppbPQJBSlgjnRRlaX/KR6J46htECUHpf+ElJZ4xxssAfEz/Cb2iIMYA==}
|
||||
'@vitest/runner@3.0.2':
|
||||
resolution: {integrity: sha512-GHEsWoncrGxWuW8s405fVoDfSLk6RF2LCXp6XhevbtDjdDme1WV/eNmUueDfpY1IX3MJaCRelVCEXsT9cArfEg==}
|
||||
|
||||
'@vitest/snapshot@3.0.1':
|
||||
resolution: {integrity: sha512-ZYV+iw2lGyc4QY2xt61b7Y3NJhSAO7UWcYWMcV0UnMrkXa8hXtfZES6WAk4g7Jr3p4qJm1P0cgDcOFyY5me+Ug==}
|
||||
'@vitest/snapshot@3.0.2':
|
||||
resolution: {integrity: sha512-h9s67yD4+g+JoYG0zPCo/cLTabpDqzqNdzMawmNPzDStTiwxwkyYM1v5lWE8gmGv3SVJ2DcxA2NpQJZJv9ym3g==}
|
||||
|
||||
'@vitest/spy@3.0.1':
|
||||
resolution: {integrity: sha512-HnGJB3JFflnlka4u7aD0CfqrEtX3FgNaZAar18/KIhfo0r/WADn9PhBfiqAmNw4R/xaRcLzLPFXDwEQV1vHlJA==}
|
||||
'@vitest/spy@3.0.2':
|
||||
resolution: {integrity: sha512-8mI2iUn+PJFMT44e3ISA1R+K6ALVs47W6eriDTfXe6lFqlflID05MB4+rIFhmDSLBj8iBsZkzBYlgSkinxLzSQ==}
|
||||
|
||||
'@vitest/utils@3.0.1':
|
||||
resolution: {integrity: sha512-i+Gm61rfIeSitPUsu4ZcWqucfb18ShAanRpOG6KlXfd1j6JVK5XxO2Z6lEmfjMnAQRIvvLtJ3JByzDTv347e8w==}
|
||||
'@vitest/utils@3.0.2':
|
||||
resolution: {integrity: sha512-Qu01ZYZlgHvDP02JnMBRpX43nRaZtNpIzw3C1clDXmn8eakgX6iQVGzTQ/NjkIr64WD8ioqOjkaYRVvHQI5qiw==}
|
||||
|
||||
'@volar/language-core@2.4.11':
|
||||
resolution: {integrity: sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==}
|
||||
@@ -2947,8 +2947,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==}
|
||||
@@ -6253,8 +6253,8 @@ packages:
|
||||
engines: {node: '>=v14.18.0'}
|
||||
hasBin: true
|
||||
|
||||
vite-node@3.0.1:
|
||||
resolution: {integrity: sha512-PoH9mCNsSZQXl3gdymM5IE4WR0k0WbnFd89nAyyDvltF2jVGdFcI8vpB1PBdKTcjAR7kkYiHSlIO68X/UT8Q1A==}
|
||||
vite-node@3.0.2:
|
||||
resolution: {integrity: sha512-hsEQerBAHvVAbv40m3TFQe/lTEbOp7yDpyqMJqr2Tnd+W58+DEYOt+fluQgekOePcsNBmR77lpVAnIU2Xu4SvQ==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
hasBin: true
|
||||
|
||||
@@ -6373,15 +6373,15 @@ packages:
|
||||
yaml:
|
||||
optional: true
|
||||
|
||||
vitest@3.0.1:
|
||||
resolution: {integrity: sha512-SWKoSAkxtFHqt8biR3eN53dzmeWkigEpyipqfblcsoAghVvoFMpxQEj0gc7AajMi6Ra49fjcTN6v4AxklmS4aQ==}
|
||||
vitest@3.0.2:
|
||||
resolution: {integrity: sha512-5bzaHakQ0hmVVKLhfh/jXf6oETDBtgPo8tQCHYB+wftNgFJ+Hah67IsWc8ivx4vFL025Ow8UiuTf4W57z4izvQ==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@edge-runtime/vm': '*'
|
||||
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
||||
'@vitest/browser': 3.0.1
|
||||
'@vitest/ui': 3.0.1
|
||||
'@vitest/browser': 3.0.2
|
||||
'@vitest/ui': 3.0.2
|
||||
happy-dom: '*'
|
||||
jsdom: '*'
|
||||
peerDependenciesMeta:
|
||||
@@ -8978,43 +8978,43 @@ snapshots:
|
||||
vite: 6.0.7(@types/node@22.10.7)(jiti@1.21.6)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0)
|
||||
vue: 3.5.13(typescript@5.7.3)
|
||||
|
||||
'@vitest/expect@3.0.1':
|
||||
'@vitest/expect@3.0.2':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.0.1
|
||||
'@vitest/utils': 3.0.1
|
||||
'@vitest/spy': 3.0.2
|
||||
'@vitest/utils': 3.0.2
|
||||
chai: 5.1.2
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/mocker@3.0.1(vite@6.0.7(@types/node@22.10.7)(jiti@1.21.6)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0))':
|
||||
'@vitest/mocker@3.0.2(vite@6.0.7(@types/node@22.10.7)(jiti@1.21.6)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0))':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.0.1
|
||||
'@vitest/spy': 3.0.2
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.17
|
||||
optionalDependencies:
|
||||
vite: 6.0.7(@types/node@22.10.7)(jiti@1.21.6)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0)
|
||||
|
||||
'@vitest/pretty-format@3.0.1':
|
||||
'@vitest/pretty-format@3.0.2':
|
||||
dependencies:
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/runner@3.0.1':
|
||||
'@vitest/runner@3.0.2':
|
||||
dependencies:
|
||||
'@vitest/utils': 3.0.1
|
||||
'@vitest/utils': 3.0.2
|
||||
pathe: 2.0.1
|
||||
|
||||
'@vitest/snapshot@3.0.1':
|
||||
'@vitest/snapshot@3.0.2':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 3.0.1
|
||||
'@vitest/pretty-format': 3.0.2
|
||||
magic-string: 0.30.17
|
||||
pathe: 2.0.1
|
||||
|
||||
'@vitest/spy@3.0.1':
|
||||
'@vitest/spy@3.0.2':
|
||||
dependencies:
|
||||
tinyspy: 3.0.2
|
||||
|
||||
'@vitest/utils@3.0.1':
|
||||
'@vitest/utils@3.0.2':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 3.0.1
|
||||
'@vitest/pretty-format': 3.0.2
|
||||
loupe: 3.1.2
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
@@ -9081,7 +9081,7 @@ snapshots:
|
||||
'@vue/compiler-ssr': 3.5.13
|
||||
'@vue/shared': 3.5.13
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.14
|
||||
magic-string: 0.30.17
|
||||
postcss: 8.5.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
@@ -9349,7 +9349,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 +9450,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 +9513,7 @@ snapshots:
|
||||
|
||||
camelcase@5.3.1: {}
|
||||
|
||||
caniuse-lite@1.0.30001692: {}
|
||||
caniuse-lite@1.0.30001695: {}
|
||||
|
||||
capital-case@1.0.4:
|
||||
dependencies:
|
||||
@@ -13148,7 +13148,7 @@ snapshots:
|
||||
- supports-color
|
||||
- terser
|
||||
|
||||
vite-node@3.0.1(@types/node@22.10.7)(jiti@1.21.6)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0):
|
||||
vite-node@3.0.2(@types/node@22.10.7)(jiti@1.21.6)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
@@ -13266,15 +13266,15 @@ snapshots:
|
||||
terser: 5.31.6
|
||||
yaml: 2.5.0
|
||||
|
||||
vitest@3.0.1(@types/node@22.10.7)(happy-dom@16.6.0)(jiti@1.21.6)(jsdom@20.0.3)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0):
|
||||
vitest@3.0.2(@types/node@22.10.7)(happy-dom@16.6.0)(jiti@1.21.6)(jsdom@20.0.3)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0):
|
||||
dependencies:
|
||||
'@vitest/expect': 3.0.1
|
||||
'@vitest/mocker': 3.0.1(vite@6.0.7(@types/node@22.10.7)(jiti@1.21.6)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0))
|
||||
'@vitest/pretty-format': 3.0.1
|
||||
'@vitest/runner': 3.0.1
|
||||
'@vitest/snapshot': 3.0.1
|
||||
'@vitest/spy': 3.0.1
|
||||
'@vitest/utils': 3.0.1
|
||||
'@vitest/expect': 3.0.2
|
||||
'@vitest/mocker': 3.0.2(vite@6.0.7(@types/node@22.10.7)(jiti@1.21.6)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0))
|
||||
'@vitest/pretty-format': 3.0.2
|
||||
'@vitest/runner': 3.0.2
|
||||
'@vitest/snapshot': 3.0.2
|
||||
'@vitest/spy': 3.0.2
|
||||
'@vitest/utils': 3.0.2
|
||||
chai: 5.1.2
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
expect-type: 1.1.0
|
||||
@@ -13286,7 +13286,7 @@ snapshots:
|
||||
tinypool: 1.0.2
|
||||
tinyrainbow: 2.0.0
|
||||
vite: 6.0.7(@types/node@22.10.7)(jiti@1.21.6)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0)
|
||||
vite-node: 3.0.1(@types/node@22.10.7)(jiti@1.21.6)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0)
|
||||
vite-node: 3.0.2(@types/node@22.10.7)(jiti@1.21.6)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 22.10.7
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,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:
|
||||
|
||||
@@ -1124,7 +1124,7 @@
|
||||
}
|
||||
},
|
||||
"date": {
|
||||
"locale": "nb_NO",
|
||||
"locale": "nn",
|
||||
"altFormatLong": "d.m.y H:i",
|
||||
"altFormatShort": "d.m.y"
|
||||
},
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
2
go.mod
@@ -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
2
go.sum
@@ -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=
|
||||
|
||||
Reference in New Issue
Block a user