Compare commits

...

96 Commits

Author SHA1 Message Date
Frederick [Bot] 1f088cca18 [skip ci] Updated translations via Crowdin 2023-02-05 00:10:26 +00:00
renovate 6fad1e4969 fix(deps): update dependency axios to v1.3.2 2023-02-03 19:04:04 +00:00
Dominik Pschenitschni eaeddda4e4
feat: improve naming and styles 2023-02-03 17:25:38 +01:00
kolaente 7cbf0acac5
fix: always show update popup on top 2023-02-03 17:04:51 +01:00
Dominik Pschenitschni 3db5ea45d7
feat: move update from navigation to app 2023-02-03 17:04:51 +01:00
RoboMagus dcd5c3fd6a
Disable listening on IPv6 ports when IPv6 is not supported (#102) 2023-02-03 15:55:36 +01:00
renovate 61fff44764 chore(deps): update dependency rollup to v3.13.0 2023-02-03 13:03:57 +00:00
renovate ecdae4e03e chore(deps): update dependency vitest to v0.28.4 2023-02-03 11:04:06 +00:00
kolaente b26ea45fe0
chore: update funding links 2023-02-03 11:47:46 +01:00
kolaente 7cb0cd293d
chore: update funding links 2023-02-03 11:46:37 +01:00
konrad 6572f75e5d fix: Use Build Time Base Path (#2964)
Reviewed-on: vikunja/frontend#2964
Reviewed-by: konrad <k@knt.li>
2023-02-03 08:57:27 +00:00
Jef Oliver af55992057
feat(config): Support Setting Base Path in .env
* This uses loadEnv to load an environment file at configuration
  time.
  * Documentation:
    * https://vitejs.dev/config/#environment-variables
  * More on environment files:
    * https://vitejs.dev/guide/env-and-mode.html
  * `VIKUNJA_FRONTEND_BASE` is the variable in the environment
     file that will be used to set Vite’s base option.
* This adds a commented example to .env.local.example

Signed-off-by: Jef Oliver <jef@eljef.me>
2023-02-03 09:21:08 +01:00
Jef Oliver e92559dc00
fix(base): Use Build Time Base Path
* If a base path is provided at build time, use it.
  * Base path can be set with `VIKUNJA_FRONTEND_BASE` at
    build time
    * `VIKUNJA_FRONTEND_BASE` sets `import.meta.env.BASE_URL` after Vite resolves it.
    * Usages of `import.meta.env.BASE_URL` are statically replaced
      at build time.
    * If base path is not provided, `import.meta.env.BASE_URL`
      defaults to '/'.
    * Documentation:
      https://vitejs.dev/guide/env-and-mode.html

* Fixes:
  * Manifest not loading because of incorrect path.
  * Service Worker not loading because path is incorrect in
    manifest.
  * Service Worker crashing because import of workbox is from
    wrong path.
  * Service Worker not loading a task because path is incorrect
    in event listener.
  * Incorrect URLs being set on window because base path is
    incorrect.
    * ex: `/login` vs `/base/login`

Signed-off-by: Jef Oliver <jef@eljef.me>
2023-02-03 09:21:06 +01:00
renovate 3dbf02fd7a chore(deps): update dependency @vue/test-utils to v2.2.10 2023-02-03 00:05:11 +00:00
Dominik Pschenitschni 81a4f2d977 chore: typo 2023-02-02 19:11:08 +00:00
renovate 2972d0d400 chore(deps): update dependency cypress to v12.5.1 2023-02-02 18:03:56 +00:00
renovate c11ebc44c4 chore(deps): update dependency vite to v4.1.1 2023-02-02 15:04:03 +00:00
renovate 144f90c5f7 fix(deps): update sentry-javascript monorepo to v7.36.0 2023-02-02 14:14:47 +00:00
renovate 913879604a chore(deps): update dependency @vitejs/plugin-legacy to v4.0.1 2023-02-02 14:03:59 +00:00
renovate 1589ed5739 chore(deps): update dependency @vitejs/plugin-legacy to v4 2023-02-02 12:04:02 +00:00
renovate a991c537ac chore(deps): update dependency postcss-preset-env to v8 (#3000)
Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/frontend#3000
Reviewed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-02-02 11:49:30 +00:00
renovate 69b57aa23a chore(deps): update dependency vite to v4.1.0 2023-02-02 11:04:00 +00:00
renovate 1a1939963a fix(deps): update dependency vue to v3.2.47 2023-02-02 07:03:57 +00:00
renovate 3d62c9789c fix(deps): update dependency axios to v1.3.1 2023-02-02 06:56:51 +00:00
renovate c18df8687c chore(deps): update dependency @vue/test-utils to v2.2.9 2023-02-02 00:05:20 +00:00
renovate d83ba0c158 fix(deps): update dependency pinia to v2.0.30 (#3042)
Reviewed-on: vikunja/frontend#3042
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-02-01 14:26:35 +00:00
kolaente cea31d1da7
fix(docker): cross compilation with buildx 2023-02-01 15:08:12 +01:00
renovate 12509a7e0f fix(deps): update sentry-javascript monorepo to v7.35.0 (#3041)
Reviewed-on: vikunja/frontend#3041
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-02-01 13:12:20 +00:00
renovate dd43057a08 chore(deps): update dependency rollup to v3.12.1 2023-02-01 10:03:47 +00:00
renovate 19d3cf01cd chore(deps): update pnpm to v7.26.3 2023-02-01 09:36:45 +00:00
renovate 80012bf035 chore(deps): update dependency cypress to v12.5.0 2023-02-01 09:11:17 +00:00
renovate 899d9e1cb7 chore(deps): update dependency sass to v1.58.0 2023-02-01 02:04:06 +00:00
renovate 56830ddadc fix(deps): update dependency axios to v1.3.0 (#3036)
Reviewed-on: vikunja/frontend#3036
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-31 17:16:19 +00:00
kolaente 1749d6ba0a
fix(list): make sure favorite lists are not duplicated in the menu when renaming them
Resolves vikunja/frontend#3031
2023-01-31 17:12:11 +01:00
renovate b29008d304 chore(deps): update typescript-eslint monorepo to v5.50.0 2023-01-31 10:03:47 +00:00
renovate 8ae3054b1a chore(deps): update dependency typescript to v4.9.5 2023-01-30 22:03:50 +00:00
renovate f9dad79b23 chore(deps): update dependency caniuse-lite to v1.0.30001449 2023-01-30 07:21:12 +00:00
renovate 30f5cb0656 chore(deps): update dependency happy-dom to v8.2.0 2023-01-30 07:20:45 +00:00
renovate 3f58c983da chore(deps): update dependency netlify-cli to v12.10.0 2023-01-30 01:04:11 +00:00
kolaente 8fa8b03aa6
fix(tests): only look in src for tests 2023-01-29 20:24:44 +01:00
Yurii Vlasov e4499f44b7 Docker refactoring (#3018)
Co-authored-by: Yurii Vlasov <yv@itsvit.org>
Reviewed-on: vikunja/frontend#3018
Reviewed-by: konrad <k@knt.li>
Co-authored-by: Yurii Vlasov <yuriy@vlasov.pro>
Co-committed-by: Yurii Vlasov <yuriy@vlasov.pro>
2023-01-29 14:47:22 +00:00
kolaente b799233bca
fix(quick add magic): correctly parse "next {weekday}" on the beginning of the text
Resolves vikunja/frontend#3022
2023-01-29 15:32:01 +01:00
renovate be0ae4bc29 chore(deps): update dependency eslint to v8.33.0 2023-01-29 13:49:19 +00:00
renovate 60d99f3bba chore(deps): update pnpm to v7.26.2 2023-01-29 13:48:52 +00:00
renovate fa666d2817 fix(deps): update dependency @vueuse/core to v9.12.0 2023-01-29 04:04:10 +00:00
renovate 9312aa14fa fix(deps): update dependency axios to v1.2.6 2023-01-28 17:03:57 +00:00
renovate 68e4f776b9 chore(deps): update dependency esbuild to v0.17.5 2023-01-28 07:32:56 +00:00
renovate 2d137d564e chore(deps): update dependency rollup to v3.12.0 2023-01-28 06:03:58 +00:00
Frederick [Bot] fc8824d942 [skip ci] Updated translations via Crowdin 2023-01-28 00:27:10 +00:00
renovate 6d4ca57601 chore(deps): update dependency cypress to v12.4.1 2023-01-27 16:04:04 +00:00
renovate d2bf4e38b1 chore(deps): update dependency vitest to v0.28.3 (#3019)
Reviewed-on: vikunja/frontend#3019
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-27 13:13:52 +00:00
renovate a5f6857a40 chore(deps): update dependency @vue/test-utils to v2.2.8 2023-01-27 07:48:21 +00:00
renovate ed3d79fa4c chore(deps): update pnpm to v7.26.1 2023-01-27 05:03:37 +00:00
Frederick [Bot] 81c5c54aed [skip ci] Updated translations via Crowdin 2023-01-27 00:28:14 +00:00
renovate 793e06c6ac fix(deps): update sentry-javascript monorepo to v7.34.0 2023-01-26 22:04:00 +00:00
Nikola Sivkov v2 7eb07e92f8
Add Ipv6 support to nginx (#100)
(cherry picked from commit 0e68bcfd5a518b5cbd0bafce1fc48d31b25e1fa1)
2023-01-26 22:00:49 +01:00
renovate 2a15878b81 fix(deps): update dependency axios to v1.2.5 2023-01-26 16:03:45 +00:00
renovate ebd2b1e8c0 chore(deps): update dependency @vitejs/plugin-legacy to v3.0.2 (#3012)
Reviewed-on: vikunja/frontend#3012
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-26 15:20:58 +00:00
renovate d11fcfa072 chore(deps): update dependency rollup to v3.11.0 (#3013)
Reviewed-on: vikunja/frontend#3013
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-26 14:19:03 +00:00
Frederick [Bot] 8e6e976867 [skip ci] Updated translations via Crowdin 2023-01-26 00:28:07 +00:00
kolaente 9adf1aba89
chore: simplify getting the error text from an exception 2023-01-25 18:44:02 +01:00
kolaente e67088fdb7
chore: simplify error handling for login and OpenId Auth 2023-01-25 18:41:30 +01:00
kolaente da241d21f3
fix(quick actions): hide edges of last entry on hover 2023-01-25 16:26:05 +01:00
kolaente 97133010af
fix(quick actions): don't throw an error message when selecting the last items with the arrow keys 2023-01-25 16:23:46 +01:00
kolaente 4576da0dd3
fix: make sure global error handler handles unrejected promises correctly
Resolves vikunja/frontend#2992
2023-01-25 15:05:54 +01:00
renovate fd4a68daf0 chore(deps): update dependency vitest to v0.28.2 (#3008)
Reviewed-on: vikunja/frontend#3008
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-25 12:30:19 +00:00
renovate 6f02d43801 fix(deps): update dependency @infectoone/vue-ganttastic to v2.1.4 (#3009)
Reviewed-on: vikunja/frontend#3009
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-25 12:29:41 +00:00
konrad 2be784766f feat: small content auth improvements (#2998)
Reviewed-on: vikunja/frontend#2998
2023-01-24 22:09:25 +00:00
Dominik Pschenitschni 13a39be3de feat: unindent settings page (#2996)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2996
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-24 21:54:48 +00:00
renovate d2e07efc7d chore(deps): update dependency cypress to v12.4.0 (#3006)
Reviewed-on: vikunja/frontend#3006
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-24 21:16:24 +00:00
renovate a44299e786 chore(deps): update pnpm to v7.26.0 (#3002)
Reviewed-on: vikunja/frontend#3002
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-24 20:50:13 +00:00
renovate 221f73c347 fix(deps): update dependency axios to v1.2.4 (#3005)
Reviewed-on: vikunja/frontend#3005
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-24 18:21:26 +00:00
renovate 9b170d0d81 fix(deps): update sentry-javascript monorepo to v7.33.0 (#3004)
Reviewed-on: vikunja/frontend#3004
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-24 17:49:43 +00:00
kolaente 16e61a8492
fix(ci): disable auto_tag for release docker images 2023-01-24 18:24:36 +01:00
kolaente a95f1090d7
fix(ci): save .tags file to generate release tags 2023-01-24 17:46:28 +01:00
kolaente c6026107fa
chore: 0.20.3 release preperations 2023-01-24 17:17:03 +01:00
renovate e07e6bf677 fix(deps): update dependency @fortawesome/vue-fontawesome to v3.0.3 (#3003)
Reviewed-on: vikunja/frontend#3003
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-24 15:56:09 +00:00
Dominik Pschenitschni c6ed925424
chore: move class name to top 2023-01-23 22:26:26 +01:00
Dominik Pschenitschni 7ed1a37de5
feat: use v-show for navigation buttons 2023-01-23 22:26:14 +01:00
renovate 1a2e9af88f chore(deps): update dependency start-server-and-test to v1.15.3 2023-01-23 21:24:26 +00:00
renovate f5e90067f6 chore(deps): update typescript-eslint monorepo to v5.49.0 (#2994)
Reviewed-on: vikunja/frontend#2994
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-23 21:07:36 +00:00
renovate 188ae57dc0 chore(deps): update dependency @types/codemirror to v5.60.7 (#2993)
Reviewed-on: vikunja/frontend#2993
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-23 17:17:39 +00:00
renovate 3e4bbd58a3 chore(deps): update dependency vitest to v0.28.1 (#2990)
Reviewed-on: vikunja/frontend#2990
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-23 17:01:15 +00:00
renovate bb8ee15a2d fix(deps): update sentry-javascript monorepo to v7.32.1 (#2991)
Reviewed-on: vikunja/frontend#2991
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-23 17:00:23 +00:00
renovate 4c46ae5b2f chore(deps): update dependency netlify-cli to v12.9.1 (#2988)
Reviewed-on: vikunja/frontend#2988
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-23 10:04:50 +00:00
renovate ff27030e1c chore(deps): update dependency happy-dom to v8.1.5 (#2987)
Reviewed-on: vikunja/frontend#2987
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-23 10:04:03 +00:00
renovate 639e5e3d23 chore(deps): update dependency caniuse-lite to v1.0.30001447 (#2986)
Reviewed-on: vikunja/frontend#2986
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-23 10:03:19 +00:00
Frederick [Bot] e49e9352e5 [skip ci] Updated translations via Crowdin 2023-01-23 00:29:07 +00:00
renovate 705afa0272 chore(deps): update dependency esbuild to v0.17.4 (#2985)
Reviewed-on: vikunja/frontend#2985
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-22 13:14:39 +00:00
renovate 83864a6bac chore(deps): update dependency vitest to v0.27.3 (#2984)
Reviewed-on: vikunja/frontend#2984
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-22 12:03:32 +00:00
renovate 664a39b70d chore(deps): update dependency vite-plugin-inject-preload to v1.2.0 (#2983)
Reviewed-on: vikunja/frontend#2983
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-22 11:59:51 +00:00
Dominik Pschenitschni 9922fcba65 fix: close button hover for sidebar (#2981)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2981
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-21 11:02:17 +00:00
Dominik Pschenitschni 489014944a feat: fix broken font preloading (#2980)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2980
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-21 11:01:32 +00:00
Frederick [Bot] bb44beb4ba [skip ci] Updated translations via Crowdin 2023-01-21 00:29:03 +00:00
renovate d996f24028 chore(deps): update dependency rollup to v3.10.1 2023-01-20 16:03:52 +00:00
renovate 59cac0eb38 fix(deps): update dependency @vueuse/core to v9.11.1 2023-01-20 14:03:51 +00:00
57 changed files with 3580 additions and 2958 deletions

View File

@ -393,6 +393,7 @@ steps:
environment:
DOCKER_AUTOTAG_VERSION: ${DRONE_TAG}
DOCKER_AUTOTAG_EXTRA_TAGS: latest
DOCKER_AUTOTAG_OUTPUT_FILE: .tags
depends_on: [ fetch-tags ]
when:
ref:
@ -408,7 +409,6 @@ steps:
password:
from_secret: docker_password
repo: vikunja/frontend
auto_tag: true
build_args:
- USE_RELEASE=true
- RELEASE_VERSION=${DRONE_TAG##v}
@ -521,6 +521,6 @@ steps:
from_secret: crowdin_key
---
kind: signature
hmac: aa44c442f3901546cad1d83f4251b000f5b95d8af7597553fdacd0838ee8eabe
hmac: 971875b90c7bb1649d1b00d022d0b594ba9b68f927bf8f0dbe840190816d676b
...

View File

@ -1,8 +1,13 @@
# Duplicate this file and remove the '.example' suffix.
# Adjust the values as needed.
# (1) Duplicate this file and remove the '.example' suffix.
# Naming this file '.env.local' is a Vite convention to prevent accidentally
# submitting to git.
# For more info see: https://vitejs.dev/guide/env-and-mode.html#env-files
VITE_IS_ONLINE=true
VITE_WORKBOX_DEBUG=false
SENTRY_AUTH_TOKEN=YOUR_TOKEN
SENTRY_ORG=vikunja
SENTRY_PROJECT=frontend-oss
# (2) Comment in and adjust the values as needed.
# VITE_IS_ONLINE=true
# VITE_WORKBOX_DEBUG=false
# SENTRY_AUTH_TOKEN=YOUR_TOKEN
# SENTRY_ORG=vikunja
# SENTRY_PROJECT=frontend-oss
# VIKUNJA_FRONTEND_BASE=/custom-subpath

3
.github/FUNDING.yml vendored
View File

@ -1,2 +1,3 @@
github: kolaente
custom: https://www.buymeacoffee.com/kolaente
open_collective: vikunja
custom: ["https://vikunja.cloud", "https://www.buymeacoffee.com/kolaente"]

View File

@ -9,6 +9,173 @@ All releases can be found on https://code.vikunja.io/frontend/releases.
The releases aim at the api versions which is why there are missing versions.
## [0.20.3] - 2023-01-24
### Bug Fixes
* *(BaseButton)* Prop type
* *(ci)* Make sure the i18n sync cron job actually runs
* *(ci)* Sign drone config
* *(ci)* Sign drone config
* *(ci)* Tagging logic for release docker images
* *(ci)* Sign drone config
* *(cypress)* Use ts for updateUserSettings
* *(cypress)* Use env for API_URL (#2925)
* *(drone)* Use correct property value (#2920)
* *(drone)* Pnpm cache folder path (#2932)
* *(faker)* Remove mock types (#2921)
* *(i18n)* Incorrect translation string
* *(migration)* Actually pass migration oauth code from query param
* *(quick add magic)* Make sure assignees which don't exist are not removed from task title
* *(task)* Update task description when switching between related tasks
* *(task)* Don't show the list color on the task when only viewing the list (#2975)
* *(useOnline)* Only log if actually faking state (#2924)
* Close button hover for sidebar (#2981) ([9922fcb](9922fcba65c8dc2c46c4f085813c2fbc0d0a7df6))
### Dependencies
* *(deps)* Update dependency vite to v4.0.2 (#2861)
* *(deps)* Update dependency netlify-cli to v12.4.0 (#2862)
* *(deps)* Update typescript-eslint monorepo to v5.47.0 (#2864)
* *(deps)* Update dependency esbuild to v0.16.10 (#2865)
* *(deps)* Update dependency sass to v1.57.1 (#2866)
* *(deps)* Update dependency vue-tsc to v1.0.16 (#2867)
* *(deps)* Update dependency codemirror to v5.65.11
* *(deps)* Update dependency @vueuse/core to v9.8.0
* *(deps)* Update dependency vitest to v0.26.1
* *(deps)* Update dependency @vueuse/core to v9.8.1 (#2870)
* *(deps)* Update dependency @vueuse/core to v9.8.2
* *(deps)* Update sentry-javascript monorepo to v7.28.0
* *(deps)* Update dependency cypress to v12.2.0 (#2873)
* *(deps)* Update dependency vitest to v0.26.2 (#2874)
* *(deps)* Update dependency vite to v4.0.3 (#2876)
* *(deps)* Update pnpm to v7.19.0 (#2875)
* *(deps)* Update dependency rollup to v3.8.0 (#2877)
* *(deps)* Update sentry-javascript monorepo to v7.28.1 (#2878)
* *(deps)* Update dependency @vueuse/core to v9.9.0 (#2881)
* *(deps)* Update dependency rollup to v3.8.1 (#2879)
* *(deps)* Update dependency vite-svg-loader to v4 (#2882)
* *(deps)* Update dependency vue-tsc to v1.0.17 (#2883)
* *(deps)* Update dependency caniuse-lite to v1.0.30001441 (#2884)
* *(deps)* Update dependency netlify-cli to v12.5.0 (#2886)
* *(deps)* Update pnpm to v7.20.0 (#2887)
* *(deps)* Update dependency vue-tsc to v1.0.18 (#2888)
* *(deps)* Update dependency happy-dom to v8.1.1 (#2885)
* *(deps)* Update dependency @types/node to v18.11.18 (#2889)
* *(deps)* Update typescript-eslint monorepo to v5.47.1 (#2890)
* *(deps)* Update dependency esbuild to v0.16.11
* *(deps)* Update dependency esbuild to v0.16.12 (#2893)
* *(deps)* Update dependency rollup to v3.9.0 (#2894)
* *(deps)* Update dependency rollup-plugin-visualizer to v5.9.0 (#2896)
* *(deps)* Update dependency marked to v4.2.5 (#2880)
* *(deps)* Update pnpm to v7.21.0 (#2895)
* *(deps)* Update dependency eslint to v8.31.0
* *(deps)* Update dependency vue-tsc to v1.0.19
* *(deps)* Update dependency @types/codemirror to v5.60.6
* *(deps)* Update dependency rollup to v3.9.1
* *(deps)* Update dependency vitest to v0.26.3
* *(deps)* Update dependency vite-plugin-pwa to v0.14.1 (#2909)
* *(deps)* Update dependency esbuild to v0.16.13 (#2907)
* *(deps)* Update typescript-eslint monorepo to v5.48.0 (#2906)
* *(deps)* Update dependency vue-tsc to v1.0.20
* *(deps)* Update dependency cypress to v12.3.0
* *(deps)* Update dependency @vueuse/core to v9.10.0 (#2911)
* *(deps)* Update pnpm to v7.22.0 (#2910)
* *(deps)* Update dependency @vue/test-utils to v2.2.7 (#2914)
* *(deps)* Update dependency vite to v4.0.4 (#2908)
* *(deps)* Update sentry-javascript monorepo to v7.29.0 (#2915)
* *(deps)* Update dependency esbuild to v0.16.14
* *(deps)* Update dependency axios to v1
* *(deps)* Update dependency vue-tsc to v1.0.21
* *(deps)* Update dependency vue-tsc to v1.0.22
* *(deps)* Update dependency dompurify to v2.4.2
* *(deps)* Update dependency dompurify to v2.4.3 (#2931)
* *(deps)* Update dependency postcss to v8.4.21 (#2933)
* *(deps)* Update dependency esbuild to v0.16.15 (#2934)
* *(deps)* Update dependency vue-tsc to v1.0.24
* *(deps)* Update pnpm to v7.23.0 (#2940)
* *(deps)* Update dependency happy-dom to v8.1.3 (#2939)
* *(deps)* Update dependency esbuild to v0.16.16 (#2937)
* *(deps)* Update dependency caniuse-lite to v1.0.30001442 (#2938)
* *(deps)* Update dependency vitest to v0.27.0 (#2941)
* *(deps)* Update typescript-eslint monorepo to v5.48.1 (#2942)
* *(deps)* Update pnpm to v7.24.2 (#2944)
* *(deps)* Update sentry-javascript monorepo to v7.30.0 (#2945)
* *(deps)* Update pnpm to v7.24.3 (#2946)
* *(deps)* Update dependency vitest to v0.27.1 (#2947)
* *(deps)* Update dependency esbuild to v0.16.17 (#2948)
* *(deps)* Update dependency rollup to v3.10.0 (#2949)
* *(deps)* Update dependency eslint-plugin-vue to v9.9.0 (#2950)
* *(deps)* Update pnpm to v7.25.0 (#2951)
* *(deps)* Update dependency marked to v4.2.12 (#2952)
* *(deps)* Update dependency esbuild to v0.17.0 (#2953)
* *(deps)* Update dependency eslint to v8.32.0 (#2954)
* *(deps)* Update dependency vue-advanced-cropper to v2.8.8 (#2955)
* *(deps)* Update dependency pinia to v2.0.29 (#2956)
* *(deps)* Update dependency @kyvg/vue3-notification to v2.8.0 (#2957)
* *(deps)* Update dependency caniuse-lite to v1.0.30001445 (#2958)
* *(deps)* Update dependency happy-dom to v8.1.4 (#2959)
* *(deps)* Update dependency netlify-cli to v12.7.2 (#2960)
* *(deps)* Update sentry-javascript monorepo to v7.31.0
* *(deps)* Update dependency esbuild to v0.17.1 (#2963)
* *(deps)* Update typescript-eslint monorepo to v5.48.2 (#2962)
* *(deps)* Update dependency esbuild to v0.17.2 (#2965)
* *(deps)* Update dependency vitest to v0.27.2 (#2966)
* *(deps)* Update dependency @vueuse/core to v9.11.0 (#2967)
* *(deps)* Update sentry-javascript monorepo to v7.31.1 (#2973)
* *(deps)* Update dependency axios to v1.2.3 (#2974)
* *(deps)* Update dependency esbuild to v0.17.3 (#2976)
* *(deps)* Update pnpm to v7.25.1 (#2977)
* *(deps)* Update dependency @vueuse/core to v9.11.1
* *(deps)* Update dependency rollup to v3.10.1
* *(deps)* Update dependency vite-plugin-inject-preload to v1.2.0 (#2983)
* *(deps)* Update dependency vitest to v0.27.3 (#2984)
* *(deps)* Update dependency esbuild to v0.17.4 (#2985)
* *(deps)* Update dependency caniuse-lite to v1.0.30001447 (#2986)
* *(deps)* Update dependency happy-dom to v8.1.5 (#2987)
* *(deps)* Update dependency netlify-cli to v12.9.1 (#2988)
* *(deps)* Update sentry-javascript monorepo to v7.32.1 (#2991)
* *(deps)* Update dependency vitest to v0.28.1 (#2990)
* *(deps)* Update dependency @types/codemirror to v5.60.7 (#2993)
* *(deps)* Update typescript-eslint monorepo to v5.49.0 (#2994)
* *(deps)* Update dependency start-server-and-test to v1.15.3
* *(deps)* Update dependency @fortawesome/vue-fontawesome to v3.0.3 (#3003)
### Features
* *(cypress)* Remove getSettled
* *(cypress)* Use cy.session
* *(i18n)* Add Norwegian translation
* *(netlify)* Abstract createSlug helper function (#2923)
* *(postcss)* Mock plugin types (#2930)
* Enable ts for rollup-plugin-visualizer (#2897) ([09d1352](09d13520b060e47be18640865befde44f59332e3))
* Remove date-fns formatISO (#2899) ([1f25386](1f25386f54f376357722e1e589d3a8bd8288a033))
* Add-task usability improvements (#2767) ([4be53b0](4be53b098ca909194aefb464a93b6dae99f4b9ab))
* Remove formatISO from list-view-gantt.spec (#2922) ([a29131e](a29131e7d4be2c83c3e9046549924d1f7692c95e))
* Add histoire ([7be8e89](7be8e892e2480f17cb5de6a69d35287906151c0f))
* Add XButton story ([ccc85b9](ccc85b9a828488dc849758f1e89f3ba3f75967d1))
* Add card story ([35cfb2f](35cfb2f3ca42ac83a9b943fc59818c978ee95fcc))
* Add histoire (#2724) ([a4424e0](a4424e089cdfadb4ab3b753e6fdca818bbe82dc4))
* Add describe project better in package.json (#2971) ([14466bf](14466bf9b7b8a3fc455c0d601205abbaf8cba4f5))
* Add .env.local.example (#2972) ([e1b35ff](e1b35ff023679a7cb8448a06e9edeb8eccc2f727))
* Fix broken font preloading (#2980) ([4890149](489014944a1544846875910d7d5e17e3d71b7e2d))
### Miscellaneous Tasks
* *(config)* Remove unused URL_PREFIX const (#2926)
* *(package)* Use pnpm commands (#2919)
* *(tests)* Fix macos cypress and align with create vite (#2898)
* Improve migrate title (#2968) ([56fd25e](56fd25e888cae8343f64a4c14ac5a3a760bdc7be))
* Add has content="false" to gantt charts (#2969) ([903e9a9](903e9a9904c18ced59962fc03b4c36e5ac8cd688))
* Use es6 imports for deploy-preview-netlify (#2970) ([2a2c27a](2a2c27af9226f441ec80d9d4f560b55cd357126c))
### Other
* *(other)* [skip ci] Updated translations via Crowdin
* *(other)* Redirect to oidc provider if configured correctly (#2805)
## [0.20.2] - 2022-12-18
### Bug Fixes

View File

@ -1,49 +1,69 @@
# Stage 1: Build application
FROM --platform=$BUILDPLATFORM node:18-alpine AS compile-image
# syntax=docker/dockerfile:1
# ┬─┐┬ ┐o┬ ┬─┐
# │─││ │││ │ │
# ┘─┘┘─┘┘┘─┘┘─┘
FROM --platform=$BUILDPLATFORM node:18-alpine AS builder
WORKDIR /build
ARG USE_RELEASE=false
ARG RELEASE_VERSION=main
ENV PNPM_CACHE_FOLDER .cache/pnpm/
ADD . ./
RUN \
if [ $USE_RELEASE = true ]; then \
wget https://dl.vikunja.io/frontend/vikunja-frontend-$RELEASE_VERSION.zip -O frontend-release.zip && \
unzip frontend-release.zip -d dist/ && \
exit 0; \
fi && \
# https://pnpm.io/installation#using-corepack
corepack enable && \
# we don't use corepack prepare here by intend since
# we have renovate to keep our dependencies up to date
# Build the frontend
pnpm install && \
apk add --no-cache git && \
echo '{"VERSION": "'$(git describe --tags --always --abbrev=10 | sed 's/-/+/' | sed 's/^v//' | sed 's/-g/-/')'"}' > src/version.json && \
pnpm run build
COPY package.json ./
COPY pnpm-lock.yaml ./
# Stage 2: copy
FROM nginx:alpine
RUN if [ "$USE_RELEASE" != true ]; then \
# https://pnpm.io/installation#using-corepack
corepack enable && \
pnpm install; \
fi
COPY nginx.conf /etc/nginx/nginx.conf
COPY scripts/run.sh /run.sh
COPY . ./
# copy compiled files from stage 1
COPY --from=compile-image /build/dist /usr/share/nginx/html
RUN if [ "$USE_RELEASE" != true ]; then \
apk add --no-cache --virtual .build-deps git jq && \
git describe --tags --always --abbrev=10 | sed 's/-/+/; s/^v//; s/-g/-/' | \
xargs -0 -I{} jq -Mcnr --arg version {} '{VERSION:$version}' | \
tee src/version.json && \
apk del .build-deps; \
fi
# Unprivileged user
ENV PUID 1000
ENV PGID 1000
RUN if [ "$USE_RELEASE" = true ]; then \
wget "https://dl.vikunja.io/frontend/vikunja-frontend-${RELEASE_VERSION}.zip" -O frontend-release.zip && \
unzip frontend-release.zip -d dist/; \
else \
# we don't use corepack prepare here by intend since
# we have renovate to keep our dependencies up to date
# Build the frontend
pnpm run build; \
fi
# ┌┐┐┌─┐o┌┐┐┐ │
# ││││ ┬││││┌┼┘
# ┘└┘┘─┘┘┘└┘┘ └
FROM nginx:stable-alpine AS runner
WORKDIR /usr/share/nginx/html
LABEL maintainer="maintainers@vikunja.io"
RUN apk add --no-cache \
# for sh file
bash \
# installs usermod and groupmod
shadow
ENV VIKUNJA_HTTP_PORT 80
ENV VIKUNJA_HTTP2_PORT 81
ENV VIKUNJA_LOG_FORMAT main
ENV VIKUNJA_API_URL http://localhost:3456/api/v1
ENV VIKUNJA_SENTRY_ENABLED false
ENV VIKUNJA_SENTRY_DSN https://85694a2d757547cbbc90cd4b55c5a18d@o1047380.ingest.sentry.io/6024480
CMD "/run.sh"
COPY docker/injector.sh /docker-entrypoint.d/50-injector.sh
COPY docker/ipv6-disable.sh /docker-entrypoint.d/60-ipv6-disable.sh
COPY docker/nginx.conf /etc/nginx/nginx.conf
COPY docker/templates/. /etc/nginx/templates/
# copy compiled files from stage 1
COPY --from=builder /build/dist ./
# manage permissions
RUN chmod 0755 /docker-entrypoint.d/*.sh /etc/nginx/templates && \
chmod -R 0644 /etc/nginx/nginx.conf && \
chown -R nginx:nginx ./ /etc/nginx/conf.d /etc/nginx/templates
# unprivileged user
USER nginx

View File

@ -4,7 +4,7 @@
[![Build Status](https://drone.kolaente.de/api/badges/vikunja/frontend/status.svg)](https://drone.kolaente.de/vikunja/frontend)
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](LICENSE)
[![Download](https://img.shields.io/badge/download-v0.20.2-brightgreen.svg)](https://dl.vikunja.io)
[![Download](https://img.shields.io/badge/download-v0.20.3-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.
@ -18,6 +18,14 @@ If you find any security-related issues you don't want to disclose publicly, ple
## Docker
There is a [docker image available](https://hub.docker.com/r/vikunja/api) 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 Refer [to multi-platform documentation](https://docs.docker.com/build/building/multi-platform/) in order to build for the different platform.
## Project setup

15
docker/injector.sh Normal file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env sh
set -e
echo "info: API URL is $VIKUNJA_API_URL"
echo "info: Sentry enabled: $VIKUNJA_SENTRY_ENABLED"
# Escape the variable to prevent sed from complaining
VIKUNJA_API_URL="$(echo "$VIKUNJA_API_URL" | sed -r 's/([:;])/\\\1/g')"
VIKUNJA_SENTRY_DSN="$(echo "$VIKUNJA_SENTRY_DSN" | sed -r 's/([:;])/\\\1/g')"
sed -ri "s:^(\s*window.API_URL\s*=)\s*.+:\1 '${VIKUNJA_API_URL}':g" /usr/share/nginx/html/index.html
sed -ri "s:^(\s*window.SENTRY_ENABLED\s*=)\s*.+:\1 ${VIKUNJA_SENTRY_ENABLED}:g" /usr/share/nginx/html/index.html
sed -ri "s:^(\s*window.SENTRY_DSN\s*=)\s*.+:\1 '${VIKUNJA_SENTRY_DSN}':g" /usr/share/nginx/html/index.html
date -uIseconds | xargs echo 'info: started at'

13
docker/ipv6-disable.sh Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env sh
set -e
DEFAULT_CONF_FILE="etc/nginx/conf.d/default.conf"
if [ -f "/proc/net/if_inet6" ]; then
echo "info: IPv6 available."
exit 0
fi
echo "info: IPv6 not available!"
echo "info: Removing IPv6 lines from /$DEFAULT_CONF_FILE"
sed -i 's/\(listen\s*\[::\].*\)$/#\1 # Disabled IPv6/' /${DEFAULT_CONF_FILE}

112
docker/nginx.conf Normal file
View File

@ -0,0 +1,112 @@
# Generated by nginxconfig.io
# https://www.digitalocean.com/community/tools/nginx?domains.0.server.domain=localhost&domains.0.server.documentRoot=%2Fusr%2Fshare%2Fnginx%2Fhtml&domains.0.server.cdnSubdomain=true&domains.0.https.https=false&domains.0.php.php=false&domains.0.routing.index=index.html&domains.0.routing.fallbackHtml=true&domains.0.routing.fallbackPhp=false&global.performance.assetsExpiration=1d&global.performance.mediaExpiration=1d&global.performance.svgExpiration=1d&global.performance.fontsExpiration=1d&global.logging.accessLog=%2Fdev%2Fstdout&global.logging.errorLog=%2Fdev%2Fstderr%20warn&global.logging.logNotFound=true&global.nginx.user=nginx&global.nginx.pid=%2Fvar%2Frun%2Fnginx.pid&global.nginx.clientMaxBodySize=50&global.docker.dockerfile=true&global.tools.modularizedStructure=false&global.tools.symlinkVhost=false
# and then edited manually ;)
pid /tmp/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 65535;
events {
multi_accept on;
worker_connections 1024;
}
http {
charset utf-8;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
server_tokens off;
types_hash_max_size 2048;
types_hash_bucket_size 64;
# rootless
client_body_temp_path /tmp/client_temp;
proxy_temp_path /tmp/proxy_temp_path;
fastcgi_temp_path /tmp/fastcgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
scgi_temp_path /tmp/scgi_temp;
# MIME
include mime.types;
default_type application/octet-stream;
types {
application/manifest+json webmanifest;
}
# Logging
log_format json escape=json
'{'
'"bytes_sent": "$bytes_sent",'
'"http_user_agent": "$http_user_agent",'
'"nginx_version": "$nginx_version",'
'"query_string": "$query_string",'
'"realip_remote_addr": "$realip_remote_addr",'
'"remote_addr": "$remote_addr",'
'"remote_user": "$remote_user",'
'"request_length": "$request_length",'
'"request_method": "$request_method",'
'"request_time": "$request_time",'
'"server_addr": "$server_addr",'
'"server_port": "$server_port",'
'"server_protocol": "$server_protocol",'
'"status": "$status",'
'"time_local": "$time_local",'
'"uri": "$uri"'
'}';
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /dev/stdout main;
error_log /dev/stderr warn;
keepalive_timeout 65;
# compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types
text/plain
text/css
application/json
application/x-javascript
application/javascript
text/xml
application/xml
application/xml+rss
text/javascript
application/vnd.ms-fontobject
application/x-font-ttf
font/opentype
image/svg+xml
image/x-icon
audio/wav;
map_hash_max_size 128;
map_hash_bucket_size 128;
map $sent_http_content_type $expires {
default off;
text/css max;
application/javascript max;
text/javascript max;
application/vnd.ms-fontobject max;
application/x-font-ttf max;
font/opentype max;
font/woff2 max;
image/svg+xml max;
image/x-icon max;
audio/wav max;
~images/ max;
~font/ max;
}
include /etc/nginx/conf.d/*.conf;
}

View File

@ -0,0 +1,71 @@
server {
listen ${VIKUNJA_HTTP_PORT};
listen [::]:${VIKUNJA_HTTP_PORT};
## Needed when behind HAProxy with SSL termination + HTTP/2 support
listen ${VIKUNJA_HTTP2_PORT} default_server http2 proxy_protocol;
listen [::]:${VIKUNJA_HTTP2_PORT} default_server http2 proxy_protocol;
server_name _;
expires $expires;
root /usr/share/nginx/html;
access_log /dev/stdout ${VIKUNJA_LOG_FORMAT};
# security headers
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';" always;
add_header Permissions-Policy "interest-cohort=()" always;
# . files
location ~ /\.(?!well-known) {
deny all;
}
# assume that everything else is handled by the application router, by injecting the index.html.
location / {
autoindex off;
expires off;
add_header Cache-Control "public, max-age=0, s-maxage=0, must-revalidate" always;
try_files $uri /index.html =404;
}
# favicon.ico
location = /favicon.ico {
log_not_found off;
access_log off;
}
# robots.txt
location = /robots.txt {
log_not_found off;
access_log off;
expires -1; # no-cache
}
location = /ready {
return 200 "";
access_log off;
expires -1; # no-cache
}
# all assets contain hash in filename, cache forever
location ^~ /assets/ {
add_header Cache-Control "public, max-age=31536000, s-maxage=31536000, immutable";
try_files $uri =404;
}
# all workbox scripts are compiled with hash in filename, cache forever3
location ^~ /workbox- {
add_header Cache-Control "public, max-age=31536000, s-maxage=31536000, immutable";
try_files $uri =404;
}
# assets, media
location ~* .(txt|webmanifest|css|js|mjs|map|svg|jpg|jpeg|png|ico|ttf|woff|woff2|wav)$ {
try_files $uri $uri/ =404;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html { }
}

View File

@ -9,9 +9,7 @@
<link rel="icon" href="/favicon.ico">
<link rel="apple-touch-icon" href="/images/icons/apple-touch-icon-180x180.png"/>
<link rel="preload" crossorigin="anonymous" href="/src/assets/fonts/OpenSans[wght].woff2" as="font">
<link rel="preload" crossorigin="anonymous" href="/src/assets/fonts/OpenSans-Italic[wght].woff2" as="font">
<link rel="preload" crossorigin="anonymous" href="/src/assets/fonts/Quicksand[wght].woff2" as="font">
<!--__vite-plugin-inject-preload__-->
</head>
<body>
<noscript>

View File

@ -1,115 +0,0 @@
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
types {
application/manifest+json webmanifest;
}
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types
text/plain
text/css
application/json
application/x-javascript
application/javascript
text/xml
application/xml
application/xml+rss
text/javascript
application/vnd.ms-fontobject
application/x-font-ttf
font/opentype
image/svg+xml
image/x-icon
audio/wav;
map_hash_max_size 128;
map_hash_bucket_size 128;
# Expires map
map $sent_http_content_type $expires {
default off;
text/css max;
application/javascript max;
text/javascript max;
application/vnd.ms-fontobject max;
application/x-font-ttf max;
font/opentype max;
font/woff2 max;
image/svg+xml max;
image/x-icon max;
audio/wav max;
~images/ max;
~font/ max;
}
server {
listen 80;
listen 81 default_server http2 proxy_protocol; ## Needed when behind HAProxy with SSL termination + HTTP/2 support
server_name _;
expires $expires;
root /usr/share/nginx/html;
# all assets contain hash in filename, cache forever
location ^~ /assets/ {
add_header Cache-Control "public, max-age=31536000, s-maxage=31536000, immutable";
try_files $uri =404;
}
# all workbox scripts are compiled with hash in filename, cache forever3
location ^~ /workbox- {
add_header Cache-Control "public, max-age=31536000, s-maxage=31536000, immutable";
try_files $uri =404;
}
# assume that everything else is handled by the application router, by injecting the index.html.
location / {
autoindex off;
expires off;
add_header Cache-Control "public, max-age=0, s-maxage=0, must-revalidate" always;
try_files $uri /index.html =404;
}
location ~* .(txt|webmanifest|css|js|mjs|map|svg|jpg|jpeg|png|ico|ttf|woff|woff2|wav)$ {
try_files $uri $uri/ =404;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
}

View File

@ -13,7 +13,7 @@
},
"homepage": "https://vikunja.io/",
"funding": "https://opencollective.com/vikunja",
"packageManager": "pnpm@7.25.1",
"packageManager": "pnpm@7.26.3",
"keywords": [
"todo",
"productivity",
@ -34,7 +34,7 @@
"test:e2e-record": "start-server-and-test preview http://127.0.0.1:4173 'cypress run --e2e --browser chrome --record'",
"test:e2e-dev-dev": "start-server-and-test preview:dev http://127.0.0.1:4173 'cypress open --e2e'",
"test:e2e-dev": "start-server-and-test preview http://127.0.0.1:4173 'cypress open --e2e'",
"test:unit": "vitest",
"test:unit": "vitest --dir ./src",
"typecheck": "vue-tsc --noEmit && vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
"browserslist:update": "pnpm dlx browserslist@latest --update-db",
"fonts:update": "pnpm fonts:download && pnpm fonts:subset",
@ -48,18 +48,18 @@
"@fortawesome/fontawesome-svg-core": "6.2.1",
"@fortawesome/free-regular-svg-icons": "6.2.1",
"@fortawesome/free-solid-svg-icons": "6.2.1",
"@fortawesome/vue-fontawesome": "3.0.2",
"@fortawesome/vue-fontawesome": "3.0.3",
"@github/hotkey": "2.0.1",
"@infectoone/vue-ganttastic": "2.1.3",
"@infectoone/vue-ganttastic": "2.1.4",
"@intlify/unplugin-vue-i18n": "0.8.1",
"@kyvg/vue3-notification": "2.8.0",
"@sentry/tracing": "7.31.1",
"@sentry/vue": "7.31.1",
"@sentry/tracing": "7.36.0",
"@sentry/vue": "7.36.0",
"@types/is-touch-device": "1.0.0",
"@types/lodash.clonedeep": "4.5.7",
"@types/sortablejs": "1.15.0",
"@vueuse/core": "9.11.0",
"axios": "1.2.3",
"@vueuse/core": "9.12.0",
"axios": "1.3.2",
"blurhash": "2.0.4",
"bulma-css-variables": "0.9.33",
"camel-case": "4.1.2",
@ -79,12 +79,12 @@
"lodash.debounce": "4.0.8",
"marked": "4.2.12",
"minimist": "1.2.7",
"pinia": "2.0.29",
"pinia": "2.0.30",
"register-service-worker": "1.7.2",
"snake-case": "3.0.4",
"sortablejs": "1.15.0",
"ufo": "1.0.1",
"vue": "3.2.45",
"vue": "3.2.47",
"vue-advanced-cropper": "2.8.8",
"vue-flatpickr-component": "11.0.1",
"vue-i18n": "9.2.2",
@ -100,7 +100,7 @@
"@histoire/plugin-screenshot": "0.12.4",
"@histoire/plugin-vue": "0.12.4",
"@rushstack/eslint-patch": "1.2.0",
"@types/codemirror": "5.60.6",
"@types/codemirror": "5.60.7",
"@types/dompurify": "2.4.0",
"@types/flexsearch": "0.7.3",
"@types/focus-within": "1.0.1",
@ -108,37 +108,38 @@
"@types/marked": "4.0.8",
"@types/node": "18.11.18",
"@types/postcss-preset-env": "7.7.0",
"@typescript-eslint/eslint-plugin": "5.48.2",
"@typescript-eslint/parser": "5.48.2",
"@vitejs/plugin-legacy": "3.0.1",
"@typescript-eslint/eslint-plugin": "5.50.0",
"@typescript-eslint/parser": "5.50.0",
"@vitejs/plugin-legacy": "4.0.1",
"@vitejs/plugin-vue": "4.0.0",
"@vue/eslint-config-typescript": "11.0.2",
"@vue/test-utils": "2.2.7",
"@vue/test-utils": "2.2.10",
"@vue/tsconfig": "0.1.3",
"autoprefixer": "10.4.13",
"browserslist": "4.21.4",
"caniuse-lite": "1.0.30001445",
"caniuse-lite": "1.0.30001449",
"csstype": "3.1.1",
"cypress": "12.3.0",
"esbuild": "0.17.3",
"eslint": "8.32.0",
"cypress": "12.5.1",
"esbuild": "0.17.5",
"eslint": "8.33.0",
"eslint-plugin-vue": "9.9.0",
"happy-dom": "8.1.4",
"happy-dom": "8.2.0",
"histoire": "0.12.4",
"netlify-cli": "12.7.2",
"netlify-cli": "12.10.0",
"postcss": "8.4.21",
"postcss-easing-gradients": "3.0.1",
"postcss-easings": "3.0.1",
"postcss-preset-env": "7.8.3",
"rollup": "3.10.0",
"postcss-preset-env": "8.0.1",
"rollup": "3.13.0",
"rollup-plugin-visualizer": "5.9.0",
"sass": "1.57.1",
"start-server-and-test": "1.15.2",
"typescript": "4.9.4",
"vite": "4.0.4",
"sass": "1.58.0",
"start-server-and-test": "1.15.3",
"typescript": "4.9.5",
"vite": "4.1.1",
"vite-plugin-inject-preload": "1.2.0",
"vite-plugin-pwa": "0.14.1",
"vite-svg-loader": "4.0.0",
"vitest": "0.27.2",
"vitest": "0.28.4",
"vue-tsc": "1.0.24",
"wait-on": "7.0.1",
"workbox-cli": "6.5.4"

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +0,0 @@
#!/bin/bash
# This shell script sets the api url based on an environment variable and starts nginx in foreground.
VIKUNJA_API_URL="${VIKUNJA_API_URL:-"/api/v1"}"
VIKUNJA_SENTRY_ENABLED="${VIKUNJA_SENTRY_ENABLED:-"false"}"
VIKUNJA_SENTRY_DSN="${VIKUNJA_SENTRY_DSN:-"https://85694a2d757547cbbc90cd4b55c5a18d@o1047380.ingest.sentry.io/6024480"}"
VIKUNJA_HTTP_PORT="${VIKUNJA_HTTP_PORT:-80}"
VIKUNJA_HTTPS_PORT="${VIKUNJA_HTTPS_PORT:-443}"
echo "Using $VIKUNJA_API_URL as default api url"
# Escape the variable to prevent sed from complaining
VIKUNJA_API_URL=$(echo $VIKUNJA_API_URL |sed 's/\//\\\//g')
sed -i "s/http\:\/\/localhost\:3456//g" /usr/share/nginx/html/index.html # replacing in two steps to make sure api urls from releases are properly replaced as well
sed -i "s/'\/api\/v1/'$VIKUNJA_API_URL/g" /usr/share/nginx/html/index.html
sed -i "s/\.SENTRY_ENABLED = false/\.SENTRY_ENABLED = $VIKUNJA_SENTRY_ENABLED/g" /usr/share/nginx/html/index.html
sed -i "s|\.SENTRY_DSN = '.*'|\.SENTRY_DSN = '$VIKUNJA_SENTRY_DSN'|g" /usr/share/nginx/html/index.html
sed -i "s/listen 80/listen $VIKUNJA_HTTP_PORT/g" /etc/nginx/nginx.conf
sed -i "s/listen 443/listen $VIKUNJA_HTTPS_PORT/g" /etc/nginx/nginx.conf
# Set the uid and gid of the nginx run user
usermod --non-unique --uid ${PUID} nginx
groupmod --non-unique --gid ${PGID} nginx
nginx -g "daemon off;"

View File

@ -8,9 +8,13 @@
<no-auth-wrapper v-else>
<router-view/>
</no-auth-wrapper>
<Notification/>
<keyboard-shortcuts v-if="keyboardShortcutsActive"/>
<Teleport to="body">
<UpdateNotification/>
<Notification/>
</Teleport>
</ready>
</template>
@ -19,23 +23,26 @@ import {computed, watch} from 'vue'
import {useRoute, useRouter} from 'vue-router'
import {useI18n} from 'vue-i18n'
import isTouchDevice from 'is-touch-device'
import {success} from '@/message'
import Notification from '@/components/misc/notification.vue'
import KeyboardShortcuts from './components/misc/keyboard-shortcuts/index.vue'
import UpdateNotification from '@/components/home/UpdateNotification.vue'
import KeyboardShortcuts from '@/components/misc/keyboard-shortcuts/index.vue'
import TheNavigation from '@/components/home/TheNavigation.vue'
import ContentAuth from './components/home/contentAuth.vue'
import ContentLinkShare from './components/home/contentLinkShare.vue'
import ContentAuth from '@/components/home/contentAuth.vue'
import ContentLinkShare from '@/components/home/contentLinkShare.vue'
import NoAuthWrapper from '@/components/misc/no-auth-wrapper.vue'
import Ready from '@/components/misc/ready.vue'
import {setLanguage} from './i18n'
import {setLanguage} from '@/i18n'
import AccountDeleteService from '@/services/accountDelete'
import {success} from '@/message'
import {useAuthStore} from '@/stores/auth'
import {useBaseStore} from '@/stores/base'
import {useColorScheme} from '@/composables/useColorScheme'
import {useBodyClass} from '@/composables/useBodyClass'
import {useAuthStore} from './stores/auth'
const baseStore = useBaseStore()
const authStore = useAuthStore()

View File

@ -25,7 +25,6 @@
</div>
<div class="navbar-end">
<update/>
<BaseButton
@click="openQuickActions"
class="trigger-button pr-0"
@ -95,7 +94,6 @@ import {ref, computed, onMounted, nextTick} from 'vue'
import {RIGHTS as Rights} from '@/constants/rights'
import Update from '@/components/home/update.vue'
import ListSettingsDropdown from '@/components/list/list-settings-dropdown.vue'
import Dropdown from '@/components/misc/dropdown.vue'
import DropdownItem from '@/components/misc/dropdown-item.vue'

View File

@ -1,7 +1,11 @@
<template>
<div class="update-notification" v-if="updateAvailable">
<p>{{ $t('update.available') }}</p>
<x-button @click="refreshApp()" :shadow="false" class="has-no-text-wrap">
<p class="update-notification__message">{{ $t('update.available') }}</p>
<x-button
@click="refreshApp()"
:shadow="false"
:wrap="false"
>
{{ $t('update.do') }}
</x-button>
</div>
@ -16,15 +20,13 @@ const refreshing = ref(false)
document.addEventListener('swUpdated', showRefreshUI, {once: true})
if (navigator && navigator.serviceWorker) {
navigator.serviceWorker.addEventListener(
'controllerchange', () => {
if (refreshing.value) return
refreshing.value = true
window.location.reload()
},
)
}
navigator?.serviceWorker?.addEventListener(
'controllerchange', () => {
if (refreshing.value) return
refreshing.value = true
window.location.reload()
},
)
function showRefreshUI(e: Event) {
console.log('recieved refresh event', e)
@ -33,6 +35,7 @@ function showRefreshUI(e: Event) {
}
function refreshApp() {
updateAvailable.value = false
if (!registration.value || !registration.value.waiting) {
return
}
@ -43,39 +46,30 @@ function refreshApp() {
<style lang="scss" scoped>
.update-notification {
position: fixed;
// FIXME: We should prevent usage of z-index or
// at least define it centrally
// the highest z-index of a modal is .hint-modal with 4500
z-index: 5000;
bottom: 1rem;
inset-inline: 1rem;
max-width: max-content;
margin-inline: auto;
display: flex;
align-items: center;
background: $warning;
justify-content: space-between;
gap: 1rem;
padding: .5rem;
background: $warning;
border-radius: $radius;
font-size: .9rem;
color: var(--grey-900);
justify-content: space-between;
position: fixed;
bottom: 1rem;
width: 450px;
left: calc(50vw - 225px);
@media screen and (max-width: $tablet) {
position: fixed;
left: 1rem;
right: 1rem;
bottom: 1rem;
width: auto;
}
p {
text-align: center;
width: 100%;
}
> * + * {
margin-left: .5rem;
}
}
.dark .update-notification {
color: var(--grey-200);
.update-notification__message {
width: 100%;
text-align: center;
}
</style>

View File

@ -1,16 +1,16 @@
<template>
<div class="content-auth">
<BaseButton
v-if="menuActive"
v-show="menuActive"
@click="baseStore.setMenuActive(false)"
class="menu-hide-button d-print-none"
>
<icon icon="times"/>
</BaseButton>
<div
class="app-container"
:class="{'has-background': background || blurHash}"
:style="{'background-image': blurHash && `url(${blurHash})`}"
class="app-container"
>
<div
:class="{'is-visible': background}"
@ -18,14 +18,14 @@
:style="{'background-image': background && `url(${background})`}"></div>
<navigation class="d-print-none"/>
<main
class="app-content"
:class="[
{ 'is-menu-enabled': menuActive },
$route.name,
]"
class="app-content"
>
<BaseButton
v-if="menuActive"
v-show="menuActive"
@click="baseStore.setMenuActive(false)"
class="mobile-overlay d-print-none"
/>
@ -143,7 +143,6 @@ labelStore.loadAllLabels()
&:hover,
&:focus {
height: 1rem;
color: var(--grey-600);
}
}
@ -225,9 +224,4 @@ labelStore.loadAllLabels()
position: relative;
z-index: 1;
}
.is-touch .content-auth,
.content-auth.z-unset {
z-index: unset;
}
</style>

View File

@ -8,17 +8,20 @@
'has-no-shadow': !shadow || variant === 'tertiary',
}
]"
:style="{
'--button-white-space': wrap ? 'break-spaces' : 'nowrap',
}"
>
<template v-if="icon">
<icon
v-if="showIconOnly"
:icon="icon"
:style="{'color': iconColor !== '' ? iconColor : false}"
:style="{'color': iconColor !== '' ? iconColor : undefined}"
/>
<span class="icon is-small" v-else>
<icon
:icon="icon"
:style="{'color': iconColor !== '' ? iconColor : false}"
:style="{'color': iconColor !== '' ? iconColor : undefined}"
/>
</span>
</template>
@ -50,6 +53,7 @@ export interface ButtonProps extends BaseButtonProps {
iconColor?: string
loading?: boolean
shadow?: boolean
wrap?: boolean
}
const {
@ -58,6 +62,7 @@ const {
iconColor = '',
loading = false,
shadow = true,
wrap = true,
} = defineProps<ButtonProps>()
const variantClass = computed(() => BUTTON_TYPES_MAP[variant])
@ -67,7 +72,7 @@ const showIconOnly = computed(() => icon !== '' && typeof slots.default === 'und
</script>
<style lang="scss" scoped>
.button {
:where(.button) {
transition: all $transition;
border: 0;
text-transform: uppercase;
@ -77,7 +82,7 @@ const showIconOnly = computed(() => icon !== '' && typeof slots.default === 'und
min-height: $button-height;
box-shadow: var(--shadow-sm);
display: inline-flex;
white-space: break-spaces;
white-space: var(--button-white-space);
&:hover {
box-shadow: var(--shadow-md);
@ -99,7 +104,6 @@ const showIconOnly = computed(() => icon !== '' && typeof slots.default === 'und
&.is-primary.is-outlined:hover {
color: var(--white);
}
}
.is-small {

View File

@ -546,7 +546,7 @@ function select(parentIndex: number, index: number) {
}
let elems = resultRefs.value[parentIndex][index]
if (results.value[parentIndex].items.length === index) {
elems = resultRefs.value[parentIndex + 1][0]
elems = resultRefs.value[parentIndex + 1] ? resultRefs.value[parentIndex + 1][0] : undefined
}
if (
typeof elems === 'undefined'
@ -576,6 +576,8 @@ function reset() {
<style lang="scss" scoped>
.quick-actions {
overflow: hidden;
// FIXME: changed position should be an option of the modal
:deep(.modal-content) {
top: 3rem;

View File

@ -0,0 +1,14 @@
/**
* Get full BASE_URL
* - including path
* - will always end with a trailing slash
*/
export function getFullBaseUrl() {
// (1) The injected BASE_URL is declared from the `resolvedBase` that might miss a trailing slash...
// see: https://github.com/vitejs/vite/blob/b35fe883fdc699ac1450882562872095abe9959b/packages/vite/src/node/config.ts#LL614C25-L614C25
const rawBase = import.meta.env.BASE_URL
// (2) so we readd a slash like done here
// https://github.com/vitejs/vite/blob/b35fe883fdc699ac1450882562872095abe9959b/packages/vite/src/node/config.ts#L643
// See this comment: https://github.com/vitejs/vite/pull/10723#issuecomment-1303627478
return rawBase.endsWith('/') ? rawBase : rawBase + '/'
}

View File

@ -233,7 +233,7 @@ export const getDateFromTextIn = (text: string, now: Date = new Date()) => {
}
const getDateFromWeekday = (text: string): dateFoundResult => {
const matcher = / (next )?(monday|mon|tuesday|tue|wednesday|wed|thursday|thu|friday|fri|saturday|sat|sunday|sun)($| )/g
const matcher = /(^| )(next )?(monday|mon|tuesday|tue|wednesday|wed|thursday|thu|friday|fri|saturday|sat|sunday|sun)($| )/g
const results: string[] | null = matcher.exec(text.toLowerCase()) // The i modifier does not seem to work.
if (results === null) {
return {
@ -246,7 +246,7 @@ const getDateFromWeekday = (text: string): dateFoundResult => {
const currentDay: number = date.getDay()
let day = 0
switch (results[2]) {
switch (results[3]) {
case 'mon':
case 'monday':
day = 1

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "There is an update for Vikunja available!",
"available": "There is an update available!",
"do": "Update Now"
},
"menu": {

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "K dispozici je aktualizace pro Vikunja!",
"available": "There is an update available!",
"do": "Aktualizovat nyní"
},
"menu": {

File diff suppressed because it is too large Load Diff

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "Es ist ein Aktualisierung für Vikunja verfügbar!",
"available": "There is an update available!",
"do": "Jetzt aktualisieren"
},
"menu": {

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "Es het es Update für Vikiunja!",
"available": "There is an update available!",
"do": "Jetzt aktualisierä"
},
"menu": {

View File

@ -915,7 +915,7 @@
}
},
"update": {
"available": "There is an update for Vikunja available!",
"available": "There is an update available!",
"do": "Update Now"
},
"menu": {

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "¡Hay una actualización de Vikunja disponible!",
"available": "There is an update available!",
"do": "Actualizar Ahora"
},
"menu": {

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "Il y a une mise à jour pour Vikunja disponible !",
"available": "There is an update available!",
"do": "Mettre à jour maintenant"
},
"menu": {

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Import from other services",
"title": "Importa da altri servizi",
"titleService": "Importa i tuoi dati da {name} in Vikunja",
"import": "Importa i tuoi dati in Vikunja",
"description": "Clicca sul logo di uno dei servizi esterni qui sotto per iniziare.",
@ -911,7 +911,7 @@
}
},
"update": {
"available": "È disponibile un aggiornamento per Vikunja!",
"available": "There is an update available!",
"do": "Aggiorna Adesso"
},
"menu": {

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "Er is een update voor Vikunja beschikbaar!",
"available": "There is an update available!",
"do": "Nu bijwerken"
},
"menu": {

View File

@ -91,7 +91,7 @@
},
"totp": {
"title": "To-faktor-autentisering",
"enroll": "Delta",
"enroll": "Registrere",
"finishSetupPart1": "For å fullføre oppsettet, bruk denne appen (Google Authenticator eller lignende):",
"finishSetupPart2": "Etter det, skriv inn en kode fra appen under.",
"scanQR": "Alternativt kan du skanne denne QR-koden:",
@ -911,7 +911,7 @@
}
},
"update": {
"available": "Det er en oppdatering for Vikunja tilgjengelig!",
"available": "There is an update available!",
"do": "Oppdater Nå"
},
"menu": {

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "Dostępna jest aktualizacja Vikunji!",
"available": "There is an update available!",
"do": "Aktualizuj teraz"
},
"menu": {

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "There is an update for Vikunja available!",
"available": "There is an update available!",
"do": "Atualizar agora"
},
"menu": {

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "Há uma atualização para o Vikunja disponível!",
"available": "Existe uma atualização disponível!",
"do": "Atualizar Agora"
},
"menu": {

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "There is an update for Vikunja available!",
"available": "There is an update available!",
"do": "Update Now"
},
"menu": {

File diff suppressed because it is too large Load Diff

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "There is an update for Vikunja available!",
"available": "There is an update available!",
"do": "Update Now"
},
"menu": {

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "There is an update for Vikunja available!",
"available": "There is an update available!",
"do": "Update Now"
},
"menu": {

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "There is an update for Vikunja available!",
"available": "There is an update available!",
"do": "Update Now"
},
"menu": {

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "Đã có bản cập nhật cho Vikunja!",
"available": "There is an update available!",
"do": "Cập nhật bây giờ"
},
"menu": {

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "Vikunja 有可用的更新!",
"available": "There is an update available!",
"do": "立即更新"
},
"menu": {

View File

@ -911,7 +911,7 @@
}
},
"update": {
"available": "There is an update for Vikunja available!",
"available": "There is an update available!",
"do": "Update Now"
},
"menu": {

View File

@ -1,38 +1,27 @@
import {i18n} from '@/i18n'
import {notify} from '@kyvg/vue3-notification'
export const getErrorText = (r) => {
export function getErrorText(r): string {
const data = r?.reason?.response?.data || r?.response?.data
if (r.response && r.response.data) {
if(r.response.data.code) {
const path = `error.${r.response.data.code}`
const message = i18n.global.t(path)
if (data?.code) {
const path = `error.${data.code}`
const message = i18n.global.t(path)
// If message and path are equal no translation exists for that error code
if (path !== message) {
return [
r.message,
message,
]
}
}
if (r.response.data.message) {
return [
r.message,
r.response.data.message,
]
// If message and path are equal no translation exists for that error code
if (path !== message) {
return message
}
}
return [r.message]
return data?.message || r.message
}
export function error(e, actions = []) {
notify({
type: 'error',
title: i18n.global.t('error.error'),
text: getErrorText(e),
text: [getErrorText(e)],
actions: actions,
})
}
@ -41,7 +30,7 @@ export function success(e, actions = []) {
notify({
type: 'success',
title: i18n.global.t('error.success'),
text: getErrorText(e),
text: [getErrorText(e)],
data: {
actions: actions,
},

View File

@ -124,6 +124,18 @@ describe('Parse Task Text', () => {
expect(result?.date?.getMonth()).toBe(nextMonday.getMonth())
expect(result?.date?.getDate()).toBe(nextMonday.getDate())
})
it('should recognize next monday on the beginning of the sentence', () => {
const result = parseTaskText('next monday Lorem Ipsum')
const untilNextMonday = calculateDayInterval('nextMonday')
expect(result.text).toBe('Lorem Ipsum')
const nextMonday = new Date()
nextMonday.setDate(nextMonday.getDate() + untilNextMonday)
expect(result?.date?.getFullYear()).toBe(nextMonday.getFullYear())
expect(result?.date?.getMonth()).toBe(nextMonday.getMonth())
expect(result?.date?.getDate()).toBe(nextMonday.getDate())
})
it('should recognize next monday and ignore casing', () => {
const result = parseTaskText('Lorem Ipsum nExt Monday')
@ -216,46 +228,7 @@ describe('Parse Task Text', () => {
expect(result?.date?.getDate()).toBe(date.getDate())
})
const cases = {
'monday': 1,
'Monday': 1,
'mon': 1,
'Mon': 1,
'tuesday': 2,
'Tuesday': 2,
'tue': 2,
'Tue': 2,
'wednesday': 3,
'Wednesday': 3,
'wed': 3,
'Wed': 3,
'thursday': 4,
'Thursday': 4,
'thu': 4,
'Thu': 4,
'friday': 5,
'Friday': 5,
'fri': 5,
'Fri': 5,
'saturday': 6,
'Saturday': 6,
'sat': 6,
'Sat': 6,
'sunday': 7,
'Sunday': 7,
'sun': 7,
'Sun': 7,
} as Record<string, number>
for (const c in cases) {
it(`should recognize ${c} as weekday`, () => {
const result = parseTaskText(`Lorem Ipsum ${c}`)
expect(result.text).toBe('Lorem Ipsum')
const nextDate = new Date()
nextDate.setDate(nextDate.getDate() + ((cases[c] + 7 - nextDate.getDay()) % 7))
expect(`${result?.date?.getFullYear()}-${result?.date?.getMonth()}-${result?.date?.getDate()}`).toBe(`${nextDate.getFullYear()}-${nextDate.getMonth()}-${nextDate.getDate()}`)
})
}
it('should recognize weekdays with time', () => {
const result = parseTaskText('Lorem Ipsum thu at 14:00')
@ -369,20 +342,34 @@ describe('Parse Task Text', () => {
describe('Parse weekdays', () => {
const days = {
'mon': 1,
'monday': 1,
'tue': 2,
'Monday': 1,
'mon': 1,
'Mon': 1,
'tuesday': 2,
'wed': 3,
'Tuesday': 2,
'tue': 2,
'Tue': 2,
'wednesday': 3,
'thu': 4,
'Wednesday': 3,
'wed': 3,
'Wed': 3,
'thursday': 4,
'fri': 5,
'Thursday': 4,
'thu': 4,
'Thu': 4,
'friday': 5,
'sat': 6,
'Friday': 5,
'fri': 5,
'Fri': 5,
'saturday': 6,
'sun': 7,
'Saturday': 6,
'sat': 6,
'Sat': 6,
'sunday': 7,
'Sunday': 7,
'sun': 7,
'Sun': 7,
} as Record<string, number>
const prefix = [
@ -399,6 +386,18 @@ describe('Parse Task Text', () => {
const distance = (days[d] + 7 - next.getDay()) % 7
next.setDate(next.getDate() + distance)
expect(result.text).toBe('Lorem Ipsum')
expect(result?.date?.getFullYear()).toBe(next.getFullYear())
expect(result?.date?.getMonth()).toBe(next.getMonth())
expect(result?.date?.getDate()).toBe(next.getDate())
})
it(`should recognize ${p}${d} at the beginning of the text`, () => {
const result = parseTaskText(`${p}${d} Lorem Ipsum`)
const next = new Date()
const distance = (days[d] + 7 - next.getDay()) % 7
next.setDate(next.getDate() + distance)
expect(result.text).toBe('Lorem Ipsum')
expect(result?.date?.getFullYear()).toBe(next.getFullYear())
expect(result?.date?.getMonth()).toBe(next.getMonth())

View File

@ -2,8 +2,10 @@
import {register} from 'register-service-worker'
import {getFullBaseUrl} from './helpers/getFullBaseUrl'
if (import.meta.env.PROD) {
register('/sw.js', {
register(getFullBaseUrl() + 'sw.js', {
ready() {
console.log('App is being served from cache by a service worker.')
},

View File

@ -81,7 +81,7 @@ const EditTeamComponent = () => import('@/views/teams/EditTeam.vue')
const NewTeamComponent = () => import('@/views/teams/NewTeam.vue')
const router = createRouter({
history: createWebHistory(),
history: createWebHistory(import.meta.env.BASE_URL),
scrollBehavior(to, from, savedPosition) {
// If the user is using their forward/backward keys to navigate, we want to restore the scroll view
if (savedPosition) {

View File

@ -124,10 +124,10 @@ export const useListStore = defineStore('list', () => {
...list,
namespaceId: FavoriteListsNamespace,
}
namespaceStore.removeListFromNamespaceById(newList)
if (list.isFavorite) {
namespaceStore.addListToNamespace(newList)
} else {
namespaceStore.removeListFromNamespaceById(newList)
}
namespaceStore.loadNamespacesIfFavoritesDontExist()
namespaceStore.removeFavoritesNamespaceIfEmpty()

View File

@ -2,8 +2,4 @@
@media print {
display: none !important;
}
}
.has-no-text-wrap {
white-space: nowrap !important;
}
}

View File

@ -1,10 +1,16 @@
/* eslint-disable no-console */
/* eslint-disable no-undef */
import {getFullBaseUrl} from './helpers/getFullBaseUrl'
declare let self: ServiceWorkerGlobalScope
const fullBaseUrl = getFullBaseUrl()
const workboxVersion = 'v6.5.4'
importScripts( `/workbox-${workboxVersion}/workbox-sw.js`)
importScripts(`${fullBaseUrl}workbox-${workboxVersion}/workbox-sw.js`)
workbox.setConfig({
modulePathPrefix: `/workbox-${workboxVersion}`,
modulePathPrefix: `${fullBaseUrl}workbox-${workboxVersion}`,
debug: Boolean(import.meta.env.VITE_WORKBOX_DEBUG),
})
@ -47,7 +53,7 @@ self.addEventListener('notificationclick', function (event) {
switch (event.action) {
case 'show-task':
clients.openWindow(`/tasks/${taskId}`)
clients.openWindow(`${fullBaseUrl}tasks/${taskId}`)
break
}
})

View File

@ -193,8 +193,7 @@ async function submit() {
return
}
const err = getErrorText(e)
errorMessage.value = typeof err[1] !== 'undefined' ? err[1] : err[0]
errorMessage.value = getErrorText(e)
}
}
</script>

View File

@ -76,8 +76,7 @@ async function authenticateWithCode() {
})
redirectIfSaved()
} catch(e) {
const err = getErrorText(e)
errorMessage.value = typeof err[1] !== 'undefined' ? err[1] : err[0]
errorMessage.value = getErrorText(e)
} finally {
localStorage.removeItem('authenticating')
}

View File

@ -4,7 +4,7 @@
<nav class="navigation">
<ul>
<li v-for="({routeName, title }, index) in navigationItems" :key="index">
<router-link :to="{name: routeName}">
<router-link class="navigation-link" :to="{name: routeName}">
{{ title }}
</router-link>
</li>
@ -90,39 +90,42 @@ const navigationItems = computed(() => {
.user-settings {
display: flex;
.navigation {
width: 25%;
padding-right: 1rem;
a {
display: block;
padding: .5rem;
color: var(--text);
width: 100%;
border-left: 3px solid transparent;
&:hover, &.router-link-active {
background: var(--white);
border-color: var(--primary);
}
}
}
.view {
width: 75%;
}
@media screen and (max-width: $tablet) {
flex-direction: column;
}
}
.navigation, .view {
width: 100%;
padding-left: 0;
}
.navigation {
width: 25%;
padding-right: 1rem;
.view {
padding-top: 1rem;
}
@media screen and (max-width: $tablet) {
width: 100%;
padding-left: 0;
}
}
.navigation-link {
display: block;
padding: .5rem;
color: var(--text);
width: 100%;
border-left: 3px solid transparent;
&:hover,
&.router-link-active {
background: var(--white);
border-color: var(--primary);
}
}
.view {
width: 75%;
@media screen and (max-width: $tablet) {
width: 100%;
padding-left: 0;
padding-top: 1rem;
}
}
</style>

View File

@ -5,7 +5,7 @@
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"lib": ["ESNext"],
"lib": ["ESNext", "DOM", "WebWorker"],
"importHelpers": true,
"sourceMap": true,

View File

@ -1,13 +1,14 @@
/// <reference types="vitest" />
import {defineConfig, type PluginOption} from 'vite'
import {defineConfig, type PluginOption, loadEnv} from 'vite'
import vue from '@vitejs/plugin-vue'
import legacyFn from '@vitejs/plugin-legacy'
import { URL, fileURLToPath } from 'node:url'
import { dirname, resolve } from 'node:path'
import {URL, fileURLToPath} from 'node:url'
import {dirname, resolve} from 'node:path'
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
import {VitePWA} from 'vite-plugin-pwa'
import {visualizer} from 'rollup-plugin-visualizer'
import {VitePWA} from 'vite-plugin-pwa'
import VitePluginInjectPreload from 'vite-plugin-inject-preload'
import {visualizer} from 'rollup-plugin-visualizer'
import svgLoader from 'vite-svg-loader'
import postcssPresetEnv from 'postcss-preset-env'
import postcssEasings from 'postcss-easings'
@ -33,139 +34,158 @@ console.log(isModernBuild
: 'Building "legacy" build with "@vitejs/plugin-legacy"',
)
/**
* @param fontNames Array of the file names of the fonts without axis and hash suffixes
*/
function createFontMatcher(fontNames: string[]) {
// The `match` option for the files of VitePluginInjectPreload
// matches the _output_ files.
// Since we only want to mach variable fonts, we exploit here the fact
// that we added the `wght` term to indicate the variable weight axis.
// The format is something like:
// `/assets/OpenSans-Italic_wght__c9a8fe68-5f21f1e7.woff2`
// see: https://regex101.com/r/UgUWr1/1
return new RegExp(`^.+\\/(${fontNames.join('|')})_wght__[a-z1-9]{8}-[a-z1-9]{8}\\.woff2$`)
}
// https://vitejs.dev/config/
export default defineConfig({
// https://vitest.dev/config/
test: {
environment: 'happy-dom',
},
css: {
preprocessorOptions: {
scss: {
additionalData: PREFIXED_SCSS_STYLES,
charset: false, // fixes "@charset" must be the first rule in the file" warnings
export default defineConfig(({mode}) => {
// Load env file based on `mode` in the current working directory.
// Set the third parameter to '' to load all env regardless of the `VITE_` prefix.
// https://vitejs.dev/config/#environment-variables
const env = loadEnv(mode, process.cwd(), '')
return {
base: env.VIKUNJA_FRONTEND_BASE,
// https://vitest.dev/config/
test: {
environment: 'happy-dom',
},
css: {
preprocessorOptions: {
scss: {
additionalData: PREFIXED_SCSS_STYLES,
charset: false, // fixes "@charset" must be the first rule in the file" warnings
},
},
},
postcss: {
plugins: [
postcssEasings(),
postcssEasingGradients(),
postcssPresetEnv({
// These plugins are enabled by default but require
// a polyfill that we don't include
// see also './src/polyfills.ts'
features: {
'blank-pseudo-class': false,
'focus-visible-pseudo-class': false,
'has-pseudo-class': false,
'prefers-color-scheme-query': false,
},
}),
],
},
},
plugins: [
vue({
reactivityTransform: true,
}),
legacy,
svgLoader({
// Since the svgs are already manually optimized via https://jakearchibald.github.io/svgomg/
// we don't need to optimize them again.
svgo: false,
}),
VueI18nPlugin({
// TODO: only install needed stuff
// Whether to install the full set of APIs, components, etc. provided by Vue I18n.
// By default, all of them will be installed.
fullInstall: true,
include: resolve(dirname(pathSrc), './src/i18n/lang/**'),
}),
VitePWA({
srcDir: 'src',
filename: 'sw.ts',
base: '/',
strategies: 'injectManifest',
injectRegister: false,
manifest: {
name: 'Vikunja',
short_name: 'Vikunja',
theme_color: '#1973ff',
icons: [
{
src: './images/icons/android-chrome-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: './images/icons/android-chrome-512x512.png',
sizes: '512x512',
type: 'image/png',
},
{
src: './images/icons/icon-maskable.png',
sizes: '1024x1024',
type: 'image/png',
purpose: 'maskable',
},
],
start_url: '.',
display: 'standalone',
background_color: '#000000',
shortcuts: [
{
name: 'Overview',
url: '/',
},
{
name: 'Namespaces And Lists Overview',
short_name: 'Namespaces & Lists',
url: '/namespaces',
},
{
name: 'Tasks Next Week',
short_name: 'Next Week',
url: '/tasks/by/week',
},
{
name: 'Tasks Next Month',
short_name: 'Next Month',
url: '/tasks/by/month',
},
{
name: 'Teams Overview',
short_name: 'Teams',
url: '/teams',
},
postcss: {
plugins: [
postcssEasings(),
postcssEasingGradients(),
postcssPresetEnv(),
],
},
}),
],
resolve: {
alias: [
{
find: '@',
replacement: pathSrc,
},
},
plugins: [
vue({
reactivityTransform: true,
}),
legacy,
svgLoader({
// Since the svgs are already manually optimized via https://jakearchibald.github.io/svgomg/
// we don't need to optimize them again.
svgo: false,
}),
VueI18nPlugin({
// TODO: only install needed stuff
// Whether to install the full set of APIs, components, etc. provided by Vue I18n.
// By default, all of them will be installed.
fullInstall: true,
include: resolve(dirname(pathSrc), './src/i18n/lang/**'),
}),
// https://github.com/Applelo/vite-plugin-inject-preload
VitePluginInjectPreload({
files: [{
match: createFontMatcher(['Quicksand', 'OpenSans', 'OpenSans-Italic']),
attributes: {crossorigin: 'anonymous'},
}],
injectTo: 'custom',
}),
VitePWA({
srcDir: 'src',
filename: 'sw.ts',
strategies: 'injectManifest',
injectRegister: false,
manifest: {
name: 'Vikunja',
short_name: 'Vikunja',
theme_color: '#1973ff',
icons: [
{
src: './images/icons/android-chrome-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: './images/icons/android-chrome-512x512.png',
sizes: '512x512',
type: 'image/png',
},
{
src: './images/icons/icon-maskable.png',
sizes: '1024x1024',
type: 'image/png',
purpose: 'maskable',
},
],
start_url: '.',
display: 'standalone',
background_color: '#000000',
shortcuts: [
{
name: 'Overview',
url: '/',
},
{
name: 'Namespaces And Lists Overview',
short_name: 'Namespaces & Lists',
url: '/namespaces',
},
{
name: 'Tasks Next Week',
short_name: 'Next Week',
url: '/tasks/by/week',
},
{
name: 'Tasks Next Month',
short_name: 'Next Month',
url: '/tasks/by/month',
},
{
name: 'Teams Overview',
short_name: 'Teams',
url: '/teams',
},
],
},
}),
],
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
},
server: {
host: '127.0.0.1', // see: https://github.com/vitejs/vite/pull/8543
port: 4173,
strictPort: true,
},
build: {
target: 'esnext',
rollupOptions: {
plugins: [
visualizer({
filename: 'stats.html',
gzipSize: true,
// template: 'sunburst',
// brotliSize: true,
}) as PluginOption,
resolve: {
alias: [
{
find: '@',
replacement: pathSrc,
},
],
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
},
},
server: {
host: '127.0.0.1', // see: https://github.com/vitejs/vite/pull/8543
port: 4173,
strictPort: true,
},
build: {
target: 'esnext',
rollupOptions: {
plugins: [
visualizer({
filename: 'stats.html',
gzipSize: true,
// template: 'sunburst',
// brotliSize: true,
}) as PluginOption,
],
},
},
}
})