Compare commits

..
Author SHA1 Message Date
Renovate 9551e68b86 fix(deps): update dependency sortablejs to v1.15.7 2026-02-13 16:06:28 +00:00
Dominik Pschenitschniandkonrad cbbc4c0372 fix: i18n missing translation key 2025-01-19 20:10:22 +00:00
Dominik Pschenitschniandkonrad c7e708cf7d fix: deprecated import in useTitle 2025-01-19 20:07:55 +00:00
Dominik Pschenitschniandkonrad 6d3a30c799 fix: postcss-easing-gradient types 2025-01-19 19:58:16 +00:00
Dominik Pschenitschniandkonrad 4c972e1bc4 feat: load project in project view 2025-01-19 19:56:07 +00:00
Dominik Pschenitschniandkonrad 144571e448 feat: simplify ProjectView 2025-01-19 19:56:07 +00:00
Dominik Pschenitschniandkonrad a24c64da8f fix: vite config linting 2025-01-19 19:39:40 +00:00
renovateandkonrad 2faa03757c fix(deps): update module github.com/wneessen/go-mail to v0.6.1 2025-01-19 16:30:07 +00:00
kolaente 9cef2c4c97 chore: add Bug type to bug issue template 2025-01-19 16:48:44 +01:00
renovate ea6b141d42 chore(deps): update dependency caniuse-lite to v1.0.30001695 2025-01-19 03:07:01 +00:00
renovateandkonrad c0877dd0ab chore(deps): update dependency vitest to v3.0.2 2025-01-18 10:32:50 +00:00
Frederick [Bot]andDominik Pschenitschni 5c31ccaddd chore(i18n): update translations via Crowdin 2025-01-18 00:14:03 +00:00
Dominik Pschenitschni 47538ca810 fix: method name 2025-01-17 13:59:12 +01:00
kolaente abae6f05e0 fix(auth): move read all notifications to notification group
Resolves
https://kolaente.dev/vikunja/vikunja/issues/2977
2025-01-17 12:52:42 +01:00
kolaente 45ec1a4c47 fix(auth): make sure routes from the "other" group work as intended
Resolves
https://kolaente.dev/vikunja/vikunja/issues/2977
2025-01-17 11:06:00 +01:00
kolaente 77d1616fea docs: adjust frontend readme
Resolves https://kolaente.dev/vikunja/vikunja/issues/3012
2025-01-17 10:41:45 +01:00
Dominik Pschenitschniandkonrad b5cb98498a fix: global component types
See canonical source https://github.com/vuejs/language-tools/wiki/Global-Component-Types
This was kind of hard to find, imho should be in vue docs itself…
2025-01-17 08:09:44 +00:00
Dominik Pschenitschniandkonrad 70e027a84e feat: withDefaults for RelatedTasks 2025-01-17 07:56:57 +00:00
Dominik Pschenitschniandkonrad 289bb73e9e feat: withDefaults for Flatpickr 2025-01-17 07:55:44 +00:00
Dominik Pschenitschniandkonrad 8bada3e967 fix: git ignore all dist folders 2025-01-17 07:53:54 +00:00
renovate 1863b06d0c chore(deps): update dev-dependencies 2025-01-17 00:07:27 +00:00
renovate d630586f63 chore(deps): update dependency go to v1.23.5 2025-01-16 21:07:59 +00:00
renovateandkonrad 13002d77f5 fix(deps): update module github.com/threedotslabs/watermill to v1.4.4 2025-01-16 15:38:43 +00:00
Dominik Pschenitschni a61e2d064d fix: lowlight imports for v3 2025-01-16 13:14:09 +01:00
renovateandDominik Pschenitschni e1f78462e5 fix(deps): update dependency lowlight to v3 2025-01-16 13:05:24 +01:00
67 changed files with 373 additions and 1292 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
name: Bug Report
description: Found something you weren't expecting? Report it here!
labels: kind/bug
type: Bug
body:
- type: markdown
attributes:
@@ -49,4 +49,4 @@ body:
id: screenshots
attributes:
label: Screenshots
description: If this issue involves the Web Interface, please provide one or more screenshots
description: If this issue involves the Web Interface, please provide one or more screenshots
+2
View File
@@ -11,6 +11,8 @@ stats.html
node_modules
.DS_Store
dist
dist-dev
dist-test
coverage
*.zip
.vite/
+2 -18
View File
@@ -2,30 +2,14 @@
> The todo app to organize your life.
[![Build Status](https://drone.kolaente.de/api/badges/vikunja/vikunja/status.svg)](https://drone.kolaente.de/vikunja/vikunja)
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](LICENSE)
[![Download](https://img.shields.io/badge/download-v0.22.1-brightgreen.svg)](https://dl.vikunja.io)
[![Translation](https://badges.crowdin.net/vikunja/localized.svg)](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!
## 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.
For general information about the project, refer to the top-level readme of this repo.
## Project setup
@@ -36,7 +20,7 @@ pnpm install
### Compiles and hot-reloads for development
```shell
pnpm run serve
pnpm run dev
```
### Compiles and minifies for production
+1 -4
View File
@@ -1,4 +1 @@
declare module 'postcss-easing-gradients' {
import postcssEasingGradients from 'postcss-easing-gradients'
export default postcssEasingGradients
}
declare module 'postcss-easing-gradients';
+6 -9
View File
@@ -93,14 +93,13 @@
"floating-vue": "5.2.2",
"is-touch-device": "1.0.1",
"klona": "2.0.6",
"lowlight": "2.9.0",
"lowlight": "3.3.0",
"pinia": "2.3.0",
"register-service-worker": "1.7.2",
"sortablejs": "1.15.6",
"sortablejs": "1.15.7",
"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",
@@ -108,8 +107,7 @@
"vue-router": "4.5.0",
"vuemoji-picker": "0.3.2",
"workbox-precaching": "7.3.0",
"zhyswan-vuedraggable": "4.1.3",
"zod": "3.24.1"
"zhyswan-vuedraggable": "4.1.3"
},
"devDependencies": {
"@4tw/cypress-drag-drop": "2.2.5",
@@ -124,7 +122,6 @@
"@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",
@@ -133,9 +130,9 @@
"@vue/tsconfig": "0.7.0",
"autoprefixer": "10.4.20",
"browserslist": "4.24.4",
"caniuse-lite": "1.0.30001692",
"caniuse-lite": "1.0.30001695",
"csstype": "3.1.3",
"cypress": "13.17.0",
"cypress": "14.0.0",
"esbuild": "0.24.2",
"eslint": "9.18.0",
"eslint-plugin-vue": "9.32.0",
@@ -155,7 +152,7 @@
"vite-plugin-sentry": "1.4.0",
"vite-plugin-vue-devtools": "7.7.0",
"vite-svg-loader": "5.1.0",
"vitest": "2.1.8",
"vitest": "3.0.2",
"vue-tsc": "2.2.0",
"wait-on": "8.0.2",
"workbox-cli": "7.3.0"
+188 -183
View File
@@ -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.8.0)(lowlight@2.9.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.11.1)(lowlight@3.3.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: 2.9.0
version: 2.9.0
specifier: 3.3.0
version: 3.3.0
pinia:
specifier: 2.3.0
version: 2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3))
@@ -158,8 +158,8 @@ importers:
specifier: 1.7.2
version: 1.7.2
sortablejs:
specifier: 1.15.6
version: 1.15.6
specifier: 1.15.7
version: 1.15.7
tailwindcss:
specifier: 3.4.17
version: 3.4.17
@@ -169,9 +169,6 @@ 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)
@@ -196,19 +193,16 @@ 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@13.17.0)
version: 2.2.5(cypress@14.0.0)
'@cypress/vite-dev-server':
specifier: 6.0.1
version: 6.0.1(cypress@13.17.0)
version: 6.0.1(cypress@14.0.0)
'@cypress/vue':
specifier: 6.0.2
version: 6.0.2(cypress@13.17.0)(vue@3.5.13(typescript@5.7.3))
version: 6.0.2(cypress@14.0.0)(vue@3.5.13(typescript@5.7.3))
'@faker-js/faker':
specifier: 9.4.0
version: 9.4.0
@@ -236,9 +230,6 @@ 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)
@@ -264,14 +255,14 @@ importers:
specifier: 4.24.4
version: 4.24.4
caniuse-lite:
specifier: 1.0.30001692
version: 1.0.30001692
specifier: 1.0.30001695
version: 1.0.30001695
csstype:
specifier: 3.1.3
version: 3.1.3
cypress:
specifier: 13.17.0
version: 13.17.0
specifier: 14.0.0
version: 14.0.0
esbuild:
specifier: 0.24.2
version: 0.24.2
@@ -330,8 +321,8 @@ importers:
specifier: 5.1.0
version: 5.1.0(vue@3.5.13(typescript@5.7.3))
vitest:
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)
specifier: 3.0.2
version: 3.0.2(@types/node@22.10.7)(happy-dom@16.6.0)(jiti@1.21.6)(jsdom@20.0.3)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0)
vue-tsc:
specifier: 2.2.0
version: 2.2.0(typescript@5.7.3)
@@ -1544,6 +1535,7 @@ packages:
'@faker-js/faker@9.4.0':
resolution: {integrity: sha512-85+k0AxaZSTowL0gXp8zYWDIrWclTbRPg/pm/V0dSFZ6W6D4lhcG3uuZl4zLsEKfEvs69xDbLN2cHQudwp95JA==}
engines: {node: '>=18.0.0', npm: '>=9.0.0'}
deprecated: Please update to a newer version
'@floating-ui/core@1.6.7':
resolution: {integrity: sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==}
@@ -1652,22 +1644,26 @@ packages:
resolution: {integrity: sha512-NAmhw1l/llM0HZRpagR/ChJTNymW4ll6/4EDSJML5c8L5Hl/+k6UyF8EIgE6DeHpfheQujkSRngauViHqq6jJQ==}
engines: {node: '>= 16'}
'@intlify/message-compiler@11.0.0-rc.1':
resolution: {integrity: sha512-TGw2uBfuTFTegZf/BHtUQBEKxl7Q/dVGLoqRIdw8lFsp9g/53sYn5iD+0HxIzdYjbWL6BTJMXCPUHp9PxDTRPw==}
engines: {node: '>= 16'}
'@intlify/message-compiler@11.0.1':
resolution: {integrity: sha512-5RFH8x+Mn3mbjcHXnb6KCXGiczBdiQkWkv99iiA0JpKrNuTAQeW59Pjq/uObMB0eR0shnKYGTkIJxum+DbL3sw==}
engines: {node: '>= 16'}
'@intlify/shared@11.0.0-rc.1':
resolution: {integrity: sha512-8tR1xe7ZEbkabTuE/tNhzpolygUn9OaYp9yuYAF4MgDNZg06C3Qny80bes2/e9/Wm3aVkPUlCw6WgU7mQd0yEg==}
'@intlify/message-compiler@12.0.0-alpha.3':
resolution: {integrity: sha512-mDDTN3gfYOHhBnpnlby19UHyvMaOnzdlpsIrxUfs44R/vCATfn8pMOkE8PXD2t410xkocEj3FpDcC9XC/0v4Dg==}
engines: {node: '>= 16'}
'@intlify/shared@11.0.1':
resolution: {integrity: sha512-lH164+aDDptHZ3dBDbIhRa1dOPQUp+83iugpc+1upTOWCnwyC1PVis6rSWNMMJ8VQxvtHQB9JMib48K55y0PvQ==}
engines: {node: '>= 16'}
'@intlify/shared@11.2.8':
resolution: {integrity: sha512-l6e4NZyUgv8VyXXH4DbuucFOBmxLF56C/mqh2tvApbzl2Hrhi1aTDcuv5TKdxzfHYmpO3UB0Cz04fgDT9vszfw==}
engines: {node: '>= 16'}
'@intlify/shared@12.0.0-alpha.3':
resolution: {integrity: sha512-ryaNYBvxQjyJUmVuBBg+HHUsmGnfxcEUPR0NCeG4/K9N2qtyFE35C80S15IN6iYFE2MGWLN7HfOSyg0MXZIc9w==}
engines: {node: '>= 16'}
'@intlify/unplugin-vue-i18n@6.0.3':
resolution: {integrity: sha512-9ZDjBlhUHtgjRl23TVcgfJttgu8cNepwVhWvOv3mUMRDAhjW0pur1mWKEUKr1I8PNwE4Gvv2IQ1xcl4RL0nG0g==}
engines: {node: '>= 18'}
@@ -2385,8 +2381,8 @@ packages:
'@types/har-format@1.2.15':
resolution: {integrity: sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==}
'@types/hast@2.3.10':
resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==}
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
'@types/is-touch-device@1.0.3':
resolution: {integrity: sha512-mEDTb0fQdHRzXLKbwTcpLxFFcQAzfUz6JdSj4qOS8ZevHH78umROLg1ftssalYHKjHz3qPgXBY9DrPsOFcDwMQ==}
@@ -2442,9 +2438,6 @@ 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==}
@@ -2526,34 +2519,34 @@ packages:
vite: ^5.0.0 || ^6.0.0
vue: ^3.2.25
'@vitest/expect@2.1.8':
resolution: {integrity: sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==}
'@vitest/expect@3.0.2':
resolution: {integrity: sha512-dKSHLBcoZI+3pmP5hiZ7I5grNru2HRtEW8Z5Zp4IXog8QYcxhlox7JUPyIIFWfN53+3HW3KPLIl6nSzUGgKSuQ==}
'@vitest/mocker@2.1.8':
resolution: {integrity: sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==}
'@vitest/mocker@3.0.2':
resolution: {integrity: sha512-Hr09FoBf0jlwwSyzIF4Xw31OntpO3XtZjkccpcBf8FeVW3tpiyKlkeUzxS/txzHqpUCNIX157NaTySxedyZLvA==}
peerDependencies:
msw: ^2.4.9
vite: ^5.0.0
vite: ^5.0.0 || ^6.0.0
peerDependenciesMeta:
msw:
optional: true
vite:
optional: true
'@vitest/pretty-format@2.1.8':
resolution: {integrity: sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==}
'@vitest/pretty-format@3.0.2':
resolution: {integrity: sha512-yBohcBw/T/p0/JRgYD+IYcjCmuHzjC3WLAKsVE4/LwiubzZkE8N49/xIQ/KGQwDRA8PaviF8IRO8JMWMngdVVQ==}
'@vitest/runner@2.1.8':
resolution: {integrity: sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==}
'@vitest/runner@3.0.2':
resolution: {integrity: sha512-GHEsWoncrGxWuW8s405fVoDfSLk6RF2LCXp6XhevbtDjdDme1WV/eNmUueDfpY1IX3MJaCRelVCEXsT9cArfEg==}
'@vitest/snapshot@2.1.8':
resolution: {integrity: sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==}
'@vitest/snapshot@3.0.2':
resolution: {integrity: sha512-h9s67yD4+g+JoYG0zPCo/cLTabpDqzqNdzMawmNPzDStTiwxwkyYM1v5lWE8gmGv3SVJ2DcxA2NpQJZJv9ym3g==}
'@vitest/spy@2.1.8':
resolution: {integrity: sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==}
'@vitest/spy@3.0.2':
resolution: {integrity: sha512-8mI2iUn+PJFMT44e3ISA1R+K6ALVs47W6eriDTfXe6lFqlflID05MB4+rIFhmDSLBj8iBsZkzBYlgSkinxLzSQ==}
'@vitest/utils@2.1.8':
resolution: {integrity: sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==}
'@vitest/utils@3.0.2':
resolution: {integrity: sha512-Qu01ZYZlgHvDP02JnMBRpX43nRaZtNpIzw3C1clDXmn8eakgX6iQVGzTQ/NjkIr64WD8ioqOjkaYRVvHQI5qiw==}
'@volar/language-core@2.4.11':
resolution: {integrity: sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==}
@@ -2959,8 +2952,8 @@ packages:
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
engines: {node: '>=6'}
caniuse-lite@1.0.30001692:
resolution: {integrity: sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==}
caniuse-lite@1.0.30001695:
resolution: {integrity: sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==}
capital-case@1.0.4:
resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==}
@@ -3212,9 +3205,9 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
cypress@13.17.0:
resolution: {integrity: sha512-5xWkaPurwkIljojFidhw8lFScyxhtiFHl/i/3zov+1Z5CmY4t9tjIdvSXfu82Y3w7wt0uR9KkucbhkVvJZLQSA==}
engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
cypress@14.0.0:
resolution: {integrity: sha512-kEGqQr23so5IpKeg/dp6GVi7RlHx1NmW66o2a2Q4wk9gRaAblLZQSiZJuDI8UMC4LlG5OJ7Q6joAiqTrfRNbTw==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
dashdash@1.14.1:
@@ -3363,11 +3356,18 @@ 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==}
@@ -3504,8 +3504,8 @@ packages:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
es-module-lexer@1.5.4:
resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==}
es-module-lexer@1.6.0:
resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==}
es-object-atoms@1.0.0:
resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
@@ -3692,9 +3692,6 @@ 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==}
@@ -3797,10 +3794,6 @@ 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'}
@@ -3895,11 +3888,12 @@ packages:
glob@10.4.5:
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Glob versions prior to v9 are no longer supported
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
global-dirs@2.1.0:
resolution: {integrity: sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==}
@@ -3991,8 +3985,8 @@ packages:
header-case@2.0.4:
resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==}
highlight.js@11.8.0:
resolution: {integrity: sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==}
highlight.js@11.11.1:
resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==}
engines: {node: '>=12.0.0'}
histoire@0.17.17:
@@ -4513,8 +4507,8 @@ packages:
resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
engines: {node: '>=8'}
lowlight@2.9.0:
resolution: {integrity: sha512-OpcaUTCLmHuVuBcyNckKfH5B0oA4JUavb/M/8n9iAvanJYNQkrVm4pvyX0SUaqkBG4dnWHKt7p50B3ngAG2Rfw==}
lowlight@3.3.0:
resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==}
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
@@ -4528,6 +4522,9 @@ 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'}
@@ -4700,6 +4697,7 @@ packages:
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
deprecated: Use your platform's native DOMException instead
node-fetch@2.6.7:
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
@@ -4928,6 +4926,9 @@ 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'}
@@ -5319,7 +5320,7 @@ packages:
puppeteer@13.7.0:
resolution: {integrity: sha512-U1uufzBjz3+PkpCxFrWzh4OrMIdIb2ztzCu0YEPfRHjHswcSwHZswnK+WdsOQJsRV8WeTg3jLhJR4D867+fjsA==}
engines: {node: '>=10.18.1'}
deprecated: < 22.8.2 is no longer supported
deprecated: < 24.15.0 is no longer supported
qs@6.13.0:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
@@ -5738,8 +5739,8 @@ packages:
sortablejs@1.14.0:
resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==}
sortablejs@1.15.6:
resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==}
sortablejs@1.15.7:
resolution: {integrity: sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A==}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
@@ -5759,6 +5760,7 @@ packages:
source-map@0.8.0-beta.0:
resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
engines: {node: '>= 8'}
deprecated: The work that was done in this beta branch won't be included in future versions
sourcemap-codec@1.4.8:
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
@@ -5963,19 +5965,19 @@ packages:
tinybench@2.9.0:
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
tinyexec@0.3.1:
resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==}
tinyexec@0.3.2:
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
tinyglobby@0.2.10:
resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==}
engines: {node: '>=12.0.0'}
tinypool@1.0.1:
resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==}
tinypool@1.0.2:
resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==}
engines: {node: ^18.0.0 || >=20.0.0}
tinyrainbow@1.2.0:
resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==}
tinyrainbow@2.0.0:
resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
engines: {node: '>=14.0.0'}
tinyspy@3.0.2:
@@ -6242,10 +6244,6 @@ 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==}
@@ -6263,9 +6261,9 @@ packages:
engines: {node: '>=v14.18.0'}
hasBin: true
vite-node@2.1.8:
resolution: {integrity: sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==}
engines: {node: ^18.0.0 || >=20.0.0}
vite-node@3.0.2:
resolution: {integrity: sha512-hsEQerBAHvVAbv40m3TFQe/lTEbOp7yDpyqMJqr2Tnd+W58+DEYOt+fluQgekOePcsNBmR77lpVAnIU2Xu4SvQ==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
vite-plugin-inspect@0.8.9:
@@ -6383,15 +6381,15 @@ packages:
yaml:
optional: true
vitest@2.1.8:
resolution: {integrity: sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==}
engines: {node: ^18.0.0 || >=20.0.0}
vitest@3.0.2:
resolution: {integrity: sha512-5bzaHakQ0hmVVKLhfh/jXf6oETDBtgPo8tQCHYB+wftNgFJ+Hah67IsWc8ivx4vFL025Ow8UiuTf4W57z4izvQ==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
'@types/node': ^18.0.0 || >=20.0.0
'@vitest/browser': 2.1.8
'@vitest/ui': 2.1.8
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
'@vitest/browser': 3.0.2
'@vitest/ui': 3.0.2
happy-dom: '*'
jsdom: '*'
peerDependenciesMeta:
@@ -6446,6 +6444,7 @@ packages:
vue-i18n@11.0.1:
resolution: {integrity: sha512-pWAT8CusK8q9/EpN7V3oxwHwxWm6+Kp2PeTZmRGvdZTkUzMQDpbbmHp0TwQ8xw04XKm23cr6B4GL72y3W8Yekg==}
engines: {node: '>= 16'}
deprecated: This version is NOT deprecated. Previous deprecation was a mistake.
peerDependencies:
vue: ^3.0.0
@@ -6521,6 +6520,7 @@ packages:
whatwg-encoding@2.0.0:
resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
engines: {node: '>=12'}
deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation
whatwg-mimetype@3.0.0:
resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
@@ -6716,14 +6716,11 @@ 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@13.17.0)':
'@4tw/cypress-drag-drop@2.2.5(cypress@14.0.0)':
dependencies:
cypress: 13.17.0
cypress: 14.0.0
'@akryum/tinypool@0.3.1': {}
@@ -7784,9 +7781,9 @@ snapshots:
tunnel-agent: 0.6.0
uuid: 8.3.2
'@cypress/vite-dev-server@6.0.1(cypress@13.17.0)':
'@cypress/vite-dev-server@6.0.1(cypress@14.0.0)':
dependencies:
cypress: 13.17.0
cypress: 14.0.0
debug: 4.4.0(supports-color@8.1.1)
find-up: 6.3.0
node-html-parser: 5.3.3
@@ -7794,9 +7791,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@cypress/vue@6.0.2(cypress@13.17.0)(vue@3.5.13(typescript@5.7.3))':
'@cypress/vue@6.0.2(cypress@14.0.0)(vue@3.5.13(typescript@5.7.3))':
dependencies:
cypress: 13.17.0
cypress: 14.0.0
vue: 3.5.13(typescript@5.7.3)
'@cypress/xvfb@1.2.4(supports-color@8.1.1)':
@@ -8118,8 +8115,8 @@ snapshots:
'@intlify/bundle-utils@10.0.0(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))':
dependencies:
'@intlify/message-compiler': 11.0.0-rc.1
'@intlify/shared': 11.0.0-rc.1
'@intlify/message-compiler': 12.0.0-alpha.3
'@intlify/shared': 12.0.0-alpha.3
acorn: 8.14.0
escodegen: 2.1.0
estree-walker: 2.0.2
@@ -8135,26 +8132,28 @@ snapshots:
'@intlify/message-compiler': 11.0.1
'@intlify/shared': 11.0.1
'@intlify/message-compiler@11.0.0-rc.1':
dependencies:
'@intlify/shared': 11.0.0-rc.1
source-map-js: 1.2.1
'@intlify/message-compiler@11.0.1':
dependencies:
'@intlify/shared': 11.0.1
source-map-js: 1.2.1
'@intlify/shared@11.0.0-rc.1': {}
'@intlify/message-compiler@12.0.0-alpha.3':
dependencies:
'@intlify/shared': 12.0.0-alpha.3
source-map-js: 1.2.1
'@intlify/shared@11.0.1': {}
'@intlify/shared@11.2.8': {}
'@intlify/shared@12.0.0-alpha.3': {}
'@intlify/unplugin-vue-i18n@6.0.3(@vue/compiler-dom@3.5.13)(eslint@9.18.0(jiti@1.21.6))(rollup@4.30.1)(typescript@5.7.3)(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))':
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.18.0(jiti@1.21.6))
'@intlify/bundle-utils': 10.0.0(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))
'@intlify/shared': 11.0.1
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.0.1)(@vue/compiler-dom@3.5.13)(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))
'@intlify/shared': 11.2.8
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.2.8)(@vue/compiler-dom@3.5.13)(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))
'@rollup/pluginutils': 5.1.3(rollup@4.30.1)
'@typescript-eslint/scope-manager': 8.18.2
'@typescript-eslint/typescript-estree': 8.18.2(typescript@5.7.3)
@@ -8176,11 +8175,11 @@ snapshots:
- supports-color
- typescript
'@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.0.1)(@vue/compiler-dom@3.5.13)(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))':
'@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.2.8)(@vue/compiler-dom@3.5.13)(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))':
dependencies:
'@babel/parser': 7.26.1
optionalDependencies:
'@intlify/shared': 11.0.1
'@intlify/shared': 11.2.8
'@vue/compiler-dom': 3.5.13
vue: 3.5.13(typescript@5.7.3)
vue-i18n: 11.0.1(vue@3.5.13(typescript@5.7.3))
@@ -8595,13 +8594,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.8.0)(lowlight@2.9.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.11.1)(lowlight@3.3.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.8.0
lowlight: 2.9.0
highlight.js: 11.11.1
lowlight: 3.3.0
'@tiptap/extension-code-block@2.11.2(@tiptap/core@2.11.2(@tiptap/pm@2.11.2))(@tiptap/pm@2.11.2)':
dependencies:
@@ -8820,7 +8819,7 @@ snapshots:
'@types/har-format@1.2.15': {}
'@types/hast@2.3.10':
'@types/hast@3.0.4':
dependencies:
'@types/unist': 2.0.11
@@ -8874,8 +8873,6 @@ snapshots:
'@types/unist@2.0.11': {}
'@types/validator@13.12.2': {}
'@types/web-bluetooth@0.0.16': {}
'@types/web-bluetooth@0.0.20': {}
@@ -8993,45 +8990,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@2.1.8':
'@vitest/expect@3.0.2':
dependencies:
'@vitest/spy': 2.1.8
'@vitest/utils': 2.1.8
'@vitest/spy': 3.0.2
'@vitest/utils': 3.0.2
chai: 5.1.2
tinyrainbow: 1.2.0
tinyrainbow: 2.0.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))':
'@vitest/mocker@3.0.2(vite@6.0.7(@types/node@22.10.7)(jiti@1.21.6)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0))':
dependencies:
'@vitest/spy': 2.1.8
'@vitest/spy': 3.0.2
estree-walker: 3.0.3
magic-string: 0.30.14
magic-string: 0.30.17
optionalDependencies:
vite: 5.4.11(@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)
'@vitest/pretty-format@2.1.8':
'@vitest/pretty-format@3.0.2':
dependencies:
tinyrainbow: 1.2.0
tinyrainbow: 2.0.0
'@vitest/runner@2.1.8':
'@vitest/runner@3.0.2':
dependencies:
'@vitest/utils': 2.1.8
pathe: 1.1.2
'@vitest/utils': 3.0.2
pathe: 2.0.1
'@vitest/snapshot@2.1.8':
'@vitest/snapshot@3.0.2':
dependencies:
'@vitest/pretty-format': 2.1.8
magic-string: 0.30.14
pathe: 1.1.2
'@vitest/pretty-format': 3.0.2
magic-string: 0.30.17
pathe: 2.0.1
'@vitest/spy@2.1.8':
'@vitest/spy@3.0.2':
dependencies:
tinyspy: 3.0.2
'@vitest/utils@2.1.8':
'@vitest/utils@3.0.2':
dependencies:
'@vitest/pretty-format': 2.1.8
'@vitest/pretty-format': 3.0.2
loupe: 3.1.2
tinyrainbow: 1.2.0
tinyrainbow: 2.0.0
'@volar/language-core@2.4.11':
dependencies:
@@ -9096,7 +9093,7 @@ snapshots:
'@vue/compiler-ssr': 3.5.13
'@vue/shared': 3.5.13
estree-walker: 2.0.2
magic-string: 0.30.14
magic-string: 0.30.17
postcss: 8.5.1
source-map-js: 1.2.1
@@ -9364,7 +9361,7 @@ snapshots:
autoprefixer@10.4.20(postcss@8.5.1):
dependencies:
browserslist: 4.24.4
caniuse-lite: 1.0.30001692
caniuse-lite: 1.0.30001695
fraction.js: 4.3.7
normalize-range: 0.1.2
picocolors: 1.0.1
@@ -9465,7 +9462,7 @@ snapshots:
browserslist@4.24.4:
dependencies:
caniuse-lite: 1.0.30001692
caniuse-lite: 1.0.30001695
electron-to-chromium: 1.5.73
node-releases: 2.0.19
update-browserslist-db: 1.1.1(browserslist@4.24.4)
@@ -9528,7 +9525,7 @@ snapshots:
camelcase@5.3.1: {}
caniuse-lite@1.0.30001692: {}
caniuse-lite@1.0.30001695: {}
capital-case@1.0.4:
dependencies:
@@ -9803,7 +9800,7 @@ snapshots:
csstype@3.1.3: {}
cypress@13.17.0:
cypress@14.0.0:
dependencies:
'@cypress/request': 3.0.6
'@cypress/xvfb': 1.2.4(supports-color@8.1.1)
@@ -9965,9 +9962,15 @@ 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: {}
@@ -10145,7 +10148,7 @@ snapshots:
es-errors@1.3.0: {}
es-module-lexer@1.5.4: {}
es-module-lexer@1.6.0: {}
es-object-atoms@1.0.0:
dependencies:
@@ -10447,10 +10450,6 @@ 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
@@ -10553,8 +10552,6 @@ 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
@@ -10765,7 +10762,7 @@ snapshots:
capital-case: 1.0.4
tslib: 2.7.0
highlight.js@11.8.0: {}
highlight.js@11.11.1: {}
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:
@@ -11296,11 +11293,11 @@ snapshots:
lowercase-keys@2.0.0: {}
lowlight@2.9.0:
lowlight@3.3.0:
dependencies:
'@types/hast': 2.3.10
fault: 2.0.1
highlight.js: 11.8.0
'@types/hast': 3.0.4
devlop: 1.1.0
highlight.js: 11.11.1
lru-cache@10.4.3: {}
@@ -11316,6 +11313,10 @@ 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
@@ -11690,6 +11691,8 @@ snapshots:
pathe@1.1.2: {}
pathe@2.0.1: {}
pathval@2.0.0: {}
pause-stream@0.0.11:
@@ -12586,7 +12589,7 @@ snapshots:
sortablejs@1.14.0: {}
sortablejs@1.15.6: {}
sortablejs@1.15.7: {}
source-map-js@1.2.1: {}
@@ -12862,16 +12865,16 @@ snapshots:
tinybench@2.9.0: {}
tinyexec@0.3.1: {}
tinyexec@0.3.2: {}
tinyglobby@0.2.10:
dependencies:
fdir: 6.4.2(picomatch@4.0.2)
picomatch: 4.0.2
tinypool@1.0.1: {}
tinypool@1.0.2: {}
tinyrainbow@1.2.0: {}
tinyrainbow@2.0.0: {}
tinyspy@3.0.2: {}
@@ -13126,8 +13129,6 @@ snapshots:
spdx-correct: 3.2.0
spdx-expression-parse: 3.0.1
validator@13.12.0: {}
varint@6.0.0: {}
verror@1.10.0:
@@ -13159,15 +13160,16 @@ snapshots:
- supports-color
- terser
vite-node@2.1.8(@types/node@22.10.7)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6):
vite-node@3.0.2(@types/node@22.10.7)(jiti@1.21.6)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0):
dependencies:
cac: 6.7.14
debug: 4.4.0(supports-color@8.1.1)
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)
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)
transitivePeerDependencies:
- '@types/node'
- jiti
- less
- lightningcss
- sass
@@ -13176,6 +13178,8 @@ 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:
@@ -13274,33 +13278,34 @@ snapshots:
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):
vitest@3.0.2(@types/node@22.10.7)(happy-dom@16.6.0)(jiti@1.21.6)(jsdom@20.0.3)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0):
dependencies:
'@vitest/expect': 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
'@vitest/expect': 3.0.2
'@vitest/mocker': 3.0.2(vite@6.0.7(@types/node@22.10.7)(jiti@1.21.6)(sass-embedded@1.83.4)(sass@1.80.6)(terser@5.31.6)(yaml@2.5.0))
'@vitest/pretty-format': 3.0.2
'@vitest/runner': 3.0.2
'@vitest/snapshot': 3.0.2
'@vitest/spy': 3.0.2
'@vitest/utils': 3.0.2
chai: 5.1.2
debug: 4.3.7
debug: 4.4.0(supports-color@8.1.1)
expect-type: 1.1.0
magic-string: 0.30.14
pathe: 1.1.2
magic-string: 0.30.17
pathe: 2.0.1
std-env: 3.8.0
tinybench: 2.9.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)
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)
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
@@ -13310,6 +13315,8 @@ snapshots:
- sugarss
- supports-color
- terser
- tsx
- yaml
vscode-uri@3.0.8: {}
@@ -13680,5 +13687,3 @@ 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 {lowlight} from 'lowlight'
import {common, createLowlight} from 'lowlight'
import type {BottomAction, UploadCallback} from './types'
import type {ITask} from '@/modelTypes/ITask'
@@ -343,7 +343,7 @@ const extensions : Extensions = [
}),
CodeBlockLowlight.configure({
lowlight,
lowlight: createLowlight(common),
}),
HardBreak.extend({
addKeyboardShortcuts() {
@@ -63,7 +63,7 @@ const motd = computed(() => configStore.motd)
const route = useRoute()
const {t} = useI18n({useScope: 'global'})
const title = computed(() => t(route.meta?.title as string || ''))
const title = computed(() => route.meta?.title ? t(route.meta.title as string) : '')
useTitle(() => title.value)
</script>
@@ -62,29 +62,22 @@ export default {inheritAttrs: false}
</script>
<script setup lang="ts">
import {computed, onBeforeUnmount, onMounted, ref, toRefs, useAttrs, watch, watchEffect, type PropType} from 'vue'
import {computed, onBeforeUnmount, onMounted, ref, toRefs, useAttrs, watch, watchEffect} from 'vue'
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 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 emit = defineEmits([
@@ -1,7 +1,10 @@
<template>
<div
:class="{ 'is-loading': projectService.loading, 'is-archived': currentProject?.isArchived}"
class="loader-container"
:class="{
'is-loading': isLoadingProject,
'is-archived': currentProject?.isArchived,
}"
>
<h1 class="project-title-print">
{{ getProjectTitle(currentProject) }}
@@ -37,43 +40,37 @@
</Message>
</CustomTransition>
<slot v-if="loadedProjectId" />
<slot v-if="!isLoadingProject" />
</div>
</template>
<script setup lang="ts">
import {computed, ref, watch} from 'vue'
import {useRoute} from 'vue-router'
import {computed} from 'vue'
import {useI18n} from 'vue-i18n'
import BaseButton from '@/components/base/BaseButton.vue'
import Message from '@/components/misc/Message.vue'
import CustomTransition from '@/components/misc/CustomTransition.vue'
import ProjectModel from '@/models/project'
import ProjectService from '@/services/project'
import {getProjectTitle} from '@/helpers/getProjectTitle'
import {saveProjectToHistory} from '@/modules/projectHistory'
import {useTitle} from '@/composables/useTitle'
import {useBaseStore} from '@/stores/base'
import {useProjectStore} from '@/stores/projects'
import type {IProject} from '@/modelTypes/IProject'
import type {IProjectView} from '@/modelTypes/IProjectView'
import {useI18n} from 'vue-i18n'
const props = defineProps<{
isLoadingProject: boolean,
projectId: IProject['id'],
viewId: IProjectView['id'],
}>()
const route = useRoute()
const {t} = useI18n()
const baseStore = useBaseStore()
const projectStore = useProjectStore()
const projectService = ref(new ProjectService())
const loadedProjectId = ref(0)
const currentProject = computed<IProject>(() => {
return typeof baseStore.currentProject === 'undefined' ? {
@@ -87,61 +84,6 @@ useTitle(() => currentProject.value?.id ? getProjectTitle(currentProject.value)
const views = computed(() => projectStore.projects[props.projectId]?.views)
// watchEffect would be called every time the prop would get a value assigned, even if that value was the same as before.
// This resulted in loading and setting the project multiple times, even when navigating away from it.
// This caused wired bugs where the project background would be set on the home page but only right after setting a new
// project background and then navigating to home. It also highlighted the project in the menu and didn't allow changing any
// of it, most likely due to the rights not being properly populated.
watch(
() => props.projectId,
// loadProject
async (projectIdToLoad: number) => {
const projectData = {id: projectIdToLoad}
saveProjectToHistory(projectData)
// Don't load the project if we either already loaded it or aren't dealing with a project at all currently and
// the currently loaded project has the right set.
if (
(
projectIdToLoad === loadedProjectId.value ||
typeof projectIdToLoad === 'undefined' ||
projectIdToLoad === currentProject.value?.id
)
&& typeof currentProject.value !== 'undefined' && currentProject.value.maxRight !== null
) {
loadedProjectId.value = projectIdToLoad
return
}
console.debug('Loading project, $route.params =', route.params, `, loadedProjectId = ${loadedProjectId.value}, currentProject = `, currentProject.value)
// Set the current project to the one we're about to load so that the title is already shown at the top
loadedProjectId.value = 0
const projectFromStore = projectStore.projects[projectData.id]
if (projectFromStore) {
baseStore.handleSetCurrentProject({project: projectFromStore, currentProjectViewId: props.viewId})
}
// We create an extra project object instead of creating it in project.value because that would trigger a ui update which would result in bad ux.
const project = new ProjectModel(projectData)
try {
const loadedProject = await projectService.value.get(project)
baseStore.handleSetCurrentProject({project: loadedProject, currentProjectViewId: props.viewId})
} finally {
loadedProjectId.value = projectIdToLoad
}
},
{immediate: true},
)
watch(
() => props.viewId,
() => {
baseStore.setCurrentProjectViewId(props.viewId)
},
{immediate: true},
)
function getViewTitle(view: IProjectView) {
switch (view.title) {
case 'List':
@@ -1,6 +1,7 @@
<template>
<ProjectWrapper
class="project-gantt"
:is-loading-project="isLoadingProject"
:project-id="filters.projectId"
:view-id
>
@@ -95,6 +96,7 @@ import type {IProjectView} from '@/modelTypes/IProjectView'
type Options = Flatpickr.Options.Options
const props = defineProps<{
isLoadingProject: boolean,
route: RouteLocationNormalized
viewId: IProjectView['id']
}>()
@@ -1,6 +1,7 @@
<template>
<ProjectWrapper
class="project-kanban"
:is-loading-project="isLoadingProject"
:project-id="projectId"
:view-id
>
@@ -315,6 +316,7 @@ import TaskBucketService from '@/services/taskBucket'
import TaskBucketModel from '@/models/taskBucket'
const props = defineProps<{
isLoadingProject: boolean,
projectId: number,
viewId: IProjectView['id'],
}>()
@@ -1,6 +1,7 @@
<template>
<ProjectWrapper
class="project-list"
:is-loading-project="isLoadingProject"
:project-id="projectId"
:view-id
>
@@ -123,6 +124,7 @@ import TaskPositionService from '@/services/taskPosition'
import TaskPositionModel from '@/models/taskPosition'
const props = defineProps<{
isLoadingProject: boolean,
projectId: IProject['id'],
viewId: IProjectView['id'],
}>()
@@ -1,6 +1,7 @@
<template>
<ProjectWrapper
class="project-table"
:is-loading-project="isLoadingProject"
:project-id="projectId"
:view-id
>
@@ -298,6 +299,7 @@ import { camelCase } from 'change-case'
import {isSavedFilter} from '@/services/savedFilter'
const props = defineProps<{
isLoadingProject: boolean,
projectId: IProject['id'],
viewId: IProjectView['id'],
}>()
@@ -183,7 +183,7 @@
</template>
<script setup lang="ts">
import {ref, reactive, shallowReactive, watch, computed, type PropType} from 'vue'
import {ref, reactive, shallowReactive, watch, computed} from 'vue'
import {useI18n} from 'vue-i18n'
import {useRoute} from 'vue-router'
@@ -206,27 +206,17 @@ import {useTaskStore} from '@/stores/tasks'
import {useProjectStore} from '@/stores/projects'
import {playPopSound} from '@/helpers/playPop'
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 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 taskStore = useTaskStore()
+2 -2
View File
@@ -1,6 +1,6 @@
import {computed} from 'vue'
import {computed, toValue} from 'vue'
import {useTitle as useTitleVueUse, toValue, type UseTitleOptions, type ReadonlyRefOrGetter, type MaybeRef, type MaybeRefOrGetter} from '@vueuse/core'
import {useTitle as useTitleVueUse, type UseTitleOptions, type ReadonlyRefOrGetter, type MaybeRef, type MaybeRefOrGetter} from '@vueuse/core'
export function useTitle(
newTitle:
+1 -1
View File
@@ -1124,7 +1124,7 @@
}
},
"date": {
"locale": "nb_NO",
"locale": "nn",
"altFormatLong": "d.m.y H:i",
"altFormatShort": "d.m.y"
},
-13
View File
@@ -1,13 +0,0 @@
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>
-10
View File
@@ -1,10 +0,0 @@
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>
-19
View File
@@ -1,19 +0,0 @@
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>
-18
View File
@@ -1,18 +0,0 @@
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>
@@ -1,19 +0,0 @@
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>
-26
View File
@@ -1,26 +0,0 @@
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>
-14
View File
@@ -1,14 +0,0 @@
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>
@@ -1,22 +0,0 @@
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>
-11
View File
@@ -1,11 +0,0 @@
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())
-80
View File
@@ -1,80 +0,0 @@
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>
@@ -1,10 +0,0 @@
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))
-6
View File
@@ -1,6 +0,0 @@
import {number, preprocess} from 'zod'
export const IdSchema = preprocess(
(value: unknown) => Number(value),
number().positive().int(),
)
+14 -18
View File
@@ -1,26 +1,24 @@
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 { REPEAT_TYPES, type IRepeatAfter } from '@/types/IRepeatAfter'
import { nativeEnum, number, object, preprocess } from 'zod'
/**
* Parses `repeatAfterSeconds` into a usable js object.
*/
export function parseRepeatAfter(repeatAfterSeconds: number): IRepeatAfter {
let repeatAfter: IRepeatAfter
let repeatAfter: IRepeatAfter = {type: 'hours', amount: repeatAfterSeconds / SECONDS_A_HOUR}
// if its dividable by SECONDS_A_DAY, its something with days, otherwise hours
// if its dividable by 24, its something with days, otherwise hours
if (repeatAfterSeconds % SECONDS_A_DAY === 0) {
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}
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}
}
}
return repeatAfter
}
@@ -39,6 +37,4 @@ export const RepeatsSchema = preprocess(
type: nativeEnum(REPEAT_TYPES),
amount: number().int(),
}),
)
export type RepeatAfter = TypeOf<typeof RepeatsSchema>
)
@@ -1,3 +0,0 @@
import {string} from 'zod'
export const TextFieldSchema = string().transform((value) => value.trim()).default('')
-11
View File
@@ -1,11 +0,0 @@
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>
-15
View File
@@ -1,15 +0,0 @@
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>
-33
View File
@@ -1,33 +0,0 @@
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>
-26
View File
@@ -1,26 +0,0 @@
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>
-32
View File
@@ -1,32 +0,0 @@
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>
@@ -1,14 +0,0 @@
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>
-26
View File
@@ -1,26 +0,0 @@
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>
-54
View File
@@ -1,54 +0,0 @@
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>
-13
View File
@@ -1,13 +0,0 @@
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>
@@ -1,15 +0,0 @@
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>
-23
View File
@@ -1,23 +0,0 @@
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>
-19
View File
@@ -1,19 +0,0 @@
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>
-119
View File
@@ -1,119 +0,0 @@
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>
-14
View File
@@ -1,14 +0,0 @@
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>
-20
View File
@@ -1,20 +0,0 @@
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>
-24
View File
@@ -1,24 +0,0 @@
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>
-25
View File
@@ -1,25 +0,0 @@
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>
-11
View File
@@ -1,11 +0,0 @@
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>
-13
View File
@@ -1,13 +0,0 @@
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>
-11
View File
@@ -1,11 +0,0 @@
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>
-19
View File
@@ -1,19 +0,0 @@
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>
-9
View File
@@ -1,9 +0,0 @@
import type {TypeOf} from 'zod'
import {object, string} from 'zod'
export const TokenSchema = object({
token: string(),
})
export type IToken = TypeOf<typeof TokenSchema>
-12
View File
@@ -1,12 +0,0 @@
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>
-21
View File
@@ -1,21 +0,0 @@
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>
-11
View File
@@ -1,11 +0,0 @@
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>
-11
View File
@@ -1,11 +0,0 @@
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>
-29
View File
@@ -1,29 +0,0 @@
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>
-19
View File
@@ -1,19 +0,0 @@
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 -8
View File
@@ -1,13 +1,6 @@
import type {IAbstract} from './IAbstract'
export const AVATAR_PROVIDER = [
'default',
'initials',
'gravatar',
'marble',
'upload',
] as const
export type AvatarProvider = typeof AVATAR_PROVIDER[number]
export type AvatarProvider = 'default' | 'initials' | 'gravatar' | 'marble' | 'upload'
export interface IAvatar extends IAbstract {
avatarProvider: AvatarProvider
+2 -5
View File
@@ -7,11 +7,8 @@ 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/johnsoncodehk/volar/blob/2ca8fd3434423c7bea1c8e08132df3b9ce84eea7/extensions/vscode-vue-language-features/README.md#usage
// Under the hidden collapsible "Define Global Components"
declare module '@vue/runtime-core' {
// See: https://github.com/vuejs/language-tools/wiki/Global-Component-Types
declare module 'vue' {
export interface GlobalComponents {
Icon: FontAwesomeIconFixedTypes
Notifications: FunctionalComponent<Notifications>
+73 -16
View File
@@ -1,15 +1,21 @@
<script setup lang="ts">
import {computed, watch} from 'vue'
import {useProjectStore} from '@/stores/projects'
import {computed, ref, shallowReactive, watch, watchEffect} from 'vue'
import {useRoute, useRouter} from 'vue-router'
import {useBaseStore} from '@/stores/base'
import {useProjectStore} from '@/stores/projects'
import {useAuthStore} from '@/stores/auth'
import {saveProjectView} from '@/helpers/projectView'
import ProjectService from '@/services/project'
import ProjectList from '@/components/project/views/ProjectList.vue'
import ProjectGantt from '@/components/project/views/ProjectGantt.vue'
import ProjectTable from '@/components/project/views/ProjectTable.vue'
import ProjectKanban from '@/components/project/views/ProjectKanban.vue'
import {useAuthStore} from '@/stores/auth'
import {DEFAULT_PROJECT_VIEW_SETTINGS} from '@/modelTypes/IProjectView'
import {saveProjectToHistory} from '@/modules/projectHistory'
const props = defineProps<{
projectId: number,
@@ -17,8 +23,10 @@ const props = defineProps<{
}>()
const router = useRouter()
const baseStore = useBaseStore()
const projectStore = useProjectStore()
const authStore = useAuthStore()
const route = useRoute()
const currentProject = computed(() => projectStore.projects[props.projectId])
@@ -26,19 +34,68 @@ const currentView = computed(() => {
return currentProject.value?.views.find(v => v.id === props.viewId)
})
const projectService = shallowReactive(new ProjectService())
const isLoadingProject = computed(() => projectService.loading)
const loadedProjectId = ref(0)
watch(
() => props.projectId,
// loadProject
async (projectIdToLoad, oldProjectIdToLoad) => {
console.debug('Loading project, $route.params =', route.params, `, loadedProjectId = ${loadedProjectId.value}, currentProject = `, currentProject.value)
if (projectIdToLoad !== oldProjectIdToLoad) {
loadedProjectId.value = 0
}
try {
const loadedProject = await projectService.get({id: projectIdToLoad})
// Here, we only set the new project in the projectStore.
// Setting that projet as the current one in the baseStore is handled by the watcher below.
projectStore.setProject(loadedProject)
} finally {
loadedProjectId.value = projectIdToLoad
}
},
{immediate: true},
)
watch(
() => [currentProject.value, props.viewId],
([newCurrentProject, newViewId]) => {
if (!newCurrentProject) {
baseStore.handleSetCurrentProject({project: null})
return
}
baseStore.handleSetCurrentProject({
project: newCurrentProject,
currentProjectViewId: newViewId,
})
}, {
deep: true,
immediate: true,
},
)
function redirectToDefaultViewIfNecessary() {
if (props.viewId === 0 || !projectStore.projects[props.projectId]?.views.find(v => v.id === props.viewId)) {
if (props.viewId === 0 || !currentView.value) {
// Ideally, we would do that in the router redirect, but the projects (and therefore, the views)
// are not always loaded then.
const defaultView = authStore.settings.frontendSettings.defaultView
let view
if (authStore.settings.frontendSettings.defaultView !== DEFAULT_PROJECT_VIEW_SETTINGS.FIRST) {
view = projectStore.projects[props.projectId]?.views.find(v => v.viewKind === authStore.settings.frontendSettings.defaultView)
if (defaultView !== DEFAULT_PROJECT_VIEW_SETTINGS.FIRST) {
view = currentProject.value?.views.find(v => v.viewKind === defaultView)
}
// Use the first view as fallback if the default view is not available
if (view === undefined && projectStore.projects[props.projectId]?.views?.length > 0) {
view = projectStore.projects[props.projectId]?.views[0]
if (view === undefined && currentProject.value?.views?.length > 0) {
view = currentProject.value?.views[0]
}
if (view) {
@@ -60,39 +117,39 @@ watch(
)
watch(
() => projectStore.projects[props.projectId],
currentProject,
redirectToDefaultViewIfNecessary,
)
// using a watcher instead of beforeEnter because beforeEnter is not called when only the viewId changes
watch(
() => [props.projectId, props.viewId],
() => saveProjectView(props.projectId, props.viewId),
{immediate: true},
)
watchEffect(() => saveProjectToHistory({id: props.projectId}))
watchEffect(() => saveProjectView(props.projectId, props.viewId))
const route = useRoute()
watchEffect(() => baseStore.setCurrentProjectViewId(props.viewId))
</script>
<template>
<ProjectList
v-if="currentView?.viewKind === 'list'"
:project-id="projectId"
:is-loading-project="isLoadingProject"
:view-id
/>
<ProjectGantt
v-if="currentView?.viewKind === 'gantt'"
:route
:is-loading-project="isLoadingProject"
:view-id
/>
<ProjectTable
v-if="currentView?.viewKind === 'table'"
:project-id="projectId"
:is-loading-project="isLoadingProject"
:view-id
/>
<ProjectKanban
v-if="currentView?.viewKind === 'kanban'"
:project-id="projectId"
:is-loading-project="isLoadingProject"
:view-id
/>
</template>
+6 -1
View File
@@ -3,7 +3,12 @@
"@tsconfig/node22/tsconfig.json",
"@vue/tsconfig/tsconfig.json"
],
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*"],
"include": [
"env.config.d.ts",
"vite.config.*",
"vitest.config.*",
"cypress.config.*"
],
"compilerOptions": {
"composite": true,
"noEmit": true,
+1 -1
View File
@@ -184,7 +184,7 @@ export default defineConfig(({mode}) => {
},
}),
vueDevTools({
launchEditor: env.VUE_DEVTOOLS_LAUNCH_EDITOR || 'code'
launchEditor: env.VUE_DEVTOOLS_LAUNCH_EDITOR || 'code',
}),
viteSentry(getSentryConfig(env)),
],
+3 -3
View File
@@ -18,7 +18,7 @@ module code.vikunja.io/api
require (
dario.cat/mergo v1.0.1
github.com/ThreeDotsLabs/watermill v1.4.3
github.com/ThreeDotsLabs/watermill v1.4.4
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.0
github.com/wneessen/go-mail v0.6.1
github.com/yuin/goldmark v1.7.8
golang.org/x/crypto v0.32.0
golang.org/x/image v0.23.0
@@ -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.4
toolchain go1.23.5
+4
View File
@@ -664,6 +664,8 @@ 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=
@@ -1375,6 +1377,8 @@ github.com/wneessen/go-mail v0.5.2 h1:MZKwgHJoRboLJ+EHMLuHpZc95wo+u1xViL/4XSswDT
github.com/wneessen/go-mail v0.5.2/go.mod h1:kRroJvEq2hOSEPFRiKjN7Csrz0G1w+RpiGR3b6yo+Ck=
github.com/wneessen/go-mail v0.6.0 h1:wO7EeJ8RL6DD+aycFGntil6b11g3FNQpQQQC1gkm97Y=
github.com/wneessen/go-mail v0.6.0/go.mod h1:G702XlFhzHV0Z4w9j2VsH5K9dJDvj0hx+yOOp1oX9vc=
github.com/wneessen/go-mail v0.6.1 h1:cDGqlGuEEhdILRe53VFzmM9WBk8Xh/QMvbO0oxrNJB4=
github.com/wneessen/go-mail v0.6.1/go.mod h1:G702XlFhzHV0Z4w9j2VsH5K9dJDvj0hx+yOOp1oX9vc=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
+18 -7
View File
@@ -100,6 +100,12 @@ 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) {
@@ -139,10 +145,15 @@ 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 _, has := apiTokenRoutes["other"]; !has {
apiTokenRoutes["other"] = make(APITokenRoute)
if routeGroupName == "notifications" && route.Method == http.MethodPost {
ensureAPITokenRoutesGroup("notifications")
apiTokenRoutes["notifications"]["mark_all_as_read"] = routeDetail
return
}
ensureAPITokenRoutesGroup("other")
_, exists := apiTokenRoutes["other"][routeGroupName]
if exists {
routeGroupName += "_" + strings.ToLower(route.Method)
@@ -168,10 +179,7 @@ func CollectRoutesForAPITokenUsage(route echo.Route, middlewares []echo.Middlewa
if strings.HasSuffix(routeGroupName, "_bulk") {
parent := strings.TrimSuffix(routeGroupName, "_bulk")
_, has := apiTokenRoutes[parent]
if !has {
apiTokenRoutes[parent] = make(APITokenRoute)
}
ensureAPITokenRoutesGroup(parent)
method, routeDetail := getRouteDetail(route)
apiTokenRoutes[parent][method+"_bulk"] = routeDetail
@@ -202,6 +210,7 @@ func CollectRoutesForAPITokenUsage(route echo.Route, middlewares []echo.Middlewa
}
}
}
}
// GetAvailableAPIRoutesForToken returns a list of all API routes which are available for token usage.
@@ -229,7 +238,9 @@ func CanDoAPIRoute(c echo.Context, token *APIToken) (can bool) {
routeGroupName = strings.TrimSuffix(routeGroupName, "_bulk")
if routeGroupName == "user" {
if routeGroupName == "user" ||
routeGroupName == "users" ||
routeGroupName == "routes" {
routeGroupName = "other"
}