Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ad9c69fca | ||
|
|
6fddac75f9 |
@@ -1,6 +1,6 @@
|
||||
name: Bug Report
|
||||
description: Found something you weren't expecting? Report it here!
|
||||
type: Bug
|
||||
labels: kind/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
|
||||
|
||||
@@ -11,8 +11,6 @@ stats.html
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-dev
|
||||
dist-test
|
||||
coverage
|
||||
*.zip
|
||||
.vite/
|
||||
|
||||
+18
-2
@@ -2,14 +2,30 @@
|
||||
|
||||
> The todo app to organize your life.
|
||||
|
||||
[](https://drone.kolaente.de/vikunja/vikunja)
|
||||
[](LICENSE)
|
||||
[](https://dl.vikunja.io)
|
||||
[](https://crowdin.com/project/vikunja)
|
||||
|
||||
This is the web frontend for Vikunja, written in Vue.js.
|
||||
|
||||
Take a look at [our roadmap](https://my.vikunja.cloud/share/UrdhKPqumxDXUbYpEGJLSIyNTwAnbBzVlwdDpRbv/auth) (hosted on Vikunja!) for a list of things we're currently working on!
|
||||
|
||||
For general information about the project, refer to the top-level readme of this repo.
|
||||
## Security Reports
|
||||
|
||||
If you find any security-related issues you don't want to disclose publicly, please use [the contact information on our website](https://vikunja.io/contact/#security).
|
||||
|
||||
## Docker
|
||||
|
||||
There is a [docker image available](https://hub.docker.com/r/vikunja/vikunja) with support for http/2 and aggressive caching enabled.
|
||||
In order to build it from sources run the command below. (Docker >= v19.03)
|
||||
|
||||
```shell
|
||||
export DOCKER_BUILDKIT=1
|
||||
docker build -t vikunja/frontend .
|
||||
```
|
||||
|
||||
Refer to [multi-platform documentation](https://docs.docker.com/build/building/multi-platform/) in order to build for different platforms.
|
||||
|
||||
## Project setup
|
||||
|
||||
@@ -20,7 +36,7 @@ pnpm install
|
||||
### Compiles and hot-reloads for development
|
||||
|
||||
```shell
|
||||
pnpm run dev
|
||||
pnpm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
|
||||
Vendored
+4
-1
@@ -1 +1,4 @@
|
||||
declare module 'postcss-easing-gradients';
|
||||
declare module 'postcss-easing-gradients' {
|
||||
import postcssEasingGradients from 'postcss-easing-gradients'
|
||||
export default postcssEasingGradients
|
||||
}
|
||||
@@ -93,13 +93,14 @@
|
||||
"floating-vue": "5.2.2",
|
||||
"is-touch-device": "1.0.1",
|
||||
"klona": "2.0.6",
|
||||
"lowlight": "3.3.0",
|
||||
"lowlight": "2.9.0",
|
||||
"pinia": "2.3.0",
|
||||
"register-service-worker": "1.7.2",
|
||||
"sortablejs": "1.15.6",
|
||||
"tailwindcss": "3.4.17",
|
||||
"tippy.js": "6.3.7",
|
||||
"ufo": "1.5.4",
|
||||
"validator": "13.12.0",
|
||||
"vue": "3.5.13",
|
||||
"vue-advanced-cropper": "2.8.9",
|
||||
"vue-flatpickr-component": "11.0.5",
|
||||
@@ -107,7 +108,8 @@
|
||||
"vue-router": "4.5.0",
|
||||
"vuemoji-picker": "0.3.2",
|
||||
"workbox-precaching": "7.3.0",
|
||||
"zhyswan-vuedraggable": "4.1.3"
|
||||
"zhyswan-vuedraggable": "4.1.3",
|
||||
"zod": "3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@4tw/cypress-drag-drop": "2.2.5",
|
||||
@@ -122,6 +124,7 @@
|
||||
"@types/is-touch-device": "1.0.3",
|
||||
"@types/node": "22.10.7",
|
||||
"@types/sortablejs": "1.15.8",
|
||||
"@types/validator": "13.12.2",
|
||||
"@typescript-eslint/eslint-plugin": "8.20.0",
|
||||
"@typescript-eslint/parser": "8.20.0",
|
||||
"@vitejs/plugin-vue": "5.2.1",
|
||||
@@ -130,9 +133,9 @@
|
||||
"@vue/tsconfig": "0.7.0",
|
||||
"autoprefixer": "10.4.20",
|
||||
"browserslist": "4.24.4",
|
||||
"caniuse-lite": "1.0.30001695",
|
||||
"caniuse-lite": "1.0.30001692",
|
||||
"csstype": "3.1.3",
|
||||
"cypress": "14.0.0",
|
||||
"cypress": "13.17.0",
|
||||
"esbuild": "0.24.2",
|
||||
"eslint": "9.18.0",
|
||||
"eslint-plugin-vue": "9.32.0",
|
||||
@@ -152,7 +155,7 @@
|
||||
"vite-plugin-sentry": "1.4.0",
|
||||
"vite-plugin-vue-devtools": "7.7.0",
|
||||
"vite-svg-loader": "5.1.0",
|
||||
"vitest": "3.0.2",
|
||||
"vitest": "2.1.8",
|
||||
"vue-tsc": "2.2.0",
|
||||
"wait-on": "8.0.2",
|
||||
"workbox-cli": "7.3.0"
|
||||
|
||||
Generated
+158
-151
@@ -54,7 +54,7 @@ importers:
|
||||
version: 2.11.2(@tiptap/core@2.11.2(@tiptap/pm@2.11.2))(@tiptap/pm@2.11.2)
|
||||
'@tiptap/extension-code-block-lowlight':
|
||||
specifier: 2.11.2
|
||||
version: 2.11.2(@tiptap/core@2.11.2(@tiptap/pm@2.11.2))(@tiptap/extension-code-block@2.11.2(@tiptap/core@2.11.2(@tiptap/pm@2.11.2))(@tiptap/pm@2.11.2))(@tiptap/pm@2.11.2)(highlight.js@11.11.1)(lowlight@3.3.0)
|
||||
version: 2.11.2(@tiptap/core@2.11.2(@tiptap/pm@2.11.2))(@tiptap/extension-code-block@2.11.2(@tiptap/core@2.11.2(@tiptap/pm@2.11.2))(@tiptap/pm@2.11.2))(@tiptap/pm@2.11.2)(highlight.js@11.8.0)(lowlight@2.9.0)
|
||||
'@tiptap/extension-hard-break':
|
||||
specifier: 2.11.2
|
||||
version: 2.11.2(@tiptap/core@2.11.2(@tiptap/pm@2.11.2))
|
||||
@@ -149,8 +149,8 @@ importers:
|
||||
specifier: 2.0.6
|
||||
version: 2.0.6
|
||||
lowlight:
|
||||
specifier: 3.3.0
|
||||
version: 3.3.0
|
||||
specifier: 2.9.0
|
||||
version: 2.9.0
|
||||
pinia:
|
||||
specifier: 2.3.0
|
||||
version: 2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3))
|
||||
@@ -169,6 +169,9 @@ importers:
|
||||
ufo:
|
||||
specifier: 1.5.4
|
||||
version: 1.5.4
|
||||
validator:
|
||||
specifier: 13.12.0
|
||||
version: 13.12.0
|
||||
vue:
|
||||
specifier: 3.5.13
|
||||
version: 3.5.13(typescript@5.7.3)
|
||||
@@ -193,16 +196,19 @@ importers:
|
||||
zhyswan-vuedraggable:
|
||||
specifier: 4.1.3
|
||||
version: 4.1.3(vue@3.5.13(typescript@5.7.3))
|
||||
zod:
|
||||
specifier: 3.24.1
|
||||
version: 3.24.1
|
||||
devDependencies:
|
||||
'@4tw/cypress-drag-drop':
|
||||
specifier: 2.2.5
|
||||
version: 2.2.5(cypress@14.0.0)
|
||||
version: 2.2.5(cypress@13.17.0)
|
||||
'@cypress/vite-dev-server':
|
||||
specifier: 6.0.1
|
||||
version: 6.0.1(cypress@14.0.0)
|
||||
version: 6.0.1(cypress@13.17.0)
|
||||
'@cypress/vue':
|
||||
specifier: 6.0.2
|
||||
version: 6.0.2(cypress@14.0.0)(vue@3.5.13(typescript@5.7.3))
|
||||
version: 6.0.2(cypress@13.17.0)(vue@3.5.13(typescript@5.7.3))
|
||||
'@faker-js/faker':
|
||||
specifier: 9.4.0
|
||||
version: 9.4.0
|
||||
@@ -230,6 +236,9 @@ importers:
|
||||
'@types/sortablejs':
|
||||
specifier: 1.15.8
|
||||
version: 1.15.8
|
||||
'@types/validator':
|
||||
specifier: 13.12.2
|
||||
version: 13.12.2
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: 8.20.0
|
||||
version: 8.20.0(@typescript-eslint/parser@8.20.0(eslint@9.18.0(jiti@1.21.6))(typescript@5.7.3))(eslint@9.18.0(jiti@1.21.6))(typescript@5.7.3)
|
||||
@@ -255,14 +264,14 @@ importers:
|
||||
specifier: 4.24.4
|
||||
version: 4.24.4
|
||||
caniuse-lite:
|
||||
specifier: 1.0.30001695
|
||||
version: 1.0.30001695
|
||||
specifier: 1.0.30001692
|
||||
version: 1.0.30001692
|
||||
csstype:
|
||||
specifier: 3.1.3
|
||||
version: 3.1.3
|
||||
cypress:
|
||||
specifier: 14.0.0
|
||||
version: 14.0.0
|
||||
specifier: 13.17.0
|
||||
version: 13.17.0
|
||||
esbuild:
|
||||
specifier: 0.24.2
|
||||
version: 0.24.2
|
||||
@@ -321,8 +330,8 @@ importers:
|
||||
specifier: 5.1.0
|
||||
version: 5.1.0(vue@3.5.13(typescript@5.7.3))
|
||||
vitest:
|
||||
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)
|
||||
specifier: 2.1.8
|
||||
version: 2.1.8(@types/node@22.10.7)(happy-dom@16.6.0)(jsdom@20.0.3)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)
|
||||
vue-tsc:
|
||||
specifier: 2.2.0
|
||||
version: 2.2.0(typescript@5.7.3)
|
||||
@@ -2376,8 +2385,8 @@ packages:
|
||||
'@types/har-format@1.2.15':
|
||||
resolution: {integrity: sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==}
|
||||
|
||||
'@types/hast@3.0.4':
|
||||
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
|
||||
'@types/hast@2.3.10':
|
||||
resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==}
|
||||
|
||||
'@types/is-touch-device@1.0.3':
|
||||
resolution: {integrity: sha512-mEDTb0fQdHRzXLKbwTcpLxFFcQAzfUz6JdSj4qOS8ZevHH78umROLg1ftssalYHKjHz3qPgXBY9DrPsOFcDwMQ==}
|
||||
@@ -2433,6 +2442,9 @@ packages:
|
||||
'@types/unist@2.0.11':
|
||||
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
|
||||
|
||||
'@types/validator@13.12.2':
|
||||
resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==}
|
||||
|
||||
'@types/web-bluetooth@0.0.16':
|
||||
resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
|
||||
|
||||
@@ -2514,34 +2526,34 @@ packages:
|
||||
vite: ^5.0.0 || ^6.0.0
|
||||
vue: ^3.2.25
|
||||
|
||||
'@vitest/expect@3.0.2':
|
||||
resolution: {integrity: sha512-dKSHLBcoZI+3pmP5hiZ7I5grNru2HRtEW8Z5Zp4IXog8QYcxhlox7JUPyIIFWfN53+3HW3KPLIl6nSzUGgKSuQ==}
|
||||
'@vitest/expect@2.1.8':
|
||||
resolution: {integrity: sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==}
|
||||
|
||||
'@vitest/mocker@3.0.2':
|
||||
resolution: {integrity: sha512-Hr09FoBf0jlwwSyzIF4Xw31OntpO3XtZjkccpcBf8FeVW3tpiyKlkeUzxS/txzHqpUCNIX157NaTySxedyZLvA==}
|
||||
'@vitest/mocker@2.1.8':
|
||||
resolution: {integrity: sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==}
|
||||
peerDependencies:
|
||||
msw: ^2.4.9
|
||||
vite: ^5.0.0 || ^6.0.0
|
||||
vite: ^5.0.0
|
||||
peerDependenciesMeta:
|
||||
msw:
|
||||
optional: true
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
'@vitest/pretty-format@3.0.2':
|
||||
resolution: {integrity: sha512-yBohcBw/T/p0/JRgYD+IYcjCmuHzjC3WLAKsVE4/LwiubzZkE8N49/xIQ/KGQwDRA8PaviF8IRO8JMWMngdVVQ==}
|
||||
'@vitest/pretty-format@2.1.8':
|
||||
resolution: {integrity: sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==}
|
||||
|
||||
'@vitest/runner@3.0.2':
|
||||
resolution: {integrity: sha512-GHEsWoncrGxWuW8s405fVoDfSLk6RF2LCXp6XhevbtDjdDme1WV/eNmUueDfpY1IX3MJaCRelVCEXsT9cArfEg==}
|
||||
'@vitest/runner@2.1.8':
|
||||
resolution: {integrity: sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==}
|
||||
|
||||
'@vitest/snapshot@3.0.2':
|
||||
resolution: {integrity: sha512-h9s67yD4+g+JoYG0zPCo/cLTabpDqzqNdzMawmNPzDStTiwxwkyYM1v5lWE8gmGv3SVJ2DcxA2NpQJZJv9ym3g==}
|
||||
'@vitest/snapshot@2.1.8':
|
||||
resolution: {integrity: sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==}
|
||||
|
||||
'@vitest/spy@3.0.2':
|
||||
resolution: {integrity: sha512-8mI2iUn+PJFMT44e3ISA1R+K6ALVs47W6eriDTfXe6lFqlflID05MB4+rIFhmDSLBj8iBsZkzBYlgSkinxLzSQ==}
|
||||
'@vitest/spy@2.1.8':
|
||||
resolution: {integrity: sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==}
|
||||
|
||||
'@vitest/utils@3.0.2':
|
||||
resolution: {integrity: sha512-Qu01ZYZlgHvDP02JnMBRpX43nRaZtNpIzw3C1clDXmn8eakgX6iQVGzTQ/NjkIr64WD8ioqOjkaYRVvHQI5qiw==}
|
||||
'@vitest/utils@2.1.8':
|
||||
resolution: {integrity: sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==}
|
||||
|
||||
'@volar/language-core@2.4.11':
|
||||
resolution: {integrity: sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==}
|
||||
@@ -2947,8 +2959,8 @@ packages:
|
||||
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
caniuse-lite@1.0.30001695:
|
||||
resolution: {integrity: sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==}
|
||||
caniuse-lite@1.0.30001692:
|
||||
resolution: {integrity: sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==}
|
||||
|
||||
capital-case@1.0.4:
|
||||
resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==}
|
||||
@@ -3200,9 +3212,9 @@ packages:
|
||||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
cypress@14.0.0:
|
||||
resolution: {integrity: sha512-kEGqQr23so5IpKeg/dp6GVi7RlHx1NmW66o2a2Q4wk9gRaAblLZQSiZJuDI8UMC4LlG5OJ7Q6joAiqTrfRNbTw==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
cypress@13.17.0:
|
||||
resolution: {integrity: sha512-5xWkaPurwkIljojFidhw8lFScyxhtiFHl/i/3zov+1Z5CmY4t9tjIdvSXfu82Y3w7wt0uR9KkucbhkVvJZLQSA==}
|
||||
engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
|
||||
dashdash@1.14.1:
|
||||
@@ -3351,18 +3363,11 @@ packages:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
|
||||
dequal@2.0.3:
|
||||
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
detect-libc@1.0.3:
|
||||
resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
|
||||
engines: {node: '>=0.10'}
|
||||
hasBin: true
|
||||
|
||||
devlop@1.1.0:
|
||||
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
|
||||
|
||||
devtools-protocol@0.0.981744:
|
||||
resolution: {integrity: sha512-0cuGS8+jhR67Fy7qG3i3Pc7Aw494sb9yG9QgpG97SFVWwolgYjlhJg7n+UaHxOQT30d1TYu/EYe9k01ivLErIg==}
|
||||
|
||||
@@ -3499,8 +3504,8 @@ packages:
|
||||
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-module-lexer@1.6.0:
|
||||
resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==}
|
||||
es-module-lexer@1.5.4:
|
||||
resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==}
|
||||
|
||||
es-object-atoms@1.0.0:
|
||||
resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
|
||||
@@ -3687,6 +3692,9 @@ packages:
|
||||
fastq@1.17.1:
|
||||
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
|
||||
|
||||
fault@2.0.1:
|
||||
resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==}
|
||||
|
||||
fd-slicer@1.1.0:
|
||||
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
|
||||
|
||||
@@ -3789,6 +3797,10 @@ packages:
|
||||
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
format@0.2.2:
|
||||
resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
|
||||
engines: {node: '>=0.4.x'}
|
||||
|
||||
formdata-polyfill@4.0.10:
|
||||
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
@@ -3979,8 +3991,8 @@ packages:
|
||||
header-case@2.0.4:
|
||||
resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==}
|
||||
|
||||
highlight.js@11.11.1:
|
||||
resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==}
|
||||
highlight.js@11.8.0:
|
||||
resolution: {integrity: sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
histoire@0.17.17:
|
||||
@@ -4501,8 +4513,8 @@ packages:
|
||||
resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
lowlight@3.3.0:
|
||||
resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==}
|
||||
lowlight@2.9.0:
|
||||
resolution: {integrity: sha512-OpcaUTCLmHuVuBcyNckKfH5B0oA4JUavb/M/8n9iAvanJYNQkrVm4pvyX0SUaqkBG4dnWHKt7p50B3ngAG2Rfw==}
|
||||
|
||||
lru-cache@10.4.3:
|
||||
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
||||
@@ -4516,9 +4528,6 @@ packages:
|
||||
magic-string@0.30.14:
|
||||
resolution: {integrity: sha512-5c99P1WKTed11ZC0HMJOj6CDIue6F8ySu+bJL+85q1zBEIY8IklrJ1eiKC2NDRh3Ct3FcvmJPyQHb9erXMTJNw==}
|
||||
|
||||
magic-string@0.30.17:
|
||||
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
|
||||
|
||||
make-dir@3.1.0:
|
||||
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -4919,9 +4928,6 @@ packages:
|
||||
pathe@1.1.2:
|
||||
resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
|
||||
|
||||
pathe@2.0.1:
|
||||
resolution: {integrity: sha512-6jpjMpOth5S9ITVu5clZ7NOgHNsv5vRQdheL9ztp2vZmM6fRbLvyua1tiBIL4lk8SAe3ARzeXEly6siXCjDHDw==}
|
||||
|
||||
pathval@2.0.0:
|
||||
resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
|
||||
engines: {node: '>= 14.16'}
|
||||
@@ -5957,19 +5963,19 @@ packages:
|
||||
tinybench@2.9.0:
|
||||
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||
|
||||
tinyexec@0.3.2:
|
||||
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
|
||||
tinyexec@0.3.1:
|
||||
resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==}
|
||||
|
||||
tinyglobby@0.2.10:
|
||||
resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tinypool@1.0.2:
|
||||
resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==}
|
||||
tinypool@1.0.1:
|
||||
resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
|
||||
tinyrainbow@2.0.0:
|
||||
resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
|
||||
tinyrainbow@1.2.0:
|
||||
resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
tinyspy@3.0.2:
|
||||
@@ -6236,6 +6242,10 @@ packages:
|
||||
validate-npm-package-license@3.0.4:
|
||||
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
|
||||
|
||||
validator@13.12.0:
|
||||
resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
varint@6.0.0:
|
||||
resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==}
|
||||
|
||||
@@ -6253,9 +6263,9 @@ packages:
|
||||
engines: {node: '>=v14.18.0'}
|
||||
hasBin: true
|
||||
|
||||
vite-node@3.0.2:
|
||||
resolution: {integrity: sha512-hsEQerBAHvVAbv40m3TFQe/lTEbOp7yDpyqMJqr2Tnd+W58+DEYOt+fluQgekOePcsNBmR77lpVAnIU2Xu4SvQ==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
vite-node@2.1.8:
|
||||
resolution: {integrity: sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
|
||||
vite-plugin-inspect@0.8.9:
|
||||
@@ -6373,15 +6383,15 @@ packages:
|
||||
yaml:
|
||||
optional: true
|
||||
|
||||
vitest@3.0.2:
|
||||
resolution: {integrity: sha512-5bzaHakQ0hmVVKLhfh/jXf6oETDBtgPo8tQCHYB+wftNgFJ+Hah67IsWc8ivx4vFL025Ow8UiuTf4W57z4izvQ==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
vitest@2.1.8:
|
||||
resolution: {integrity: sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@edge-runtime/vm': '*'
|
||||
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
||||
'@vitest/browser': 3.0.2
|
||||
'@vitest/ui': 3.0.2
|
||||
'@types/node': ^18.0.0 || >=20.0.0
|
||||
'@vitest/browser': 2.1.8
|
||||
'@vitest/ui': 2.1.8
|
||||
happy-dom: '*'
|
||||
jsdom: '*'
|
||||
peerDependenciesMeta:
|
||||
@@ -6706,11 +6716,14 @@ packages:
|
||||
peerDependencies:
|
||||
vue: ^3.0.1
|
||||
|
||||
zod@3.24.1:
|
||||
resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@4tw/cypress-drag-drop@2.2.5(cypress@14.0.0)':
|
||||
'@4tw/cypress-drag-drop@2.2.5(cypress@13.17.0)':
|
||||
dependencies:
|
||||
cypress: 14.0.0
|
||||
cypress: 13.17.0
|
||||
|
||||
'@akryum/tinypool@0.3.1': {}
|
||||
|
||||
@@ -7771,9 +7784,9 @@ snapshots:
|
||||
tunnel-agent: 0.6.0
|
||||
uuid: 8.3.2
|
||||
|
||||
'@cypress/vite-dev-server@6.0.1(cypress@14.0.0)':
|
||||
'@cypress/vite-dev-server@6.0.1(cypress@13.17.0)':
|
||||
dependencies:
|
||||
cypress: 14.0.0
|
||||
cypress: 13.17.0
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
find-up: 6.3.0
|
||||
node-html-parser: 5.3.3
|
||||
@@ -7781,9 +7794,9 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@cypress/vue@6.0.2(cypress@14.0.0)(vue@3.5.13(typescript@5.7.3))':
|
||||
'@cypress/vue@6.0.2(cypress@13.17.0)(vue@3.5.13(typescript@5.7.3))':
|
||||
dependencies:
|
||||
cypress: 14.0.0
|
||||
cypress: 13.17.0
|
||||
vue: 3.5.13(typescript@5.7.3)
|
||||
|
||||
'@cypress/xvfb@1.2.4(supports-color@8.1.1)':
|
||||
@@ -8582,13 +8595,13 @@ snapshots:
|
||||
dependencies:
|
||||
'@tiptap/core': 2.11.2(@tiptap/pm@2.11.2)
|
||||
|
||||
'@tiptap/extension-code-block-lowlight@2.11.2(@tiptap/core@2.11.2(@tiptap/pm@2.11.2))(@tiptap/extension-code-block@2.11.2(@tiptap/core@2.11.2(@tiptap/pm@2.11.2))(@tiptap/pm@2.11.2))(@tiptap/pm@2.11.2)(highlight.js@11.11.1)(lowlight@3.3.0)':
|
||||
'@tiptap/extension-code-block-lowlight@2.11.2(@tiptap/core@2.11.2(@tiptap/pm@2.11.2))(@tiptap/extension-code-block@2.11.2(@tiptap/core@2.11.2(@tiptap/pm@2.11.2))(@tiptap/pm@2.11.2))(@tiptap/pm@2.11.2)(highlight.js@11.8.0)(lowlight@2.9.0)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.11.2(@tiptap/pm@2.11.2)
|
||||
'@tiptap/extension-code-block': 2.11.2(@tiptap/core@2.11.2(@tiptap/pm@2.11.2))(@tiptap/pm@2.11.2)
|
||||
'@tiptap/pm': 2.11.2
|
||||
highlight.js: 11.11.1
|
||||
lowlight: 3.3.0
|
||||
highlight.js: 11.8.0
|
||||
lowlight: 2.9.0
|
||||
|
||||
'@tiptap/extension-code-block@2.11.2(@tiptap/core@2.11.2(@tiptap/pm@2.11.2))(@tiptap/pm@2.11.2)':
|
||||
dependencies:
|
||||
@@ -8807,7 +8820,7 @@ snapshots:
|
||||
|
||||
'@types/har-format@1.2.15': {}
|
||||
|
||||
'@types/hast@3.0.4':
|
||||
'@types/hast@2.3.10':
|
||||
dependencies:
|
||||
'@types/unist': 2.0.11
|
||||
|
||||
@@ -8861,6 +8874,8 @@ snapshots:
|
||||
|
||||
'@types/unist@2.0.11': {}
|
||||
|
||||
'@types/validator@13.12.2': {}
|
||||
|
||||
'@types/web-bluetooth@0.0.16': {}
|
||||
|
||||
'@types/web-bluetooth@0.0.20': {}
|
||||
@@ -8978,45 +8993,45 @@ 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.2':
|
||||
'@vitest/expect@2.1.8':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.0.2
|
||||
'@vitest/utils': 3.0.2
|
||||
'@vitest/spy': 2.1.8
|
||||
'@vitest/utils': 2.1.8
|
||||
chai: 5.1.2
|
||||
tinyrainbow: 2.0.0
|
||||
tinyrainbow: 1.2.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))':
|
||||
'@vitest/mocker@2.1.8(vite@5.4.11(@types/node@22.10.7)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6))':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.0.2
|
||||
'@vitest/spy': 2.1.8
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.17
|
||||
magic-string: 0.30.14
|
||||
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)
|
||||
vite: 5.4.11(@types/node@22.10.7)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)
|
||||
|
||||
'@vitest/pretty-format@3.0.2':
|
||||
'@vitest/pretty-format@2.1.8':
|
||||
dependencies:
|
||||
tinyrainbow: 2.0.0
|
||||
tinyrainbow: 1.2.0
|
||||
|
||||
'@vitest/runner@3.0.2':
|
||||
'@vitest/runner@2.1.8':
|
||||
dependencies:
|
||||
'@vitest/utils': 3.0.2
|
||||
pathe: 2.0.1
|
||||
'@vitest/utils': 2.1.8
|
||||
pathe: 1.1.2
|
||||
|
||||
'@vitest/snapshot@3.0.2':
|
||||
'@vitest/snapshot@2.1.8':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 3.0.2
|
||||
magic-string: 0.30.17
|
||||
pathe: 2.0.1
|
||||
'@vitest/pretty-format': 2.1.8
|
||||
magic-string: 0.30.14
|
||||
pathe: 1.1.2
|
||||
|
||||
'@vitest/spy@3.0.2':
|
||||
'@vitest/spy@2.1.8':
|
||||
dependencies:
|
||||
tinyspy: 3.0.2
|
||||
|
||||
'@vitest/utils@3.0.2':
|
||||
'@vitest/utils@2.1.8':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 3.0.2
|
||||
'@vitest/pretty-format': 2.1.8
|
||||
loupe: 3.1.2
|
||||
tinyrainbow: 2.0.0
|
||||
tinyrainbow: 1.2.0
|
||||
|
||||
'@volar/language-core@2.4.11':
|
||||
dependencies:
|
||||
@@ -9081,7 +9096,7 @@ snapshots:
|
||||
'@vue/compiler-ssr': 3.5.13
|
||||
'@vue/shared': 3.5.13
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.17
|
||||
magic-string: 0.30.14
|
||||
postcss: 8.5.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
@@ -9349,7 +9364,7 @@ snapshots:
|
||||
autoprefixer@10.4.20(postcss@8.5.1):
|
||||
dependencies:
|
||||
browserslist: 4.24.4
|
||||
caniuse-lite: 1.0.30001695
|
||||
caniuse-lite: 1.0.30001692
|
||||
fraction.js: 4.3.7
|
||||
normalize-range: 0.1.2
|
||||
picocolors: 1.0.1
|
||||
@@ -9450,7 +9465,7 @@ snapshots:
|
||||
|
||||
browserslist@4.24.4:
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001695
|
||||
caniuse-lite: 1.0.30001692
|
||||
electron-to-chromium: 1.5.73
|
||||
node-releases: 2.0.19
|
||||
update-browserslist-db: 1.1.1(browserslist@4.24.4)
|
||||
@@ -9513,7 +9528,7 @@ snapshots:
|
||||
|
||||
camelcase@5.3.1: {}
|
||||
|
||||
caniuse-lite@1.0.30001695: {}
|
||||
caniuse-lite@1.0.30001692: {}
|
||||
|
||||
capital-case@1.0.4:
|
||||
dependencies:
|
||||
@@ -9788,7 +9803,7 @@ snapshots:
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
cypress@14.0.0:
|
||||
cypress@13.17.0:
|
||||
dependencies:
|
||||
'@cypress/request': 3.0.6
|
||||
'@cypress/xvfb': 1.2.4(supports-color@8.1.1)
|
||||
@@ -9950,15 +9965,9 @@ snapshots:
|
||||
|
||||
delayed-stream@1.0.0: {}
|
||||
|
||||
dequal@2.0.3: {}
|
||||
|
||||
detect-libc@1.0.3:
|
||||
optional: true
|
||||
|
||||
devlop@1.1.0:
|
||||
dependencies:
|
||||
dequal: 2.0.3
|
||||
|
||||
devtools-protocol@0.0.981744: {}
|
||||
|
||||
diacritics@1.3.0: {}
|
||||
@@ -10136,7 +10145,7 @@ snapshots:
|
||||
|
||||
es-errors@1.3.0: {}
|
||||
|
||||
es-module-lexer@1.6.0: {}
|
||||
es-module-lexer@1.5.4: {}
|
||||
|
||||
es-object-atoms@1.0.0:
|
||||
dependencies:
|
||||
@@ -10438,6 +10447,10 @@ snapshots:
|
||||
dependencies:
|
||||
reusify: 1.0.4
|
||||
|
||||
fault@2.0.1:
|
||||
dependencies:
|
||||
format: 0.2.2
|
||||
|
||||
fd-slicer@1.1.0:
|
||||
dependencies:
|
||||
pend: 1.2.0
|
||||
@@ -10540,6 +10553,8 @@ snapshots:
|
||||
combined-stream: 1.0.8
|
||||
mime-types: 2.1.35
|
||||
|
||||
format@0.2.2: {}
|
||||
|
||||
formdata-polyfill@4.0.10:
|
||||
dependencies:
|
||||
fetch-blob: 3.2.0
|
||||
@@ -10750,7 +10765,7 @@ snapshots:
|
||||
capital-case: 1.0.4
|
||||
tslib: 2.7.0
|
||||
|
||||
highlight.js@11.11.1: {}
|
||||
highlight.js@11.8.0: {}
|
||||
|
||||
histoire@0.17.17(@types/node@22.10.7)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(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:
|
||||
@@ -11281,11 +11296,11 @@ snapshots:
|
||||
|
||||
lowercase-keys@2.0.0: {}
|
||||
|
||||
lowlight@3.3.0:
|
||||
lowlight@2.9.0:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
devlop: 1.1.0
|
||||
highlight.js: 11.11.1
|
||||
'@types/hast': 2.3.10
|
||||
fault: 2.0.1
|
||||
highlight.js: 11.8.0
|
||||
|
||||
lru-cache@10.4.3: {}
|
||||
|
||||
@@ -11301,10 +11316,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
|
||||
magic-string@0.30.17:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
|
||||
make-dir@3.1.0:
|
||||
dependencies:
|
||||
semver: 6.3.1
|
||||
@@ -11679,8 +11690,6 @@ snapshots:
|
||||
|
||||
pathe@1.1.2: {}
|
||||
|
||||
pathe@2.0.1: {}
|
||||
|
||||
pathval@2.0.0: {}
|
||||
|
||||
pause-stream@0.0.11:
|
||||
@@ -12853,16 +12862,16 @@ snapshots:
|
||||
|
||||
tinybench@2.9.0: {}
|
||||
|
||||
tinyexec@0.3.2: {}
|
||||
tinyexec@0.3.1: {}
|
||||
|
||||
tinyglobby@0.2.10:
|
||||
dependencies:
|
||||
fdir: 6.4.2(picomatch@4.0.2)
|
||||
picomatch: 4.0.2
|
||||
|
||||
tinypool@1.0.2: {}
|
||||
tinypool@1.0.1: {}
|
||||
|
||||
tinyrainbow@2.0.0: {}
|
||||
tinyrainbow@1.2.0: {}
|
||||
|
||||
tinyspy@3.0.2: {}
|
||||
|
||||
@@ -13117,6 +13126,8 @@ snapshots:
|
||||
spdx-correct: 3.2.0
|
||||
spdx-expression-parse: 3.0.1
|
||||
|
||||
validator@13.12.0: {}
|
||||
|
||||
varint@6.0.0: {}
|
||||
|
||||
verror@1.10.0:
|
||||
@@ -13148,16 +13159,15 @@ snapshots:
|
||||
- supports-color
|
||||
- terser
|
||||
|
||||
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):
|
||||
vite-node@2.1.8(@types/node@22.10.7)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
es-module-lexer: 1.6.0
|
||||
pathe: 2.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)
|
||||
es-module-lexer: 1.5.4
|
||||
pathe: 1.1.2
|
||||
vite: 5.4.11(@types/node@22.10.7)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- jiti
|
||||
- less
|
||||
- lightningcss
|
||||
- sass
|
||||
@@ -13166,8 +13176,6 @@ snapshots:
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vite-plugin-inspect@0.8.9(rollup@4.30.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)):
|
||||
dependencies:
|
||||
@@ -13266,34 +13274,33 @@ snapshots:
|
||||
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):
|
||||
vitest@2.1.8(@types/node@22.10.7)(happy-dom@16.6.0)(jsdom@20.0.3)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6):
|
||||
dependencies:
|
||||
'@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
|
||||
'@vitest/expect': 2.1.8
|
||||
'@vitest/mocker': 2.1.8(vite@5.4.11(@types/node@22.10.7)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6))
|
||||
'@vitest/pretty-format': 2.1.8
|
||||
'@vitest/runner': 2.1.8
|
||||
'@vitest/snapshot': 2.1.8
|
||||
'@vitest/spy': 2.1.8
|
||||
'@vitest/utils': 2.1.8
|
||||
chai: 5.1.2
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.3.7
|
||||
expect-type: 1.1.0
|
||||
magic-string: 0.30.17
|
||||
pathe: 2.0.1
|
||||
magic-string: 0.30.14
|
||||
pathe: 1.1.2
|
||||
std-env: 3.8.0
|
||||
tinybench: 2.9.0
|
||||
tinyexec: 0.3.2
|
||||
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.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)
|
||||
tinyexec: 0.3.1
|
||||
tinypool: 1.0.1
|
||||
tinyrainbow: 1.2.0
|
||||
vite: 5.4.11(@types/node@22.10.7)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)
|
||||
vite-node: 2.1.8(@types/node@22.10.7)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 22.10.7
|
||||
happy-dom: 16.6.0
|
||||
jsdom: 20.0.3
|
||||
transitivePeerDependencies:
|
||||
- jiti
|
||||
- less
|
||||
- lightningcss
|
||||
- msw
|
||||
@@ -13303,8 +13310,6 @@ snapshots:
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vscode-uri@3.0.8: {}
|
||||
|
||||
@@ -13675,3 +13680,5 @@ snapshots:
|
||||
dependencies:
|
||||
sortablejs: 1.14.0
|
||||
vue: 3.5.13(typescript@5.7.3)
|
||||
|
||||
zod@3.24.1: {}
|
||||
|
||||
@@ -167,7 +167,7 @@ import {Node} from '@tiptap/pm/model'
|
||||
import Commands from './commands'
|
||||
import suggestionSetup from './suggestion'
|
||||
|
||||
import {common, createLowlight} from 'lowlight'
|
||||
import {lowlight} from 'lowlight'
|
||||
|
||||
import type {BottomAction, UploadCallback} from './types'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
@@ -343,7 +343,7 @@ const extensions : Extensions = [
|
||||
}),
|
||||
|
||||
CodeBlockLowlight.configure({
|
||||
lowlight: createLowlight(common),
|
||||
lowlight,
|
||||
}),
|
||||
HardBreak.extend({
|
||||
addKeyboardShortcuts() {
|
||||
|
||||
@@ -63,7 +63,7 @@ const motd = computed(() => configStore.motd)
|
||||
|
||||
const route = useRoute()
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
const title = computed(() => route.meta?.title ? t(route.meta.title as string) : '')
|
||||
const title = computed(() => t(route.meta?.title as string || ''))
|
||||
useTitle(() => title.value)
|
||||
|
||||
</script>
|
||||
|
||||
@@ -62,22 +62,29 @@ export default {inheritAttrs: false}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, onBeforeUnmount, onMounted, ref, toRefs, useAttrs, watch, watchEffect} from 'vue'
|
||||
import {computed, onBeforeUnmount, onMounted, ref, toRefs, useAttrs, watch, watchEffect, type PropType} from 'vue'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: DateOption | DateOption[] | null,
|
||||
/** https://flatpickr.js.org/options/ */
|
||||
config: Options,
|
||||
events: HookKey[],
|
||||
disabled: boolean,
|
||||
}>(), {
|
||||
modelValue: null,
|
||||
config: () => ({
|
||||
defaultDate: undefined,
|
||||
wrap: false,
|
||||
}),
|
||||
events: () => includedEvents,
|
||||
disabled: false,
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [String, Number, Date, Array] as PropType<DateOption | DateOption[] | null>,
|
||||
default: null,
|
||||
},
|
||||
// https://flatpickr.js.org/options/
|
||||
config: {
|
||||
type: Object as PropType<Options>,
|
||||
default: () => ({
|
||||
defaultDate: null,
|
||||
wrap: false,
|
||||
}),
|
||||
},
|
||||
events: {
|
||||
type: Array as PropType<HookKey[]>,
|
||||
default: () => includedEvents,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits([
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
<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) }}
|
||||
@@ -40,37 +37,43 @@
|
||||
</Message>
|
||||
</CustomTransition>
|
||||
|
||||
<slot v-if="!isLoadingProject" />
|
||||
<slot v-if="loadedProjectId" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {computed, ref, watch} from 'vue'
|
||||
import {useRoute} from 'vue-router'
|
||||
|
||||
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' ? {
|
||||
@@ -84,6 +87,61 @@ 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,7 +1,6 @@
|
||||
<template>
|
||||
<ProjectWrapper
|
||||
class="project-gantt"
|
||||
:is-loading-project="isLoadingProject"
|
||||
:project-id="filters.projectId"
|
||||
:view-id
|
||||
>
|
||||
@@ -96,7 +95,6 @@ import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
type Options = Flatpickr.Options.Options
|
||||
|
||||
const props = defineProps<{
|
||||
isLoadingProject: boolean,
|
||||
route: RouteLocationNormalized
|
||||
viewId: IProjectView['id']
|
||||
}>()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<ProjectWrapper
|
||||
class="project-kanban"
|
||||
:is-loading-project="isLoadingProject"
|
||||
:project-id="projectId"
|
||||
:view-id
|
||||
>
|
||||
@@ -316,7 +315,6 @@ import TaskBucketService from '@/services/taskBucket'
|
||||
import TaskBucketModel from '@/models/taskBucket'
|
||||
|
||||
const props = defineProps<{
|
||||
isLoadingProject: boolean,
|
||||
projectId: number,
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<ProjectWrapper
|
||||
class="project-list"
|
||||
:is-loading-project="isLoadingProject"
|
||||
:project-id="projectId"
|
||||
:view-id
|
||||
>
|
||||
@@ -124,7 +123,6 @@ import TaskPositionService from '@/services/taskPosition'
|
||||
import TaskPositionModel from '@/models/taskPosition'
|
||||
|
||||
const props = defineProps<{
|
||||
isLoadingProject: boolean,
|
||||
projectId: IProject['id'],
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<ProjectWrapper
|
||||
class="project-table"
|
||||
:is-loading-project="isLoadingProject"
|
||||
:project-id="projectId"
|
||||
:view-id
|
||||
>
|
||||
@@ -299,7 +298,6 @@ import { camelCase } from 'change-case'
|
||||
import {isSavedFilter} from '@/services/savedFilter'
|
||||
|
||||
const props = defineProps<{
|
||||
isLoadingProject: boolean,
|
||||
projectId: IProject['id'],
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, reactive, shallowReactive, watch, computed} from 'vue'
|
||||
import {ref, reactive, shallowReactive, watch, computed, type PropType} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {useRoute} from 'vue-router'
|
||||
|
||||
@@ -206,17 +206,27 @@ import {useTaskStore} from '@/stores/tasks'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {playPopSound} from '@/helpers/playPop'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
taskId: number,
|
||||
initialRelatedTasks?: ITask['relatedTasks'],
|
||||
showNoRelationsNotice?: boolean,
|
||||
projectId: number,
|
||||
editEnabled: boolean,
|
||||
}>(), {
|
||||
initialRelatedTasks: () => ({}),
|
||||
showNoRelationsNotice: false,
|
||||
projectId: 0,
|
||||
editEnabled: true, // this seems like a mistake
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
initialRelatedTasks: {
|
||||
type: Object as PropType<ITask['relatedTasks']>,
|
||||
default: () => ({}),
|
||||
},
|
||||
showNoRelationsNotice: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
projectId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
editEnabled: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
})
|
||||
|
||||
const taskStore = useTaskStore()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {computed, toValue} from 'vue'
|
||||
import {computed} from 'vue'
|
||||
|
||||
import {useTitle as useTitleVueUse, type UseTitleOptions, type ReadonlyRefOrGetter, type MaybeRef, type MaybeRefOrGetter} from '@vueuse/core'
|
||||
import {useTitle as useTitleVueUse, toValue, type UseTitleOptions, type ReadonlyRefOrGetter, type MaybeRef, type MaybeRefOrGetter} from '@vueuse/core'
|
||||
|
||||
export function useTitle(
|
||||
newTitle:
|
||||
|
||||
@@ -1124,7 +1124,7 @@
|
||||
}
|
||||
},
|
||||
"date": {
|
||||
"locale": "nn",
|
||||
"locale": "nb_NO",
|
||||
"altFormatLong": "d.m.y H:i",
|
||||
"altFormatShort": "d.m.y"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
|
||||
export const LabelTaskSchema = AbstractSchema.extend({
|
||||
id: IdSchema.nullable(),
|
||||
taskId: IdSchema.nullable(),
|
||||
labelId: IdSchema.nullable(),
|
||||
})
|
||||
|
||||
export type LabelTask = TypeOf<typeof LabelTaskSchema>
|
||||
@@ -0,0 +1,10 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {object, nativeEnum} from 'zod'
|
||||
|
||||
import {RIGHTS} from '@/constants/rights'
|
||||
|
||||
export const AbstractSchema = object({
|
||||
maxRight: nativeEnum(RIGHTS).nullable(),
|
||||
})
|
||||
|
||||
export type IAbstract = TypeOf<typeof AbstractSchema>
|
||||
@@ -0,0 +1,19 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
import {UserSchema} from './user'
|
||||
import {FileSchema} from './file'
|
||||
|
||||
export const AttachmentSchema = AbstractSchema.extend({
|
||||
id: IdSchema.default(0),
|
||||
|
||||
taskId: IdSchema.default(0), // iTaskSchema.shape.id
|
||||
createdBy: UserSchema,
|
||||
file: FileSchema,
|
||||
created: DateSchema.nullable(),
|
||||
})
|
||||
|
||||
export type IAttachment = TypeOf<typeof AttachmentSchema>
|
||||
@@ -0,0 +1,18 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {z, string, object} from 'zod'
|
||||
|
||||
export const AVATAR_PROVIDER = [
|
||||
'default',
|
||||
'initials',
|
||||
'gravatar',
|
||||
'marble',
|
||||
'upload',
|
||||
] as const
|
||||
export const AvatarProviderSchema = z.enum(AVATAR_PROVIDER)
|
||||
export type IAvatarProvider = TypeOf<typeof AvatarProviderSchema>
|
||||
|
||||
export const AvatarSchema = object({
|
||||
// FIXME: shouldn't the default be 'default'?
|
||||
avatarProvider: string().or(AvatarProviderSchema).default(''),
|
||||
})
|
||||
export type IAvatar = TypeOf<typeof AvatarSchema>
|
||||
@@ -0,0 +1,19 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {object, record, string, unknown} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
|
||||
export const BackgroundImageSchema = object({
|
||||
id: IdSchema.default(0),
|
||||
url: string().url().default(''),
|
||||
thumb: string().default(''),
|
||||
// FIXME: not sure if this needs to defined, since it seems provider specific
|
||||
// {
|
||||
// author: string(),
|
||||
// authorName: string(),
|
||||
// }
|
||||
info: record(unknown()).default({}),
|
||||
blurHash: string().default(''),
|
||||
})
|
||||
|
||||
export type BackgroundImage = TypeOf<typeof BackgroundImageSchema>
|
||||
@@ -0,0 +1,26 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {number, array, boolean} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
import {TextFieldSchema} from './common/textField'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
import {UserSchema} from './user'
|
||||
import {TaskSchema} from './task'
|
||||
|
||||
export const BucketSchema = AbstractSchema.extend({
|
||||
id: IdSchema.default(0),
|
||||
title: TextFieldSchema,
|
||||
listId: IdSchema.default(0),
|
||||
limit: number().default(0),
|
||||
tasks: array(TaskSchema).default([]),
|
||||
isDoneBucket: boolean().default(false),
|
||||
position: number().default(0),
|
||||
|
||||
createdBy: UserSchema.nullable(),
|
||||
created: DateSchema.nullable(),
|
||||
updated: DateSchema.nullable(),
|
||||
})
|
||||
|
||||
export type IBucket = TypeOf<typeof BucketSchema>
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
import type {TypeOf} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
|
||||
export const CaldavTokenSchema = AbstractSchema.extend({
|
||||
id: IdSchema,
|
||||
created: DateSchema,
|
||||
})
|
||||
|
||||
export type CaldavToken = TypeOf<typeof CaldavTokenSchema>
|
||||
@@ -0,0 +1,22 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {nativeEnum} from 'zod'
|
||||
|
||||
export const RELATION_KIND = {
|
||||
'SUBTASK': 'subtask',
|
||||
'PARENTTASK': 'parenttask',
|
||||
'RELATED': 'related',
|
||||
'DUPLICATES': 'duplicates',
|
||||
'BLOCKING': 'blocking',
|
||||
'BLOCKED': 'blocked',
|
||||
'PROCEDES': 'precedes',
|
||||
'FOLLOWS': 'follows',
|
||||
'COPIEDFROM': 'copiedfrom',
|
||||
'COPIEDTO': 'copiedto',
|
||||
} as const
|
||||
|
||||
export const RELATION_KINDS = [...Object.values(RELATION_KIND)] as const
|
||||
|
||||
export const RelationKindSchema = nativeEnum(RELATION_KIND)
|
||||
|
||||
export type IRelationKind = TypeOf<typeof RelationKindSchema>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import {preprocess, date} from 'zod'
|
||||
|
||||
export const DateSchema = preprocess((arg) => {
|
||||
if (
|
||||
// FIXME: Add comment why we check for `0001`
|
||||
typeof arg == 'string' && !arg.startsWith('0001') ||
|
||||
arg instanceof Date
|
||||
) {
|
||||
return new Date(arg)
|
||||
}
|
||||
}, date())
|
||||
@@ -0,0 +1,80 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {z, nativeEnum, array, boolean, object, number} from 'zod'
|
||||
|
||||
export enum SORT_BY {
|
||||
ID = 'id',
|
||||
DONE = 'done',
|
||||
TITLE = 'title',
|
||||
PRIORITY = 'priority',
|
||||
DONE_AT = 'done_at',
|
||||
DUE_DATE = 'due_date',
|
||||
START_DATE = 'start_date',
|
||||
END_DATE = 'end_date',
|
||||
PERCENT_DONE = 'percent_done',
|
||||
CREATED = 'created',
|
||||
UPDATED = 'updated',
|
||||
POSITION = 'position',
|
||||
KANBAN_POSITION = 'kanban_position',
|
||||
}
|
||||
|
||||
export enum ORDER_BY {
|
||||
ASC = 'asc',
|
||||
DESC = 'desc',
|
||||
NONE = 'none',
|
||||
}
|
||||
|
||||
export enum FILTER_BY {
|
||||
DONE = 'done',
|
||||
DUE_DATE = 'due_date',
|
||||
START_DATE = 'start_date',
|
||||
END_DATE = 'end_date',
|
||||
NAMESPACE = 'namespace',
|
||||
ASSIGNEES = 'assignees',
|
||||
LIST_ID = 'list_id',
|
||||
BUCKET_ID = 'bucket_id',
|
||||
PRIORITY = 'priority',
|
||||
PERCENT_DONE = 'percent_done',
|
||||
LABELS = 'labels',
|
||||
UNDEFINED = 'undefined', // FIXME: Why do we have a value that is undefined as string?
|
||||
}
|
||||
|
||||
export enum FILTER_COMPARATOR {
|
||||
EQUALS = 'equals',
|
||||
LESS = 'less',
|
||||
GREATER = 'greater',
|
||||
GREATER_EQUALS = 'greater_equals',
|
||||
LESS_EQUALS = 'less_equals',
|
||||
IN = 'in',
|
||||
}
|
||||
|
||||
export enum FILTER_CONCAT {
|
||||
AND = 'and',
|
||||
OR = 'or',
|
||||
IN = 'in',
|
||||
}
|
||||
|
||||
const TASKS_PER_BUCKET = 25
|
||||
|
||||
export const FilterSchema = object({
|
||||
sortBy: array(nativeEnum(SORT_BY)).default([SORT_BY.DONE, SORT_BY.ID]), // FIXME: create from taskSchema,
|
||||
// fixme default order seem so also be `desc`
|
||||
// see line from ListTable:
|
||||
// if (typeof order === 'undefined' || order === 'none') {
|
||||
orderBy: array(nativeEnum(ORDER_BY)).default([ORDER_BY.ASC, ORDER_BY.DESC]),
|
||||
// FIXME: create from taskSchema
|
||||
filterBy: array(nativeEnum(FILTER_BY)).default([FILTER_BY.DONE]),
|
||||
// FIXME: create from taskSchema
|
||||
// FIXME: might need to preprocess values, e.g. date.
|
||||
// see line from 'filters.vue':
|
||||
// params.filter_value = params.filter_value.map(v => v instanceof Date ? v.toISOString() : v)
|
||||
filterValue: array(z.enum(['false'])).default(['false']),
|
||||
// FIXME: is `in` value correct?
|
||||
// found in `quick-actions.vue`:
|
||||
// params.filter_comparator.push('in')
|
||||
filterComparator: array(nativeEnum(FILTER_COMPARATOR)).default([FILTER_COMPARATOR.EQUALS]),
|
||||
filterConcat: z.nativeEnum(FILTER_CONCAT).default(FILTER_CONCAT.AND),
|
||||
filterIncludeNulls: boolean().default(true),
|
||||
perPage: number().default(TASKS_PER_BUCKET), // FIXME: is perPage is just available for the bucket endpoint?
|
||||
})
|
||||
|
||||
export type IFilter = TypeOf<typeof FilterSchema>
|
||||
@@ -0,0 +1,10 @@
|
||||
import {string} from 'zod'
|
||||
import isHexColor from 'validator/lib/isHexColor'
|
||||
|
||||
export const HexColorSchema = string().transform(
|
||||
(value) => {
|
||||
if (!value || value.startsWith('#')) {
|
||||
return value
|
||||
}
|
||||
return '#' + value
|
||||
}).refine(value => isHexColor(value))
|
||||
@@ -0,0 +1,6 @@
|
||||
import {number, preprocess} from 'zod'
|
||||
|
||||
export const IdSchema = preprocess(
|
||||
(value: unknown) => Number(value),
|
||||
number().positive().int(),
|
||||
)
|
||||
@@ -1,24 +1,26 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {nativeEnum, number, object, preprocess} from 'zod'
|
||||
|
||||
import {SECONDS_A_HOUR} from '@/constants/date'
|
||||
import { REPEAT_TYPES, type IRepeatAfter } from '@/types/IRepeatAfter'
|
||||
import { nativeEnum, number, object, preprocess } from 'zod'
|
||||
import {REPEAT_TYPES, type IRepeatAfter} from '@/types/IRepeatAfter'
|
||||
|
||||
/**
|
||||
* Parses `repeatAfterSeconds` into a usable js object.
|
||||
*/
|
||||
export function parseRepeatAfter(repeatAfterSeconds: number): IRepeatAfter {
|
||||
let repeatAfter: IRepeatAfter = {type: 'hours', amount: repeatAfterSeconds / SECONDS_A_HOUR}
|
||||
let repeatAfter: IRepeatAfter
|
||||
|
||||
// if its dividable by 24, its something with days, otherwise hours
|
||||
// if its dividable by SECONDS_A_DAY, its something with days, otherwise hours
|
||||
if (repeatAfterSeconds % SECONDS_A_DAY === 0) {
|
||||
if (repeatAfterSeconds % SECONDS_A_WEEK === 0) {
|
||||
repeatAfter = {type: 'weeks', amount: repeatAfterSeconds / SECONDS_A_WEEK}
|
||||
} else if (repeatAfterSeconds % SECONDS_A_MONTH === 0) {
|
||||
repeatAfter = {type:'months', amount: repeatAfterSeconds / SECONDS_A_MONTH}
|
||||
} else if (repeatAfterSeconds % SECONDS_A_YEAR === 0) {
|
||||
repeatAfter = {type: 'years', amount: repeatAfterSeconds / SECONDS_A_YEAR}
|
||||
} else {
|
||||
repeatAfter = {type: 'days', amount: repeatAfterSeconds / SECONDS_A_DAY}
|
||||
}
|
||||
repeatAfter = {type: REPEAT_TYPES.HOURS, amount: repeatAfterSeconds / SECONDS_A_HOUR}
|
||||
} else if (repeatAfterSeconds % SECONDS_A_WEEK === 0) {
|
||||
repeatAfter = {type: REPEAT_TYPES.WEEKS, amount: repeatAfterSeconds / SECONDS_A_WEEK}
|
||||
} else if (repeatAfterSeconds % SECONDS_A_MONTH === 0) {
|
||||
repeatAfter = {type: REPEAT_TYPES.MONTHS, amount: repeatAfterSeconds / SECONDS_A_MONTH}
|
||||
} else if (repeatAfterSeconds % SECONDS_A_YEAR === 0) {
|
||||
repeatAfter = {type: REPEAT_TYPES.YEARS, amount: repeatAfterSeconds / SECONDS_A_YEAR}
|
||||
} else {
|
||||
repeatAfter = {type: REPEAT_TYPES.DAYS, amount: repeatAfterSeconds / SECONDS_A_DAY}
|
||||
}
|
||||
return repeatAfter
|
||||
}
|
||||
@@ -37,4 +39,6 @@ export const RepeatsSchema = preprocess(
|
||||
type: nativeEnum(REPEAT_TYPES),
|
||||
amount: number().int(),
|
||||
}),
|
||||
)
|
||||
)
|
||||
|
||||
export type RepeatAfter = TypeOf<typeof RepeatsSchema>
|
||||
@@ -0,0 +1,3 @@
|
||||
import {string} from 'zod'
|
||||
|
||||
export const TextFieldSchema = string().transform((value) => value.trim()).default('')
|
||||
@@ -0,0 +1,11 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {string} from 'zod'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
|
||||
export const EmailUpdateSchema = AbstractSchema.extend({
|
||||
newEmail: string().email().default(''),
|
||||
password: string().default(''),
|
||||
})
|
||||
|
||||
export type EmailUpdate = TypeOf<typeof EmailUpdateSchema>
|
||||
@@ -0,0 +1,15 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {object, number, string} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
|
||||
export const FileSchema = object({
|
||||
id: IdSchema.default(0),
|
||||
mime: string().default(''),
|
||||
name: string().default(''),
|
||||
size: number().default(0),
|
||||
created: DateSchema.nullable(),
|
||||
})
|
||||
|
||||
export type File = TypeOf<typeof FileSchema>
|
||||
@@ -0,0 +1,33 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {string} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
import {HexColorSchema} from './common/hexColor'
|
||||
|
||||
import {UserSchema} from './user'
|
||||
import {AbstractSchema} from './abstract'
|
||||
|
||||
import {colorIsDark} from '@/helpers/color/colorIsDark'
|
||||
|
||||
const DEFAULT_LABEL_BACKGROUND_COLOR = 'e8e8e8'
|
||||
|
||||
export const LabelSchema = AbstractSchema.extend({
|
||||
id: IdSchema.default(0),
|
||||
title: string().default(''),
|
||||
hexColor: HexColorSchema.default(DEFAULT_LABEL_BACKGROUND_COLOR),
|
||||
textColor: string(), // implicit
|
||||
description: string().default(''),
|
||||
createdBy: UserSchema, // FIXME: default: current user?
|
||||
listId: IdSchema.default(0),
|
||||
|
||||
created: DateSchema.nullable(),
|
||||
updated: DateSchema.nullable(),
|
||||
}).transform((obj) => {
|
||||
// FIXME: remove textColor location => should be defined in UI
|
||||
obj.textColor = colorIsDark(obj.hexColor) ? '#4a4a4a' : '#ffffff'
|
||||
return obj
|
||||
},
|
||||
)
|
||||
|
||||
export type ILabel = TypeOf<typeof LabelSchema>
|
||||
@@ -0,0 +1,26 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {number, string, nativeEnum} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
import {UserSchema} from './user'
|
||||
|
||||
import {RIGHTS} from '@/constants/rights'
|
||||
|
||||
export const LinkShareSchema = AbstractSchema.extend({
|
||||
id: IdSchema.default(0),
|
||||
hash: string().default(''),
|
||||
right: nativeEnum(RIGHTS).default(RIGHTS.READ),
|
||||
sharedBy: UserSchema,
|
||||
sharingType: number().default(0), // FIXME: use correct numbers
|
||||
listId: IdSchema.default(0),
|
||||
name: string().default(''),
|
||||
password: string().default(''),
|
||||
|
||||
created: DateSchema.nullable(),
|
||||
updated: DateSchema.nullable(),
|
||||
})
|
||||
|
||||
export type LinkShare = TypeOf<typeof LinkShareSchema>
|
||||
@@ -0,0 +1,32 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {boolean, number, string, array, any} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
import {SubscriptionSchema} from './subscription'
|
||||
import {TaskSchema} from './task'
|
||||
import {UserSchema} from './user'
|
||||
|
||||
export const ListSchema = AbstractSchema.extend({
|
||||
id: IdSchema.default(0),
|
||||
hash: string().default(''),
|
||||
description: string().default(''),
|
||||
owner: UserSchema,
|
||||
tasks: array(TaskSchema),
|
||||
namespaceId: IdSchema.default(0), // INamespace['id'],
|
||||
isArchived: boolean().default(false),
|
||||
hexColor: string().default(''),
|
||||
identifier: string().default(''),
|
||||
backgroundInformation: any().nullable().default(null), // FIXME: what is this for?
|
||||
isFavorite: boolean().default(false),
|
||||
subscription: SubscriptionSchema.nullable(),
|
||||
position: number().default(0),
|
||||
backgroundBlurHash: string().default(''),
|
||||
|
||||
created: DateSchema.nullable(),
|
||||
updated: DateSchema.nullable(),
|
||||
})
|
||||
|
||||
export type List = TypeOf<typeof ListSchema>
|
||||
@@ -0,0 +1,14 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
import {ListSchema} from './list'
|
||||
|
||||
export const ListDuplicationSchema = AbstractSchema.extend({
|
||||
listId: IdSchema.default(0),
|
||||
namespaceId: IdSchema.default(0), // INamespace['id'],
|
||||
list: ListSchema,
|
||||
})
|
||||
|
||||
export type ListDuplication = TypeOf<typeof ListDuplicationSchema>
|
||||
@@ -0,0 +1,26 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {boolean, string, array} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {HexColorSchema} from './common/hexColor'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
import {ListSchema} from './list'
|
||||
import {UserSchema} from './user'
|
||||
import {SubscriptionSchema} from './subscription'
|
||||
|
||||
export const NamespaceSchema = AbstractSchema.extend({
|
||||
id: IdSchema.default(0),
|
||||
title: string().default(''),
|
||||
description: string().default(''),
|
||||
owner: UserSchema,
|
||||
lists: array(ListSchema),
|
||||
isArchived: boolean().default(false),
|
||||
hexColor: HexColorSchema.default(''),
|
||||
subscription: SubscriptionSchema.nullable(),
|
||||
|
||||
created: IdSchema.nullable(),
|
||||
updated: IdSchema.nullable(),
|
||||
})
|
||||
|
||||
export type Namespace = TypeOf<typeof NamespaceSchema>
|
||||
@@ -0,0 +1,54 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {union, boolean, object, string} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
import {TaskSchema} from './task'
|
||||
import {TaskCommentSchema} from './taskComment'
|
||||
import {TeamSchema} from './team'
|
||||
import {UserSchema} from './user'
|
||||
|
||||
const NotificationTypeSchema = object({
|
||||
doer: UserSchema,
|
||||
})
|
||||
|
||||
const NotificationTypeTask = NotificationTypeSchema.extend({
|
||||
task: TaskSchema,
|
||||
comment: TaskCommentSchema,
|
||||
})
|
||||
|
||||
const NotificationTypeAssigned = NotificationTypeSchema.extend({
|
||||
task: TaskSchema,
|
||||
assignee: UserSchema,
|
||||
})
|
||||
|
||||
const NotificationTypeDeleted = NotificationTypeSchema.extend({
|
||||
task: TaskSchema,
|
||||
})
|
||||
|
||||
const NotificationTypeCreated = NotificationTypeSchema.extend({
|
||||
task: TaskSchema,
|
||||
})
|
||||
|
||||
const NotificationTypeMemberAdded = NotificationTypeSchema.extend({
|
||||
member: UserSchema,
|
||||
team: TeamSchema,
|
||||
})
|
||||
|
||||
export const NotificationSchema = AbstractSchema.extend({
|
||||
id: IdSchema.default(0),
|
||||
name: string().default(''),
|
||||
notification: union([
|
||||
NotificationTypeTask,
|
||||
NotificationTypeAssigned,
|
||||
NotificationTypeDeleted,
|
||||
NotificationTypeCreated,
|
||||
NotificationTypeMemberAdded,
|
||||
]),
|
||||
read: boolean().default(false),
|
||||
readAt: DateSchema.nullable(),
|
||||
})
|
||||
|
||||
export type Notification = TypeOf<typeof NotificationSchema>
|
||||
@@ -0,0 +1,13 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {string} from 'zod'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
|
||||
// FIXME: is it correct that this extends the Abstract Schema?
|
||||
export const PasswordResetSchema = AbstractSchema.extend({
|
||||
token: string().default(''),
|
||||
newPassword: string().default(''),
|
||||
email: string().email().default(''),
|
||||
})
|
||||
|
||||
export type PasswordReset = TypeOf<typeof PasswordResetSchema>
|
||||
@@ -0,0 +1,15 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {string} from 'zod'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
|
||||
// FIXME: is it correct that this extends the Abstract Schema?
|
||||
export const PasswordUpdateSchema = AbstractSchema.extend({
|
||||
newPassword: string().default(''),
|
||||
oldPassword: string().default(''),
|
||||
}).refine((data) => data.newPassword === data.oldPassword, {
|
||||
message: 'Passwords don\'t match',
|
||||
path: ['confirm'], // path of error
|
||||
})
|
||||
|
||||
export type PasswordUpdate = TypeOf<typeof PasswordUpdateSchema>
|
||||
@@ -0,0 +1,23 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {string} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
import {FilterSchema} from './common/filter'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
import {UserSchema} from './user'
|
||||
|
||||
// FIXME: is it correct that this extends the Abstract Schema?
|
||||
export const SavedFilterSchema = AbstractSchema.extend({
|
||||
id: IdSchema.default(0),
|
||||
title: string().default(''),
|
||||
description: string().default(''),
|
||||
filters: FilterSchema,
|
||||
|
||||
owner: UserSchema,
|
||||
created: DateSchema.nullable(),
|
||||
updated: DateSchema.nullable(),
|
||||
})
|
||||
|
||||
export type SavedFilter = TypeOf<typeof SavedFilterSchema>
|
||||
@@ -0,0 +1,19 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {string} from 'zod'
|
||||
|
||||
import {DateSchema} from './common/date'
|
||||
import {IdSchema} from './common/id'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
import {UserSchema} from './user'
|
||||
|
||||
export const SubscriptionSchema = AbstractSchema.extend({
|
||||
id: IdSchema.default(0),
|
||||
entity: string().default(''), // FIXME: correct type?
|
||||
entityId: IdSchema.default(0), // FIXME: correct type?
|
||||
user: UserSchema,
|
||||
|
||||
created: DateSchema.nullable(),
|
||||
})
|
||||
|
||||
export type Subscription = TypeOf<typeof SubscriptionSchema>
|
||||
@@ -0,0 +1,119 @@
|
||||
import type {ZodType, TypeOf} from 'zod'
|
||||
import {nativeEnum, boolean, number, string, array, record, unknown, lazy} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
import {HexColorSchema} from './common/hexColor'
|
||||
import {TextFieldSchema} from './common/textField'
|
||||
import {RelationKindSchema} from './common/RelationKind'
|
||||
import {RepeatsSchema} from './common/repeats'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
import {AttachmentSchema} from './attachment'
|
||||
import {LabelSchema} from './label'
|
||||
import {SubscriptionSchema} from './subscription'
|
||||
import {UserSchema} from './user'
|
||||
|
||||
import {PRIORITIES} from '@/constants/priorities'
|
||||
import {TASK_REPEAT_MODES} from '@/types/IRepeatMode'
|
||||
|
||||
const LabelsSchema = array(LabelSchema)
|
||||
.transform((labels) => labels.sort((f, s) => f.title > s.title ? 1 : -1)) // FIXME: use
|
||||
.default([])
|
||||
|
||||
export type ILabels = TypeOf<typeof LabelsSchema>
|
||||
|
||||
const RelatedTasksSchema = record(RelationKindSchema, record(string(), unknown()))
|
||||
export type IRelatedTasksSchema = TypeOf<typeof RelatedTasksSchema>
|
||||
|
||||
const RelatedTasksLazySchema : ZodType<Task['relatedTasks']> = lazy(() =>
|
||||
record(RelationKindSchema, TaskSchema),
|
||||
)
|
||||
export type IRelatedTasksLazySchema = TypeOf<typeof RelatedTasksLazySchema>
|
||||
|
||||
// export interface ITask extends IAbstract {
|
||||
// id: number
|
||||
// title: string
|
||||
// description: string
|
||||
// done: boolean
|
||||
// doneAt: Date | null
|
||||
// priority: Priority
|
||||
// labels: ILabel[]
|
||||
// assignees: IUser[]
|
||||
|
||||
// dueDate: Date | null
|
||||
// startDate: Date | null
|
||||
// endDate: Date | null
|
||||
// repeatAfter: number | IRepeatAfter
|
||||
// repeatFromCurrentDate: boolean
|
||||
// repeatMode: IRepeatMode
|
||||
// reminderDates: Date[]
|
||||
// parentTaskId: ITask['id']
|
||||
// hexColor: string
|
||||
// percentDone: number
|
||||
// relatedTasks: Partial<Record<IRelationKind, ITask>>,
|
||||
// attachments: IAttachment[]
|
||||
// identifier: string
|
||||
// index: number
|
||||
// isFavorite: boolean
|
||||
// subscription: ISubscription
|
||||
|
||||
// position: number
|
||||
// kanbanPosition: number
|
||||
|
||||
// createdBy: IUser
|
||||
// created: Date
|
||||
// updated: Date
|
||||
|
||||
// listId: IList['id'] // Meta, only used when creating a new task
|
||||
// bucketId: IBucket['id']
|
||||
// }
|
||||
|
||||
export const TaskSchema = AbstractSchema.extend({
|
||||
id: IdSchema.default(0),
|
||||
title: TextFieldSchema,
|
||||
description: TextFieldSchema,
|
||||
done: boolean().default(false),
|
||||
doneAt: DateSchema.nullable().default(null),
|
||||
priority: nativeEnum(PRIORITIES).default(PRIORITIES.UNSET),
|
||||
labels: LabelsSchema,
|
||||
assignees: array(UserSchema).default([]),
|
||||
|
||||
dueDate: DateSchema.nullable(), // FIXME: default value is `0`. Shouldn't this be `null`?
|
||||
startDate: DateSchema.nullable(), // FIXME: default value is `0`. Shouldn't this be `null`?
|
||||
endDate: DateSchema.nullable(), // FIXME: default value is `0`. Shouldn't this be `null`?
|
||||
repeatAfter: RepeatsSchema, // FIXME: default value is `0`. Shouldn't this be `null`?
|
||||
repeatFromCurrentDate: boolean().default(false),
|
||||
repeatMode: nativeEnum(TASK_REPEAT_MODES).default(TASK_REPEAT_MODES.REPEAT_MODE_DEFAULT),
|
||||
|
||||
// TODO: schedule notifications
|
||||
// FIXME: triggered notificaitons not supported anymore / remove feature?
|
||||
reminderDates: array(DateSchema).default([]),
|
||||
parentTaskId: IdSchema.default(0), // shouldn't this have `null` as default?
|
||||
hexColor: HexColorSchema.default(''),
|
||||
percentDone: number().default(0),
|
||||
relatedTasks: RelatedTasksSchema.default({}),
|
||||
attachments: array(AttachmentSchema).default([]),
|
||||
identifier: string().default(''),
|
||||
index: number().default(0),
|
||||
isFavorite: boolean().default(false),
|
||||
subscription: SubscriptionSchema.nullable().default(null),
|
||||
|
||||
position: number().default(0),
|
||||
kanbanPosition: number().default(0),
|
||||
|
||||
createdBy: UserSchema,
|
||||
created: DateSchema.nullable(),
|
||||
updated: DateSchema.nullable(),
|
||||
|
||||
listId: IdSchema.default(0), //IList['id'], // Meta, only used when creating a new task
|
||||
bucketId: IdSchema.default(0), // IBucket['id'],
|
||||
}).transform((obj) => {
|
||||
if (obj.identifier === `-${obj.index}`) {
|
||||
obj.identifier = ''
|
||||
}
|
||||
return obj
|
||||
})
|
||||
|
||||
|
||||
export type Task = TypeOf<typeof TaskSchema>
|
||||
@@ -0,0 +1,14 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
|
||||
export const TaskAssigneeSchema = AbstractSchema.extend({
|
||||
created: DateSchema.nullable(),
|
||||
userId: IdSchema.default(0), // IUser['id']
|
||||
taskId: IdSchema.default(0), // ITask['id']
|
||||
})
|
||||
|
||||
export type TaskAssignee = TypeOf<typeof TaskAssigneeSchema>
|
||||
@@ -0,0 +1,20 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {string} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
import {UserSchema} from './user'
|
||||
|
||||
export const TaskCommentSchema = AbstractSchema.extend({
|
||||
id: IdSchema.default(0),
|
||||
taskId: IdSchema.default(0),
|
||||
comment: string().default(''),
|
||||
author: UserSchema,
|
||||
|
||||
created: DateSchema.nullable(),
|
||||
updated: DateSchema.nullable(),
|
||||
})
|
||||
|
||||
export type TaskComment = TypeOf<typeof TaskCommentSchema>
|
||||
@@ -0,0 +1,24 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {nativeEnum} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
import {UserSchema} from './user'
|
||||
|
||||
import {RELATION_KIND} from '@/types/IRelationKind'
|
||||
|
||||
export const TaskRelationSchema = AbstractSchema.extend({
|
||||
id: IdSchema.default(0),
|
||||
otherTaskId: IdSchema.default(0),
|
||||
taskId: IdSchema.default(0),
|
||||
relationKind: nativeEnum(RELATION_KIND).nullable().default(null), // FIXME: default value was empty string?
|
||||
|
||||
createdBy: UserSchema,
|
||||
// FIXME: shouldn't the empty value of dates be `new Date()`
|
||||
// Because e.g. : `new Date(null)` => Thu Jan 01 1970 01:00:00 GMT+0100 (Central European Standard Time)
|
||||
created: DateSchema.nullable().default(null),
|
||||
})
|
||||
|
||||
export type ITaskRelation = TypeOf<typeof TaskRelationSchema>
|
||||
@@ -0,0 +1,25 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {array, nativeEnum, string} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
import {UserSchema} from './user'
|
||||
import {TeamMemberSchema} from './teamMember'
|
||||
|
||||
import {RIGHTS} from '@/constants/rights'
|
||||
|
||||
export const TeamSchema = AbstractSchema.extend({
|
||||
id: IdSchema.default(0),
|
||||
name: string().default(''),
|
||||
description: string().default(''),
|
||||
members: array(TeamMemberSchema),
|
||||
right: nativeEnum(RIGHTS).default(RIGHTS.READ),
|
||||
|
||||
createdBy: UserSchema, // FIXME: default was {},
|
||||
created: DateSchema.nullable(),
|
||||
updated: DateSchema.nullable(),
|
||||
})
|
||||
|
||||
export type Team = TypeOf<typeof TeamSchema>
|
||||
@@ -0,0 +1,11 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
|
||||
import {TeamShareBaseSchema} from './teamShareBase'
|
||||
|
||||
export const TeamListSchema = TeamShareBaseSchema.extend({
|
||||
listId: IdSchema.default(0), // IList['id']
|
||||
})
|
||||
|
||||
export type TeamList = TypeOf<typeof TeamListSchema>
|
||||
@@ -0,0 +1,13 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {boolean} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
|
||||
import {UserSchema} from './user'
|
||||
|
||||
export const TeamMemberSchema = UserSchema.extend({
|
||||
admin: boolean().default(false),
|
||||
teamId: IdSchema.default(0), // IList['id']
|
||||
})
|
||||
|
||||
export type TeamMember = TypeOf<typeof TeamMemberSchema>
|
||||
@@ -0,0 +1,11 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
|
||||
import {TeamShareBaseSchema} from './teamShareBase'
|
||||
|
||||
export const TeamNamespaceSchema = TeamShareBaseSchema.extend({
|
||||
namespaceId: IdSchema.default(0), // INamespace['id']
|
||||
})
|
||||
|
||||
export type ITeamNamespace = TypeOf<typeof TeamNamespaceSchema>
|
||||
@@ -0,0 +1,19 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {nativeEnum} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
|
||||
import {RIGHTS} from '@/constants/rights'
|
||||
|
||||
export const TeamShareBaseSchema = AbstractSchema.extend({
|
||||
teamId: IdSchema.default(0), // ITeam['id']
|
||||
right: nativeEnum(RIGHTS).default(RIGHTS.READ),
|
||||
|
||||
created: DateSchema.nullable(),
|
||||
updated: DateSchema.nullable(),
|
||||
})
|
||||
|
||||
export type ITeamShareBase = TypeOf<typeof TeamShareBaseSchema>
|
||||
@@ -0,0 +1,9 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
|
||||
import {object, string} from 'zod'
|
||||
|
||||
export const TokenSchema = object({
|
||||
token: string(),
|
||||
})
|
||||
|
||||
export type IToken = TypeOf<typeof TokenSchema>
|
||||
@@ -0,0 +1,12 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {string, boolean} from 'zod'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
|
||||
export const TotpSchema = AbstractSchema.extend({
|
||||
secret: string().default(''),
|
||||
enabled: boolean().default(false),
|
||||
url: string().url().default(''),
|
||||
})
|
||||
|
||||
export type Totp = TypeOf<typeof TotpSchema>
|
||||
@@ -0,0 +1,21 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {string} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
import {UserSettingsSchema} from './userSettings'
|
||||
|
||||
export const UserSchema = AbstractSchema.extend({
|
||||
id: IdSchema.default(0),
|
||||
email: string().email().default(''),
|
||||
username: string().default(''),
|
||||
name: string().default(''),
|
||||
settings: UserSettingsSchema.nullable(),
|
||||
|
||||
created: DateSchema.nullable(),
|
||||
updated: DateSchema.nullable(),
|
||||
})
|
||||
|
||||
export type User = TypeOf<typeof UserSchema>
|
||||
@@ -0,0 +1,11 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
|
||||
import {UserShareBaseSchema} from './userShareBase'
|
||||
|
||||
export const UserListSchema = UserShareBaseSchema.extend({
|
||||
listId: IdSchema.default(0), // IList['id']
|
||||
})
|
||||
|
||||
export type IUserList = TypeOf<typeof UserListSchema>
|
||||
@@ -0,0 +1,11 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
|
||||
import {UserShareBaseSchema} from './userShareBase'
|
||||
|
||||
export const UserNamespaceSchema = UserShareBaseSchema.extend({
|
||||
namespaceId: IdSchema.default(0), // INamespace['id']
|
||||
})
|
||||
|
||||
export type IUserNamespace = TypeOf<typeof UserNamespaceSchema>
|
||||
@@ -0,0 +1,29 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {boolean, string, undefined, nativeEnum} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
|
||||
const WEEKDAYS = {
|
||||
MONDAY: 0,
|
||||
TUESDAY: 1,
|
||||
WEDNESDAY: 2,
|
||||
THURSDAY: 3,
|
||||
FRIDAY: 4,
|
||||
SATURDAY: 5,
|
||||
SUNDAY: 6,
|
||||
} as const
|
||||
|
||||
export const UserSettingsSchema = AbstractSchema.extend({
|
||||
name: string().default(''),
|
||||
emailRemindersEnabled: boolean().default(true),
|
||||
discoverableByName: boolean().default(false),
|
||||
discoverableByEmail: boolean().default(false),
|
||||
overdueTasksRemindersEnabled: boolean().default(true),
|
||||
defaultListId: IdSchema.or(undefined()), // iListSchema['id'] // FIXME: shouldn't this be `null`?
|
||||
weekStart: nativeEnum(WEEKDAYS).default(WEEKDAYS.MONDAY),
|
||||
timezone: string().default(''),
|
||||
})
|
||||
|
||||
export type IUserSettings = TypeOf<typeof UserSettingsSchema>
|
||||
@@ -0,0 +1,19 @@
|
||||
import type {TypeOf} from 'zod'
|
||||
import {nativeEnum} from 'zod'
|
||||
|
||||
import {IdSchema} from './common/id'
|
||||
import {DateSchema} from './common/date'
|
||||
|
||||
import {AbstractSchema} from './abstract'
|
||||
|
||||
import {RIGHTS} from '@/constants/rights'
|
||||
|
||||
export const UserShareBaseSchema = AbstractSchema.extend({
|
||||
userId: IdSchema, // FIXME: default of model is `''`
|
||||
right: nativeEnum(RIGHTS).default(RIGHTS.READ),
|
||||
|
||||
created: DateSchema.nullable(),
|
||||
updated: DateSchema.nullable(),
|
||||
})
|
||||
|
||||
export type TeamMember = TypeOf<typeof UserShareBaseSchema>
|
||||
@@ -1,6 +1,13 @@
|
||||
import type {IAbstract} from './IAbstract'
|
||||
|
||||
export type AvatarProvider = 'default' | 'initials' | 'gravatar' | 'marble' | 'upload'
|
||||
export const AVATAR_PROVIDER = [
|
||||
'default',
|
||||
'initials',
|
||||
'gravatar',
|
||||
'marble',
|
||||
'upload',
|
||||
] as const
|
||||
export type AvatarProvider = typeof AVATAR_PROVIDER[number]
|
||||
|
||||
export interface IAvatar extends IAbstract {
|
||||
avatarProvider: AvatarProvider
|
||||
|
||||
+5
-2
@@ -7,8 +7,11 @@ import type Modal from '@/components/misc/Modal.vue'
|
||||
import type Card from '@/components/misc/Card.vue'
|
||||
|
||||
// Here we define globally imported components
|
||||
// See: https://github.com/vuejs/language-tools/wiki/Global-Component-Types
|
||||
declare module 'vue' {
|
||||
// See:
|
||||
// https://github.com/johnsoncodehk/volar/blob/2ca8fd3434423c7bea1c8e08132df3b9ce84eea7/extensions/vscode-vue-language-features/README.md#usage
|
||||
// Under the hidden collapsible "Define Global Components"
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
Icon: FontAwesomeIconFixedTypes
|
||||
Notifications: FunctionalComponent<Notifications>
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, ref, shallowReactive, watch, watchEffect} from 'vue'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {computed, watch} from 'vue'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
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,
|
||||
@@ -23,10 +17,8 @@ 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])
|
||||
|
||||
@@ -34,68 +26,19 @@ 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 || !currentView.value) {
|
||||
if (props.viewId === 0 || !projectStore.projects[props.projectId]?.views.find(v => v.id === props.viewId)) {
|
||||
// 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 (defaultView !== DEFAULT_PROJECT_VIEW_SETTINGS.FIRST) {
|
||||
view = currentProject.value?.views.find(v => v.viewKind === defaultView)
|
||||
if (authStore.settings.frontendSettings.defaultView !== DEFAULT_PROJECT_VIEW_SETTINGS.FIRST) {
|
||||
view = projectStore.projects[props.projectId]?.views.find(v => v.viewKind === authStore.settings.frontendSettings.defaultView)
|
||||
}
|
||||
|
||||
// Use the first view as fallback if the default view is not available
|
||||
if (view === undefined && currentProject.value?.views?.length > 0) {
|
||||
view = currentProject.value?.views[0]
|
||||
if (view === undefined && projectStore.projects[props.projectId]?.views?.length > 0) {
|
||||
view = projectStore.projects[props.projectId]?.views[0]
|
||||
}
|
||||
|
||||
if (view) {
|
||||
@@ -117,39 +60,39 @@ watch(
|
||||
)
|
||||
|
||||
watch(
|
||||
currentProject,
|
||||
() => projectStore.projects[props.projectId],
|
||||
redirectToDefaultViewIfNecessary,
|
||||
)
|
||||
|
||||
watchEffect(() => saveProjectToHistory({id: props.projectId}))
|
||||
watchEffect(() => saveProjectView(props.projectId, props.viewId))
|
||||
// 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(() => baseStore.setCurrentProjectViewId(props.viewId))
|
||||
const route = useRoute()
|
||||
</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,12 +3,7 @@
|
||||
"@tsconfig/node22/tsconfig.json",
|
||||
"@vue/tsconfig/tsconfig.json"
|
||||
],
|
||||
"include": [
|
||||
"env.config.d.ts",
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*"
|
||||
],
|
||||
"include": ["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)),
|
||||
],
|
||||
|
||||
@@ -18,7 +18,7 @@ module code.vikunja.io/api
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.1
|
||||
github.com/ThreeDotsLabs/watermill v1.4.4
|
||||
github.com/ThreeDotsLabs/watermill v1.4.3
|
||||
github.com/adlio/trello v1.12.0
|
||||
github.com/arran4/golang-ical v0.3.1
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
@@ -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.1
|
||||
github.com/wneessen/go-mail v0.6.0
|
||||
github.com/yuin/goldmark v1.7.8
|
||||
golang.org/x/crypto v0.32.0
|
||||
golang.org/x/image v0.23.0
|
||||
@@ -216,4 +216,4 @@ replace github.com/samedi/caldav-go => github.com/kolaente/caldav-go v3.0.1-0.20
|
||||
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.23.5
|
||||
toolchain go1.23.4
|
||||
|
||||
@@ -664,8 +664,6 @@ github.com/ThreeDotsLabs/watermill v1.4.2 h1:lX/J79HyUipxZ2VetC7vMPqlw29xreHMxzh
|
||||
github.com/ThreeDotsLabs/watermill v1.4.2/go.mod h1:lBnrLbxOjeMRgcJbv+UiZr8Ylz8RkJ4m6i/VN/Nk+to=
|
||||
github.com/ThreeDotsLabs/watermill v1.4.3 h1:cRT1v7jlAgoPyEknvz0IFp3EKdSBRD/0Qbtz6KhexG8=
|
||||
github.com/ThreeDotsLabs/watermill v1.4.3/go.mod h1:lBnrLbxOjeMRgcJbv+UiZr8Ylz8RkJ4m6i/VN/Nk+to=
|
||||
github.com/ThreeDotsLabs/watermill v1.4.4 h1:aLClMl6EYIOQy4BML9yb2VpTekbynDatvQbXGp7idCU=
|
||||
github.com/ThreeDotsLabs/watermill v1.4.4/go.mod h1:lBnrLbxOjeMRgcJbv+UiZr8Ylz8RkJ4m6i/VN/Nk+to=
|
||||
github.com/adlio/trello v1.12.0 h1:JqOE2GFHQ9YtEviRRRSnicSxPbt4WFOxhqXzjMOw8lw=
|
||||
github.com/adlio/trello v1.12.0/go.mod h1:I4Lti4jf2KxjTNgTqs5W3lLuE78QZZdYbbPnQQGwjOo=
|
||||
github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
|
||||
@@ -1377,8 +1375,6 @@ 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=
|
||||
|
||||
@@ -100,12 +100,6 @@ func getRouteDetail(route echo.Route) (method string, detail *RouteDetail) {
|
||||
}
|
||||
}
|
||||
|
||||
func ensureAPITokenRoutesGroup(group string) {
|
||||
if _, has := apiTokenRoutes[group]; !has {
|
||||
apiTokenRoutes[group] = make(APITokenRoute)
|
||||
}
|
||||
}
|
||||
|
||||
// CollectRoutesForAPITokenUsage gets called for every added APITokenRoute and builds a list of all routes we can use for the api tokens.
|
||||
func CollectRoutesForAPITokenUsage(route echo.Route, middlewares []echo.MiddlewareFunc) {
|
||||
|
||||
@@ -145,15 +139,10 @@ func CollectRoutesForAPITokenUsage(route echo.Route, middlewares []echo.Middlewa
|
||||
// and if that's the case, add it to its parent instead.
|
||||
// Otherwise, we add it to the "other" key.
|
||||
if len(routeParts) == 1 {
|
||||
if routeGroupName == "notifications" && route.Method == http.MethodPost {
|
||||
ensureAPITokenRoutesGroup("notifications")
|
||||
|
||||
apiTokenRoutes["notifications"]["mark_all_as_read"] = routeDetail
|
||||
return
|
||||
if _, has := apiTokenRoutes["other"]; !has {
|
||||
apiTokenRoutes["other"] = make(APITokenRoute)
|
||||
}
|
||||
|
||||
ensureAPITokenRoutesGroup("other")
|
||||
|
||||
_, exists := apiTokenRoutes["other"][routeGroupName]
|
||||
if exists {
|
||||
routeGroupName += "_" + strings.ToLower(route.Method)
|
||||
@@ -179,7 +168,10 @@ func CollectRoutesForAPITokenUsage(route echo.Route, middlewares []echo.Middlewa
|
||||
|
||||
if strings.HasSuffix(routeGroupName, "_bulk") {
|
||||
parent := strings.TrimSuffix(routeGroupName, "_bulk")
|
||||
ensureAPITokenRoutesGroup(parent)
|
||||
_, has := apiTokenRoutes[parent]
|
||||
if !has {
|
||||
apiTokenRoutes[parent] = make(APITokenRoute)
|
||||
}
|
||||
|
||||
method, routeDetail := getRouteDetail(route)
|
||||
apiTokenRoutes[parent][method+"_bulk"] = routeDetail
|
||||
@@ -210,7 +202,6 @@ func CollectRoutesForAPITokenUsage(route echo.Route, middlewares []echo.Middlewa
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// GetAvailableAPIRoutesForToken returns a list of all API routes which are available for token usage.
|
||||
@@ -238,9 +229,7 @@ func CanDoAPIRoute(c echo.Context, token *APIToken) (can bool) {
|
||||
|
||||
routeGroupName = strings.TrimSuffix(routeGroupName, "_bulk")
|
||||
|
||||
if routeGroupName == "user" ||
|
||||
routeGroupName == "users" ||
|
||||
routeGroupName == "routes" {
|
||||
if routeGroupName == "user" {
|
||||
routeGroupName = "other"
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user