Compare commits

...

144 Commits

Author SHA1 Message Date
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
Frederick [Bot] 7adc5ceb9f [skip ci] Updated translations via Crowdin 2023-01-20 00:31:29 +00:00
renovate a3d9cb5324 chore(deps): update pnpm to v7.25.1 (#2977)
Reviewed-on: vikunja/frontend#2977
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-19 11:14:20 +00:00
Frederick [Bot] 6c192b6f59 [skip ci] Updated translations via Crowdin 2023-01-19 00:30:50 +00:00
konrad 8ff1b3006b fix(task): don't show the list color on the task when only viewing the list (#2975)
Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/frontend#2975
2023-01-18 21:12:59 +00:00
renovate 0414352b02 chore(deps): update dependency esbuild to v0.17.3 (#2976)
Reviewed-on: vikunja/frontend#2976
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-18 20:47:14 +00:00
Dominik Pschenitschni 2a2c27af92 chore: use es6 imports for deploy-preview-netlify (#2970)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2970
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-18 15:54:37 +00:00
Dominik Pschenitschni e1b35ff023 feat: add .env.local.example (#2972)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2972
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-18 00:06:18 +00:00
renovate 95b2bcf5fb fix(deps): update dependency axios to v1.2.3 (#2974)
Reviewed-on: vikunja/frontend#2974
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-17 22:34:18 +00:00
renovate da26ec7f1c fix(deps): update sentry-javascript monorepo to v7.31.1 (#2973)
Reviewed-on: vikunja/frontend#2973
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-17 15:15:06 +00:00
Dominik Pschenitschni 14466bf9b7 feat: add describe project better in package.json (#2971)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2971
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-17 14:46:36 +00:00
Dominik Pschenitschni 903e9a9904 chore: add has content="false" to gantt charts (#2969)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2969
Reviewed-by: konrad <k@knt.li>
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-17 14:32:56 +00:00
Dominik Pschenitschni 56fd25e888 chore: improve migrate title (#2968)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2968
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-17 14:04:48 +00:00
renovate c815830700 fix(deps): update dependency @vueuse/core to v9.11.0 (#2967)
Reviewed-on: vikunja/frontend#2967
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-17 10:48:41 +00:00
renovate 69f398f789 chore(deps): update dependency vitest to v0.27.2 (#2966)
Reviewed-on: vikunja/frontend#2966
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-17 09:42:03 +00:00
renovate 8280bd6bf5 chore(deps): update dependency esbuild to v0.17.2 (#2965)
Reviewed-on: vikunja/frontend#2965
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-17 09:41:25 +00:00
renovate 89559557b8 chore(deps): update typescript-eslint monorepo to v5.48.2 (#2962)
Reviewed-on: vikunja/frontend#2962
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-16 19:22:16 +00:00
renovate 84b1b61af4 chore(deps): update dependency esbuild to v0.17.1 (#2963)
Reviewed-on: vikunja/frontend#2963
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-16 19:21:33 +00:00
renovate 7f84441e78 fix(deps): update sentry-javascript monorepo to v7.31.0 2023-01-16 16:04:13 +00:00
renovate ec6d6018da chore(deps): update dependency netlify-cli to v12.7.2 (#2960)
Reviewed-on: vikunja/frontend#2960
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-16 08:24:22 +00:00
renovate bcec4b97d2 chore(deps): update dependency happy-dom to v8.1.4 (#2959)
Reviewed-on: vikunja/frontend#2959
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-16 08:23:39 +00:00
renovate 9e9e7eaecb chore(deps): update dependency caniuse-lite to v1.0.30001445 (#2958)
Reviewed-on: vikunja/frontend#2958
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-16 08:22:55 +00:00
renovate 665f1a7a18 fix(deps): update dependency @kyvg/vue3-notification to v2.8.0 (#2957)
Reviewed-on: vikunja/frontend#2957
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-15 19:21:51 +00:00
renovate 25a56a89ae fix(deps): update dependency pinia to v2.0.29 (#2956)
Reviewed-on: vikunja/frontend#2956
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-15 16:13:03 +00:00
renovate 9b21d23245 fix(deps): update dependency vue-advanced-cropper to v2.8.8 (#2955)
Reviewed-on: vikunja/frontend#2955
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-15 15:44:36 +00:00
renovate 5c500c711e chore(deps): update dependency eslint to v8.32.0 (#2954)
Reviewed-on: vikunja/frontend#2954
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-15 09:52:49 +00:00
renovate af65447920 chore(deps): update dependency esbuild to v0.17.0 (#2953)
Reviewed-on: vikunja/frontend#2953
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-14 07:42:39 +00:00
renovate 60fee6da7f fix(deps): update dependency marked to v4.2.12 (#2952)
Reviewed-on: vikunja/frontend#2952
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-14 07:40:28 +00:00
renovate d1dbc7f983 chore(deps): update pnpm to v7.25.0 (#2951)
Reviewed-on: vikunja/frontend#2951
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-13 21:14:36 +00:00
renovate 28ace38ebb chore(deps): update dependency eslint-plugin-vue to v9.9.0 (#2950)
Reviewed-on: vikunja/frontend#2950
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-13 07:42:38 +00:00
kolaente 2af42f8fbe
fix(quick add magic): make sure assignees which don't exist are not removed from task title
Resolves vikunja/frontend#2927
2023-01-12 13:32:00 +01:00
kolaente 5999def569
fix(task): update task description when switching between related tasks
Resolves vikunja/frontend#2936
2023-01-12 12:22:56 +01:00
renovate 3b99facbfe chore(deps): update dependency rollup to v3.10.0 (#2949)
Reviewed-on: vikunja/frontend#2949
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-12 08:31:37 +00:00
renovate c980729b0e chore(deps): update dependency esbuild to v0.16.17 (#2948)
Reviewed-on: vikunja/frontend#2948
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-11 22:20:06 +00:00
viehlieb b719766062 redirect to oidc provider if configured correctly (#2805)
Co-authored-by: konrad <k@knt.li>
Reviewed-on: vikunja/frontend#2805
Reviewed-by: konrad <k@knt.li>
Co-authored-by: viehlieb <pf@pragma-shift.net>
Co-committed-by: viehlieb <pf@pragma-shift.net>
2023-01-11 21:17:53 +00:00
kolaente 61592a3c33
fix(ci): sign drone config 2023-01-11 22:03:55 +01:00
renovate e8877174d4 chore(deps): update dependency vitest to v0.27.1 (#2947)
Reviewed-on: vikunja/frontend#2947
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-11 17:21:25 +00:00
kolaente 2edf3aebef
fix(ci): tagging logic for release docker images 2023-01-11 18:15:24 +01:00
renovate 63b409e3bd chore(deps): update pnpm to v7.24.3 (#2946)
Reviewed-on: vikunja/frontend#2946
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-11 06:46:05 +00:00
renovate de45568a17 fix(deps): update sentry-javascript monorepo to v7.30.0 (#2945)
Reviewed-on: vikunja/frontend#2945
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-10 14:23:55 +00:00
renovate c3224a72c1 chore(deps): update pnpm to v7.24.2 (#2944)
Reviewed-on: vikunja/frontend#2944
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-10 14:23:26 +00:00
renovate 6a151a8cf3 chore(deps): update typescript-eslint monorepo to v5.48.1 (#2942)
Reviewed-on: vikunja/frontend#2942
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-09 18:12:51 +00:00
renovate a61ce6f1d6 chore(deps): update dependency vitest to v0.27.0 (#2941)
Reviewed-on: vikunja/frontend#2941
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-09 14:37:26 +00:00
renovate 0124f60bac chore(deps): update dependency caniuse-lite to v1.0.30001442 (#2938)
Reviewed-on: vikunja/frontend#2938
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-09 10:21:24 +00:00
renovate 1a14b1dac6 chore(deps): update dependency esbuild to v0.16.16 (#2937)
Reviewed-on: vikunja/frontend#2937
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-09 10:17:22 +00:00
renovate 9662c79b95 chore(deps): update dependency happy-dom to v8.1.3 (#2939)
Reviewed-on: vikunja/frontend#2939
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-09 10:16:29 +00:00
renovate 43b67a9d33 chore(deps): update pnpm to v7.23.0 (#2940)
Reviewed-on: vikunja/frontend#2940
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-09 10:15:58 +00:00
renovate 37368a64df chore(deps): update dependency vue-tsc to v1.0.24 2023-01-08 14:04:14 +00:00
renovate 098113b3a4 chore(deps): update dependency esbuild to v0.16.15 (#2934)
Reviewed-on: vikunja/frontend#2934
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-07 08:48:07 +00:00
renovate 5c13945393 chore(deps): update dependency postcss to v8.4.21 (#2933)
Reviewed-on: vikunja/frontend#2933
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-06 21:17:25 +00:00
Dominik Pschenitschni 86d957be4f fix(drone): pnpm cache folder path (#2932)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/frontend#2932
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-06 17:48:40 +00:00
renovate 9fe2b4ad73 fix(deps): update dependency dompurify to v2.4.3 (#2931)
Reviewed-on: vikunja/frontend#2931
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-06 15:14:54 +00:00
Dominik Pschenitschni 72e80f637d feat(postcss): mock plugin types (#2930)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2930
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-06 09:27:55 +00:00
konrad a4424e089c feat: add histoire (#2724)
Reviewed-on: vikunja/frontend#2724
2023-01-06 09:27:09 +00:00
Frederick [Bot] 945128c3cd [skip ci] Updated translations via Crowdin 2023-01-06 00:34:21 +00:00
Dominik Pschenitschni 35cfb2f3ca
feat: add card story 2023-01-05 23:12:58 +01:00
Dominik Pschenitschni ccc85b9a82
feat: add XButton story 2023-01-05 23:12:58 +01:00
Dominik Pschenitschni 9523f60763
fix(BaseButton): prop type 2023-01-05 23:12:58 +01:00
Dominik Pschenitschni 7be8e892e2
feat: add histoire 2023-01-05 23:12:58 +01:00
renovate 2a6aff6ffa fix(deps): update dependency dompurify to v2.4.2 2023-01-05 17:04:34 +00:00
Dominik Pschenitschni 6a03972f16 feat(netlify): abstract createSlug helper function (#2923)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2923
Reviewed-by: konrad <k@knt.li>
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-05 16:46:38 +00:00
Dominik Pschenitschni 4023ebcdd1 fix(drone): use correct property value (#2920)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/frontend#2920
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-05 13:50:32 +00:00
Dominik Pschenitschni 6049427322 chore(config): remove unused URL_PREFIX const (#2926)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2926
Reviewed-by: konrad <k@knt.li>
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-05 13:35:28 +00:00
Dominik Pschenitschni f658d3bbba fix(cypress): use env for API_URL (#2925)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2925
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-05 13:34:40 +00:00
Dominik Pschenitschni a029887102 fix(useOnline): only log if actually faking state (#2924)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2924
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-05 13:33:37 +00:00
Dominik Pschenitschni 0f7b7f72d0 fix(faker): remove mock types (#2921)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2921
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-05 13:27:36 +00:00
Dominik Pschenitschni a29131e7d4 feat: remove formatISO from list-view-gantt.spec (#2922)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2922
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-05 13:27:04 +00:00
Dominik Pschenitschni b71d41c5ec chore(package): use pnpm commands (#2919)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2919
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-05 10:29:16 +00:00
renovate 81594e234a chore(deps): update dependency vue-tsc to v1.0.22 2023-01-05 02:05:01 +00:00
renovate 0846d1dc5e chore(deps): update dependency vue-tsc to v1.0.21 2023-01-04 23:26:07 +00:00
renovate 5dfaa48ea5 fix(deps): update dependency axios to v1 2023-01-04 23:13:25 +00:00
renovate 63671efbe2 chore(deps): update dependency esbuild to v0.16.14 2023-01-04 21:04:20 +00:00
Dominik Pschenitschni 4be53b098c feat: add-task usability improvements (#2767)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2767
Reviewed-by: konrad <k@knt.li>
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-04 15:54:09 +00:00
renovate 5a89bc0183 fix(deps): update sentry-javascript monorepo to v7.29.0 (#2915)
Reviewed-on: vikunja/frontend#2915
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-04 14:14:26 +00:00
renovate 97b1149a90 chore(deps): update dependency vite to v4.0.4 (#2908)
Reviewed-on: vikunja/frontend#2908
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-04 13:14:24 +00:00
renovate 2ca33671aa chore(deps): update dependency @vue/test-utils to v2.2.7 (#2914)
Reviewed-on: vikunja/frontend#2914
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-04 13:13:44 +00:00
renovate d82f377f94 chore(deps): update pnpm to v7.22.0 (#2910)
Reviewed-on: vikunja/frontend#2910
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-04 12:20:17 +00:00
kolaente a2cd08a7af
fix(ci): sign drone config 2023-01-04 11:38:06 +01:00
renovate 830d6d0a38 fix(deps): update dependency @vueuse/core to v9.10.0 (#2911)
Reviewed-on: vikunja/frontend#2911
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-04 09:48:37 +00:00
renovate 6cc23ff7aa chore(deps): update dependency cypress to v12.3.0 2023-01-04 03:32:05 +00:00
konrad bb4ed3d223 chore(tests): fix macos cypress and align with create vite (#2898)
Reviewed-on: vikunja/frontend#2898
Reviewed-by: konrad <k@knt.li>
2023-01-04 03:19:36 +00:00
renovate f56302a99f chore(deps): update dependency vue-tsc to v1.0.20 2023-01-04 00:05:43 +00:00
Dominik Pschenitschni d850d5b98f
fix(cypress): use ts for updateUserSettings 2023-01-03 16:30:46 +01:00
Dominik Pschenitschni 4908469d49
feat(cypress): use cy.session
Also align repo closer to `create-vue` template and improve cypress integration
2023-01-03 16:30:46 +01:00
Dominik Pschenitschni 6ddfba4f1f
feat(cypress): remove getSettled 2022-12-30 16:39:51 +01:00
127 changed files with 5198 additions and 1682 deletions

View File

@ -96,7 +96,7 @@ steps:
- dependencies
- name: test-frontend
image: cypress/browsers:node18.12.0-chrome106-ff106
image: cypress/browsers:node18.12.0-chrome107
pull: always
environment:
CYPRESS_API_URL: http://api:3456/api/v1
@ -110,8 +110,7 @@ steps:
- sed -i 's/localhost/api/g' dist/index.html
- corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm cypress install
- pnpm run serve:dist & npx wait-on http://localhost:4173
- pnpm run test:frontend --browser chrome --record
- pnpm run test:e2e-record
depends_on:
- build-prod
@ -150,8 +149,10 @@ steps:
# Override the default api url used for preview
- sed -i 's|http://localhost:3456|https://try.vikunja.io|g' dist-preview/index.html
- apk add --no-cache perl-utils
- shasum -a 384 -c ./scripts/deploy-preview-netlify.js.sha384
- node ./scripts/deploy-preview-netlify.js
# create via:
# `shasum -a 384 ./scripts/deploy-preview-netlify.mjs > ./scripts/deploy-preview-netlify.mjs.sha384`
- shasum -a 384 -c ./scripts/deploy-preview-netlify.mjs.sha384
- node ./scripts/deploy-preview-netlify.mjs
depends_on:
- build-prod
when:
@ -204,7 +205,7 @@ steps:
PNPM_CACHE_FOLDER: .cache/pnpm
commands:
- apk add git
- corepack enable && pnpm config set store-dir .cache/.pnpm
- corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm install --fetch-timeout 100000 --frozen-lockfile
- pnpm run lint
- "echo '{\"VERSION\": \"'$(git describe --tags --always --abbrev=10 | sed 's/-/+/' | sed 's/^v//' | sed 's/-g/-/')'\"}' > src/version.json"
@ -365,7 +366,7 @@ steps:
- name: docker-unstable
image: thegeeklab/drone-docker-buildx
privileged: true
pull: true
pull: always
settings:
username:
from_secret: docker_username
@ -387,17 +388,27 @@ steps:
ref:
- refs/heads/main
- name: generate-tags
image: thegeeklab/docker-autotag
environment:
DOCKER_AUTOTAG_VERSION: ${DRONE_TAG}
DOCKER_AUTOTAG_EXTRA_TAGS: latest
DOCKER_AUTOTAG_OUTPUT_FILE: .tags
depends_on: [ fetch-tags ]
when:
ref:
- "refs/tags/**"
- name: docker-release
image: thegeeklab/drone-docker-buildx
privileged: true
pull: true
pull: always
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
repo: vikunja/frontend
auto_tag: true
build_args:
- USE_RELEASE=true
- RELEASE_VERSION=${DRONE_TAG##v}
@ -407,7 +418,7 @@ steps:
- linux/arm/v6
- linux/arm/v7
- linux/arm64/v8
depends_on: [ fetch-tags ]
depends_on: [ generate-tags ]
when:
ref:
- "refs/tags/**"
@ -510,6 +521,6 @@ steps:
from_secret: crowdin_key
---
kind: signature
hmac: 381766491766da14ff022383ce0107955f811f7ccb55e95c9c03094e4df809e1
hmac: 971875b90c7bb1649d1b00d022d0b594ba9b68f927bf8f0dbe840190816d676b
...

8
.env.local.example Normal file
View File

@ -0,0 +1,8 @@
# Duplicate this file and remove the '.example' suffix.
# 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

37
.gitignore vendored
View File

@ -1,26 +1,31 @@
.DS_Store
node_modules
/dist*
*.zip
.direnv/
# local env files
.env.local
.env.*.local
# Log files
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
stats.html
pnpm-debug.log*
lerna-debug.log*
stats.html
node_modules
.DS_Store
/dist*
coverage
*.zip
.direnv/
# Test files
cypress/screenshots
cypress/videos
# local env files
.env.local
.env.*.local
# Editor directories and files
.idea
.vscode
.idea
*.suo
*.ntvs*
*.njsproj
@ -28,9 +33,9 @@ lerna-debug.log*
*.sw*
!rollup.sw.js
# Test files
cypress/screenshots
cypress/videos
# Local Netlify folder
.netlify
# histoire
.histoire

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,68 @@
# Stage 1: Build application
FROM --platform=$BUILDPLATFORM node:18-alpine AS compile-image
# syntax=docker/dockerfile:1
# ┬─┐┬ ┐o┬ ┬─┐
# │─││ │││ │ │
# ┘─┘┘─┘┘┘─┘┘─┘
FROM 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/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

View File

@ -11,8 +11,10 @@ export default defineConfig({
},
projectId: '181c7x',
e2e: {
baseUrl: 'http://localhost:4173',
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
specPattern: 'cypress/e2e/**/*.{cy,spec}.{js,jsx,ts,tsx}',
baseUrl: 'http://127.0.0.1:4173',
experimentalRunAllSpecs: true,
// testIsolation: false,
},
component: {
devServer: {

View File

@ -36,7 +36,7 @@ to get a shell inside the cypress container.
In that shell you can then execute the tests with
```shell
pnpm run test:frontend
pnpm run test:e2e
```
### Using The Cypress Dashboard
@ -44,5 +44,5 @@ pnpm run test:frontend
To open the Cypress Dashboard and run tests from there, run
```shell
pnpm run cypress:open
pnpm run test:e2e:dev
```

View File

@ -9,7 +9,7 @@ services:
ports:
- 3456:3456
cypress:
image: cypress/browsers:node16.14.0-chrome99-ff97
image: cypress/browsers:node18.12.0-chrome107
volumes:
- ..:/project
- $HOME/.cache:/home/node/.cache/

View File

@ -1,9 +1,10 @@
import {ListFactory} from '../../factories/list'
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import '../../support/authenticateUser'
import {ListFactory} from '../../factories/list'
import {prepareLists} from './prepareLists'
describe('List History', () => {
createFakeUserAndLogin()
prepareLists()
it('should show a list history on the home page', () => {

View File

@ -1,10 +1,12 @@
import {formatISO, format} from 'date-fns'
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {TaskFactory} from '../../factories/task'
import {prepareLists} from './prepareLists'
import '../../support/authenticateUser'
describe('List View Gantt', () => {
createFakeUserAndLogin()
prepareLists()
it('Hides tasks with no dates', () => {
@ -33,8 +35,8 @@ describe('List View Gantt', () => {
it('Shows tasks with dates', () => {
const now = new Date()
const tasks = TaskFactory.create(1, {
start_date: formatISO(now),
end_date: formatISO(now.setDate(now.getDate() + 4)),
start_date: now.toISOString(),
end_date: new Date(new Date(now).setDate(now.getDate() + 4)).toISOString(),
})
cy.visit('/lists/1/gantt')
@ -60,13 +62,12 @@ describe('List View Gantt', () => {
})
it('Drags a task around', () => {
cy.intercept('**/api/v1/tasks/*')
.as('taskUpdate')
cy.intercept(Cypress.env('API_URL') + '/tasks/*').as('taskUpdate')
const now = new Date()
TaskFactory.create(1, {
start_date: formatISO(now),
end_date: formatISO(now.setDate(now.getDate() + 4)),
start_date: now.toISOString(),
end_date: new Date(new Date(now).setDate(now.getDate() + 4)).toISOString(),
})
cy.visit('/lists/1/gantt')

View File

@ -1,14 +1,15 @@
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {BucketFactory} from '../../factories/bucket'
import {ListFactory} from '../../factories/list'
import {TaskFactory} from '../../factories/task'
import {prepareLists} from './prepareLists'
import '../../support/authenticateUser'
describe('List View Kanban', () => {
let buckets
createFakeUserAndLogin()
prepareLists()
let buckets
beforeEach(() => {
buckets = BucketFactory.create(2)
})
@ -38,7 +39,7 @@ describe('List View Kanban', () => {
})
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket')
cy.get('.kanban .bucket')
.contains(buckets[0].title)
.get('.bucket-footer .button')
.contains('Add another task')
@ -70,7 +71,7 @@ describe('List View Kanban', () => {
it('Can set a bucket limit', () => {
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
.first()
.click()
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item')
@ -91,7 +92,7 @@ describe('List View Kanban', () => {
it('Can rename a bucket', () => {
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .bucket-header .title')
cy.get('.kanban .bucket .bucket-header .title')
.first()
.type('{selectall}New Bucket Title{enter}')
cy.get('.kanban .bucket .bucket-header .title')
@ -102,7 +103,7 @@ describe('List View Kanban', () => {
it('Can delete a bucket', () => {
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
.first()
.click()
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item')
@ -129,7 +130,7 @@ describe('List View Kanban', () => {
})
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .tasks .task')
cy.get('.kanban .bucket .tasks .task')
.contains(tasks[0].title)
.first()
.drag('.kanban .bucket:nth-child(2) .tasks')
@ -148,7 +149,7 @@ describe('List View Kanban', () => {
})
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .tasks .task')
cy.get('.kanban .bucket .tasks .task')
.contains(tasks[0].title)
.should('be.visible')
.click()
@ -170,7 +171,7 @@ describe('List View Kanban', () => {
const task = tasks[0]
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .tasks .task')
cy.get('.kanban .bucket .tasks .task')
.contains(task.title)
.should('be.visible')
.click()
@ -217,7 +218,7 @@ describe('List View Kanban', () => {
const task = tasks[0]
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .tasks .task')
cy.get('.kanban .bucket .tasks .task')
.contains(task.title)
.should('be.visible')
.click()
@ -234,7 +235,7 @@ describe('List View Kanban', () => {
cy.get('.global-notification')
.should('contain', 'Success')
cy.getSettled('.kanban .bucket .tasks')
cy.get('.kanban .bucket .tasks')
.should('not.contain', task.title)
})
})

View File

@ -1,12 +1,13 @@
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {UserListFactory} from '../../factories/users_list'
import {TaskFactory} from '../../factories/task'
import {UserFactory} from '../../factories/user'
import {ListFactory} from '../../factories/list'
import {prepareLists} from './prepareLists'
import '../../support/authenticateUser'
describe('List View List', () => {
createFakeUserAndLogin()
prepareLists()
it('Should be an empty list', () => {

View File

@ -1,8 +1,10 @@
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {TaskFactory} from '../../factories/task'
import '../../support/authenticateUser'
describe('List View Table', () => {
createFakeUserAndLogin()
it('Should show a table with tasks', () => {
const tasks = TaskFactory.create(1)
cy.visit('/lists/1/table')

View File

@ -1,9 +1,11 @@
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {TaskFactory} from '../../factories/task'
import {prepareLists} from './prepareLists'
import '../../support/authenticateUser'
describe('Lists', () => {
createFakeUserAndLogin()
let lists
prepareLists((newLists) => (lists = newLists))

View File

@ -1,14 +1,14 @@
import {UserFactory} from '../../factories/user'
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import '../../support/authenticateUser'
import {ListFactory} from '../../factories/list'
import {NamespaceFactory} from '../../factories/namespace'
describe('Namepaces', () => {
createFakeUserAndLogin()
let namespaces
beforeEach(() => {
UserFactory.create(1)
namespaces = NamespaceFactory.create(1)
ListFactory.create(1)
})

View File

@ -1,10 +1,8 @@
import {ListFactory} from '../../factories/list'
import {UserFactory} from '../../factories/user'
import {NamespaceFactory} from '../../factories/namespace'
import {TaskFactory} from '../../factories/task'
export function createLists() {
UserFactory.create(1)
NamespaceFactory.create(1)
const lists = ListFactory.create(1, {
title: 'First List'

View File

@ -1,14 +1,16 @@
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {TaskFactory} from '../../factories/task'
import {ListFactory} from '../../factories/list'
import {NamespaceFactory} from '../../factories/namespace'
import {UserListFactory} from '../../factories/users_list'
import '../../support/authenticateUser'
describe('Editor', () => {
createFakeUserAndLogin()
beforeEach(() => {
NamespaceFactory.create(1)
const lists = ListFactory.create(1)
ListFactory.create(1)
TaskFactory.truncate()
UserListFactory.truncate()
})

View File

@ -1,6 +1,12 @@
import '../../support/authenticateUser'
import {createFakeUserAndLogin} from '../../support/authenticateUser'
describe('The Menu', () => {
createFakeUserAndLogin()
beforeEach(() => {
cy.visit('/')
})
it('Is visible by default on desktop', () => {
cy.get('.namespace-container')
.should('have.class', 'is-active')

View File

@ -1,9 +1,12 @@
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {TeamFactory} from '../../factories/team'
import {TeamMemberFactory} from '../../factories/team_member'
import {UserFactory} from '../../factories/user'
import '../../support/authenticateUser'
describe('Team', () => {
createFakeUserAndLogin()
it('Creates a new team', () => {
TeamFactory.truncate()
cy.visit('/teams')

View File

@ -1,16 +1,13 @@
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {ListFactory} from '../../factories/list'
import {seed} from '../../support/seed'
import {TaskFactory} from '../../factories/task'
import {formatISO} from 'date-fns'
import {UserFactory} from '../../factories/user'
import {NamespaceFactory} from '../../factories/namespace'
import {BucketFactory} from '../../factories/bucket'
import {updateUserSettings} from '../../support/updateUserSettings'
import '../../support/authenticateUser'
function seedTasks(numberOfTasks = 100, startDueDate = new Date()) {
UserFactory.create(1)
function seedTasks(numberOfTasks = 50, startDueDate = new Date()) {
NamespaceFactory.create(1)
const list = ListFactory.create()[0]
BucketFactory.create(1, {
@ -20,7 +17,7 @@ function seedTasks(numberOfTasks = 100, startDueDate = new Date()) {
let dueDate = startDueDate
for (let i = 0; i < numberOfTasks; i++) {
const now = new Date()
dueDate = (new Date(dueDate.valueOf())).setDate((new Date(dueDate.valueOf())).getDate() + 2)
dueDate = new Date(new Date(dueDate).setDate(dueDate.getDate() + 2))
tasks.push({
id: i + 1,
list_id: list.id,
@ -28,9 +25,9 @@ function seedTasks(numberOfTasks = 100, startDueDate = new Date()) {
created_by_id: 1,
title: 'Test Task ' + i,
index: i + 1,
due_date: formatISO(dueDate),
created: formatISO(now),
updated: formatISO(now),
due_date: dueDate.toISOString(),
created: now.toISOString(),
updated: now.toISOString(),
})
}
seed(TaskFactory.table, tasks)
@ -38,8 +35,11 @@ function seedTasks(numberOfTasks = 100, startDueDate = new Date()) {
}
describe('Home Page Task Overview', () => {
createFakeUserAndLogin()
it('Should show tasks with a near due date first on the home page overview', () => {
const {tasks} = seedTasks()
const taskCount = 50
const {tasks} = seedTasks(taskCount)
cy.visit('/')
cy.get('[data-cy="showTasks"] .card .task')
@ -49,8 +49,10 @@ describe('Home Page Task Overview', () => {
})
it('Should show overdue tasks first, then show other tasks', () => {
const oldDate = (new Date()).setDate((new Date()).getDate() - 14)
const {tasks} = seedTasks(100, oldDate)
const now = new Date()
const oldDate = new Date(new Date(now).setDate(now.getDate() - 14))
const taskCount = 50
const {tasks} = seedTasks(taskCount, oldDate)
cy.visit('/')
cy.get('[data-cy="showTasks"] .card .task')
@ -68,7 +70,7 @@ describe('Home Page Task Overview', () => {
TaskFactory.create(1, {
id: 999,
title: newTaskTitle,
due_date: formatISO(new Date()),
due_date: new Date().toISOString(),
}, false)
cy.visit(`/lists/${tasks[0].list_id}/list`)
@ -83,7 +85,7 @@ describe('Home Page Task Overview', () => {
it('Should not show a new task without a date at the bottom when there are > 50 tasks', () => {
// We're not using the api here to create the task in order to verify the flow
const {tasks} = seedTasks()
const {tasks} = seedTasks(100)
const newTaskTitle = 'New Task'
cy.visit('/')

View File

@ -1,4 +1,4 @@
import {formatISO} from 'date-fns'
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {TaskFactory} from '../../factories/task'
import {ListFactory} from '../../factories/list'
@ -11,7 +11,6 @@ import {LabelFactory} from '../../factories/labels'
import {LabelTaskFactory} from '../../factories/label_task'
import {BucketFactory} from '../../factories/bucket'
import '../../support/authenticateUser'
import {TaskAttachmentFactory} from '../../factories/task_attachments'
function addLabelToTaskAndVerify(labelTitle: string) {
@ -46,12 +45,14 @@ function uploadAttachmentAndVerify(taskId: number) {
}
describe('Task', () => {
createFakeUserAndLogin()
let namespaces
let lists
let buckets
beforeEach(() => {
UserFactory.create(1)
// UserFactory.create(1)
namespaces = NamespaceFactory.create(1)
lists = ListFactory.create(1)
buckets = BucketFactory.create(1, {
@ -145,7 +146,7 @@ describe('Task', () => {
id: 1,
index: 1,
done: true,
done_at: formatISO(new Date())
done_at: new Date().toISOString()
})
cy.visit(`/tasks/${tasks[0].id}`)
@ -421,10 +422,10 @@ describe('Task', () => {
cy.visit(`/tasks/${tasks[0].id}`)
cy.getSettled('.task-view .details.labels-list .multiselect .input-wrapper')
cy.get('.task-view .details.labels-list .multiselect .input-wrapper')
.should('be.visible')
.should('contain', labels[0].title)
cy.getSettled('.task-view .details.labels-list .multiselect .input-wrapper')
cy.get('.task-view .details.labels-list .multiselect .input-wrapper')
.children()
.first()
.get('[data-cy="taskDetail.removeLabel"]')

11
cypress/e2e/tsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"extends": "@vue/tsconfig/tsconfig.web.json",
"include": ["./**/*", "../support/**/*", "../factories/**/*"],
"compilerOptions": {
"baseUrl": ".",
"isolatedModules": false,
"target": "ES2015",
"lib": ["ESNext", "dom"],
"types": ["cypress"]
}
}

View File

@ -11,16 +11,11 @@ const testAndAssertFailed = fixture => {
cy.get('div.message.danger').contains('Wrong username or password.')
}
const username = 'test'
context('Login', () => {
beforeEach(() => {
UserFactory.create(1, {
username: 'test',
})
cy.visit('/', {
onBeforeLoad(win) {
win.localStorage.removeItem('token')
},
})
UserFactory.create(1, {username})
})
it('Should log in with the right credentials', () => {

View File

@ -1,4 +1,4 @@
import '../../support/authenticateUser'
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {createLists} from '../list/prepareLists'
function logout() {
@ -10,6 +10,8 @@ function logout() {
}
describe('Log out', () => {
createFakeUserAndLogin()
it('Logs the user out', () => {
cy.visit('/')

View File

@ -1,11 +1,7 @@
import {UserFactory} from '../../factories/user'
import '../../support/authenticateUser'
import {createFakeUserAndLogin} from '../../support/authenticateUser'
describe('User Settings', () => {
beforeEach(() => {
UserFactory.create(1)
})
createFakeUserAndLogin()
it('Changes the user avatar', () => {
cy.intercept(`${Cypress.env('API_URL')}/user/settings/avatar/upload`).as('uploadAvatar')

View File

@ -1,6 +1,5 @@
import {faker} from '@faker-js/faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
export class BucketFactory extends Factory {
static table = 'buckets'
@ -13,8 +12,8 @@ export class BucketFactory extends Factory {
title: faker.lorem.words(3),
list_id: 1,
created_by_id: 1,
created: formatISO(now),
updated: formatISO(now)
created: now.toISOString(),
updated: now.toISOString(),
}
}
}

View File

@ -1,5 +1,4 @@
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
export class LabelTaskFactory extends Factory {
static table = 'label_tasks'
@ -11,7 +10,7 @@ export class LabelTaskFactory extends Factory {
id: '{increment}',
task_id: 1,
label_id: 1,
created: formatISO(now),
created: now.toISOString(),
}
}
}

View File

@ -1,7 +1,6 @@
import {faker} from '@faker-js/faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
export class LabelFactory extends Factory {
static table = 'labels'
@ -15,8 +14,8 @@ export class LabelFactory extends Factory {
description: faker.lorem.text(10),
hex_color: (Math.random()*0xFFFFFF<<0).toString(16), // random 6-digit hex number
created_by_id: 1,
created: formatISO(now),
updated: formatISO(now),
created: now.toISOString(),
updated: now.toISOString(),
}
}
}

View File

@ -1,5 +1,4 @@
import {Factory} from '../support/factory'
import {formatISO} from "date-fns"
import {faker} from '@faker-js/faker'
export class LinkShareFactory extends Factory {
@ -15,8 +14,8 @@ export class LinkShareFactory extends Factory {
right: 0,
sharing_type: 0,
shared_by_id: 1,
created: formatISO(now),
updated: formatISO(now)
created: now.toISOString(),
updated: now.toISOString(),
}
}
}

View File

@ -1,5 +1,4 @@
import {Factory} from '../support/factory'
import {formatISO} from "date-fns"
import {faker} from '@faker-js/faker'
export class ListFactory extends Factory {
@ -13,8 +12,8 @@ export class ListFactory extends Factory {
title: faker.lorem.words(3),
owner_id: 1,
namespace_id: 1,
created: formatISO(now),
updated: formatISO(now)
created: now.toISOString(),
updated: now.toISOString(),
}
}
}

View File

@ -1,6 +1,5 @@
import {faker} from '@faker-js/faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
export class NamespaceFactory extends Factory {
static table = 'namespaces'
@ -12,8 +11,8 @@ export class NamespaceFactory extends Factory {
id: '{increment}',
title: faker.lorem.words(3),
owner_id: 1,
created: formatISO(now),
updated: formatISO(now)
created: now.toISOString(),
updated: now.toISOString(),
}
}
}

View File

@ -1,6 +1,5 @@
import {faker} from '@faker-js/faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
export class TaskFactory extends Factory {
static table = 'tasks'
@ -16,8 +15,8 @@ export class TaskFactory extends Factory {
created_by_id: 1,
index: '{increment}',
position: '{increment}',
created: formatISO(now),
updated: formatISO(now)
created: now.toISOString(),
updated: now.toISOString()
}
}
}

View File

@ -1,5 +1,4 @@
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
export class TaskAssigneeFactory extends Factory {
static table = 'task_assignees'
@ -11,7 +10,7 @@ export class TaskAssigneeFactory extends Factory {
id: '{increment}',
task_id: 1,
user_id: 1,
created: formatISO(now),
created: now.toISOString(),
}
}
}

View File

@ -1,5 +1,4 @@
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
export class TaskAttachmentFactory extends Factory {
static table = 'task_attachments'
@ -11,7 +10,7 @@ export class TaskAttachmentFactory extends Factory {
id: '{increment}',
task_id: 1,
file_id: 1,
created: formatISO(now),
created: now.toISOString(),
}
}
}

View File

@ -1,7 +1,6 @@
import {faker} from '@faker-js/faker'
import {Factory} from '../support/factory'
import {formatISO} from "date-fns"
export class TaskCommentFactory extends Factory {
static table = 'task_comments'
@ -14,8 +13,8 @@ export class TaskCommentFactory extends Factory {
comment: faker.lorem.text(3),
author_id: 1,
task_id: 1,
created: formatISO(now),
updated: formatISO(now)
created: now.toISOString(),
updated: now.toISOString()
}
}
}

View File

@ -1,6 +1,5 @@
import {faker} from '@faker-js/faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
export class TeamFactory extends Factory {
static table = 'teams'
@ -11,8 +10,8 @@ export class TeamFactory extends Factory {
return {
name: faker.lorem.words(3),
created_by_id: 1,
created: formatISO(now),
updated: formatISO(now)
created: now.toISOString(),
updated: now.toISOString(),
}
}
}

View File

@ -1,5 +1,4 @@
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
export class TeamMemberFactory extends Factory {
static table = 'team_members'
@ -9,7 +8,7 @@ export class TeamMemberFactory extends Factory {
team_id: 1,
user_id: 1,
admin: false,
created: formatISO(new Date()),
created: new Date().toISOString(),
}
}
}

View File

@ -1,7 +1,6 @@
import {faker} from '@faker-js/faker'
import {Factory} from '../support/factory'
import {formatISO} from "date-fns"
export class UserFactory extends Factory {
static table = 'users'
@ -15,8 +14,8 @@ export class UserFactory extends Factory {
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.', // 1234
status: 0,
issuer: 'local',
created: formatISO(now),
updated: formatISO(now)
created: now.toISOString(),
updated: now.toISOString(),
}
}
}

View File

@ -1,5 +1,4 @@
import {Factory} from '../support/factory'
import {formatISO} from "date-fns"
export class UserListFactory extends Factory {
static table = 'users_lists'
@ -12,8 +11,8 @@ export class UserListFactory extends Factory {
list_id: 1,
user_id: 1,
right: 0,
created: formatISO(now),
updated: formatISO(now)
created: now.toISOString(),
updated: now.toISOString(),
}
}
}

View File

@ -4,26 +4,32 @@
import {UserFactory} from '../factories/user'
let token
before(() => {
const users = UserFactory.create(1)
cy.request('POST', `${Cypress.env('API_URL')}/login`, {
username: users[0].username,
password: '1234',
})
.its('body')
.then(r => {
token = r.token
export function login(user, cacheAcrossSpecs = false) {
if (!user) {
throw new Error('Needs user')
}
// Caching session when logging in via page visit
cy.session(`user__${user.username}`, () => {
cy.request('POST', `${Cypress.env('API_URL')}/login`, {
username: user.username,
password: '1234',
}).then(({ body }) => {
window.localStorage.setItem('token', body.token)
})
})
}, {
cacheAcrossSpecs,
})
}
beforeEach(() => {
cy.log(`Using token ${token} to make authenticated requests`)
cy.visit('/', {
onBeforeLoad(win) {
win.localStorage.setItem('token', token)
},
export function createFakeUserAndLogin() {
let user
before(() => {
user = UserFactory.create(1)[0]
})
})
beforeEach(() => {
login(user, true)
})
return user
}

View File

@ -34,38 +34,4 @@
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }
/**
* Recursively gets an element, returning only after it's determined to be attached to the DOM for good.
*
* Source: https://github.com/cypress-io/cypress/issues/7306#issuecomment-850621378
*/
Cypress.Commands.add('getSettled', (selector, opts = {}) => {
const retries = opts.retries || 3
const delay = opts.delay || 100
const isAttached = (resolve, count = 0) => {
const el = Cypress.$(selector)
// is element attached to the DOM?
count = Cypress.dom.isAttached(el) ? count + 1 : 0
// hit our base case, return the element
if (count >= retries) {
return resolve(el)
}
// retry after a bit of a delay
setTimeout(() => isAttached(resolve, count), delay)
}
// wrap, so we can chain cypress commands off the result
return cy.wrap(null).then(() => {
return new Cypress.Promise((resolve) => {
return isAttached(resolve, 0)
}).then((el) => {
return cy.wrap(el)
})
})
})
// }

View File

@ -1,10 +0,0 @@
{
"extends": "@vue/tsconfig/tsconfig.web.json",
"include": ["./integration/**/*", "./support/**/*"],
"compilerOptions": {
"isolatedModules": false,
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress"]
}
}

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'

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 { }
}

9
env.config.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
declare module 'postcss-easings' {
import postcssEasings from 'postcss-easings'
export default postcssEasings
}
declare module 'postcss-easing-gradients' {
import postcssEasingGradients from 'postcss-easing-gradients'
export default postcssEasingGradients
}

11
env.d.ts vendored
View File

@ -1,3 +1,12 @@
/// <reference types="vite/client" />
/// <reference types="vite-svg-loader" />
/// <reference types="cypress" />
/// <reference types="cypress" />
/// <reference types="@histoire/plugin-vue/components" />
interface ImportMetaEnv {
readonly VITE_IS_ONLINE: boolean
}
interface ImportMeta {
readonly env: ImportMetaEnv
}

34
histoire.config.ts Normal file
View File

@ -0,0 +1,34 @@
import {defineConfig, defaultColors} from 'histoire'
import {HstVue} from '@histoire/plugin-vue'
import {HstScreenshot} from '@histoire/plugin-screenshot'
export default defineConfig({
setupFile: './src/histoire.setup.ts',
storyIgnored: [
'**/node_modules/**',
'**/dist/**',
// see https://kolaente.dev/vikunja/frontend/pulls/2724#issuecomment-42012
'**/.direnv/**',
],
plugins: [
HstVue(),
HstScreenshot({
// Options here
}),
],
theme: {
title: 'Vikunja',
colors: {
// https://histoire.dev/guide/config.html#builtin-colors
gray: defaultColors.zinc,
primary: defaultColors.cyan,
},
// logo: {
// square: './img/square.png',
// light: './img/light.png',
// dark: './img/dark.png',
// },
// logoHref: 'https://acme.com',
// favicon: './favicon.ico',
},
})

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

@ -1,66 +1,91 @@
{
"name": "vikunja-frontend",
"version": "0.10.0",
"description": "The todo app to organize your life.",
"private": true,
"version": "0.10.0",
"license": "AGPL-3.0-or-later",
"repository": {
"type": "git",
"url": "https://kolaente.dev/vikunja/frontend"
},
"bugs": {
"url": "https://kolaente.dev/vikunja/frontend/issues"
},
"homepage": "https://vikunja.io/",
"funding": "https://opencollective.com/vikunja",
"packageManager": "pnpm@7.26.2",
"keywords": [
"todo",
"productivity",
"task management",
"organisation",
"gantt",
"kanban"
],
"scripts": {
"serve": "vite",
"serve:dist": "vite preview --port 4173",
"serve:dist:dev": "vite preview --mode development --port 4173",
"preview": "vite preview --port 4173",
"preview:dev": "vite preview --outDir dist-dev --mode development --port 4173",
"build": "vite build && workbox copyLibraries dist/",
"build:modern-only": "BUILD_MODERN_ONLY=true vite build && workbox copyLibraries dist/",
"build:dev": "vite build --mode development --outDir dist-dev/",
"lint": "eslint --ignore-pattern '*.test.*' ./src --ext .vue,.js,.ts",
"cypress:open": "cypress open",
"test:unit": "vitest --run",
"test:unit-watch": "vitest watch",
"test:frontend": "cypress run",
"test:e2e": "start-server-and-test preview http://127.0.0.1:4173 'cypress run --e2e --browser chrome'",
"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 --dir ./src",
"typecheck": "vue-tsc --noEmit && vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
"browserslist:update": "npx browserslist@latest --update-db",
"fonts:update": "pnpm run fonts:download && pnpm run fonts:subset",
"browserslist:update": "pnpm dlx browserslist@latest --update-db",
"fonts:update": "pnpm fonts:download && pnpm fonts:subset",
"fonts:download": "./scripts/fonts-download.sh",
"fonts:subset": "./scripts/fonts-subset.sh"
"fonts:subset": "./scripts/fonts-subset.sh",
"story:dev": "histoire dev",
"story:build": "histoire build",
"story:preview": "histoire preview"
},
"dependencies": {
"@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.7.0",
"@sentry/tracing": "7.28.1",
"@sentry/vue": "7.28.1",
"@kyvg/vue3-notification": "2.8.0",
"@sentry/tracing": "7.34.0",
"@sentry/vue": "7.34.0",
"@types/is-touch-device": "1.0.0",
"@types/lodash.clonedeep": "4.5.7",
"@types/sortablejs": "1.15.0",
"@vueuse/core": "9.9.0",
"axios": "0.27.2",
"@vueuse/core": "9.12.0",
"axios": "1.2.6",
"blurhash": "2.0.4",
"bulma-css-variables": "0.9.33",
"camel-case": "4.1.2",
"codemirror": "5.65.11",
"date-fns": "2.29.3",
"dayjs": "1.11.7",
"dompurify": "2.4.1",
"dompurify": "2.4.3",
"easymde": "2.18.0",
"fast-deep-equal": "3.1.3",
"flatpickr": "4.6.13",
"flexsearch": "0.7.21",
"floating-vue": "2.0.0-beta.20",
"focus-within": "3.0.2",
"highlight.js": "11.7.0",
"is-touch-device": "1.0.1",
"lodash.clonedeep": "4.5.0",
"lodash.debounce": "4.0.8",
"marked": "4.2.5",
"marked": "4.2.12",
"minimist": "1.2.7",
"pinia": "2.0.28",
"pinia": "2.0.29",
"register-service-worker": "1.7.2",
"snake-case": "3.0.4",
"sortablejs": "1.15.0",
"ufo": "1.0.1",
"vue": "3.2.45",
"vue-advanced-cropper": "2.8.6",
"vue-advanced-cropper": "2.8.8",
"vue-flatpickr-component": "11.0.1",
"vue-i18n": "9.2.2",
"vue-router": "4.1.6",
@ -72,45 +97,51 @@
"@cypress/vite-dev-server": "5.0.2",
"@cypress/vue": "5.0.3",
"@faker-js/faker": "7.6.0",
"@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",
"@types/lodash.debounce": "4.0.7",
"@types/marked": "4.0.8",
"@types/node": "18.11.18",
"@types/postcss-preset-env": "7.7.0",
"@typescript-eslint/eslint-plugin": "5.48.0",
"@typescript-eslint/parser": "5.48.0",
"@vitejs/plugin-legacy": "3.0.1",
"@typescript-eslint/eslint-plugin": "5.49.0",
"@typescript-eslint/parser": "5.49.0",
"@vitejs/plugin-legacy": "3.0.2",
"@vitejs/plugin-vue": "4.0.0",
"@vue/eslint-config-typescript": "11.0.2",
"@vue/test-utils": "2.2.6",
"@vue/test-utils": "2.2.8",
"@vue/tsconfig": "0.1.3",
"autoprefixer": "10.4.13",
"browserslist": "4.21.4",
"caniuse-lite": "1.0.30001441",
"caniuse-lite": "1.0.30001449",
"csstype": "3.1.1",
"cypress": "12.2.0",
"esbuild": "0.16.13",
"eslint": "8.31.0",
"eslint-plugin-vue": "9.8.0",
"happy-dom": "8.1.1",
"netlify-cli": "12.5.0",
"postcss": "8.4.20",
"cypress": "12.4.1",
"esbuild": "0.17.5",
"eslint": "8.33.0",
"eslint-plugin-vue": "9.9.0",
"happy-dom": "8.2.0",
"histoire": "0.12.4",
"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.9.1",
"rollup": "3.12.0",
"rollup-plugin-visualizer": "5.9.0",
"sass": "1.57.1",
"start-server-and-test": "1.15.3",
"typescript": "4.9.4",
"vite": "4.0.3",
"vite": "4.0.4",
"vite-plugin-inject-preload": "1.2.0",
"vite-plugin-pwa": "0.14.1",
"vite-svg-loader": "4.0.0",
"vitest": "0.26.3",
"vue-tsc": "1.0.19",
"vitest": "0.28.3",
"vue-tsc": "1.0.24",
"wait-on": "7.0.1",
"workbox-cli": "6.5.4"
},
"license": "AGPL-3.0-or-later",
"packageManager": "pnpm@7.21.0"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
05c69e5323a4d4bac041ade830735becd52c230277396d1f72be8fde83683a75dc095f6678804083b2ca66f27cc7995f ./scripts/deploy-preview-netlify.js

View File

@ -1,14 +1,18 @@
const { exec } = require('child_process')
import { exec } from 'node:child_process'
function createSlug(string) {
return String(string)
.trim()
.normalize('NFKD')
.toLowerCase()
.replace(/[.\s/]/g, '-')
.replace(/[^A-Za-z\d-]/g, '')
}
const BOT_USER_ID = 513
const giteaToken = process.env.GITEA_TOKEN
const siteId = process.env.NETLIFY_SITE_ID
const branchSlug = String(process.env.DRONE_SOURCE_BRANCH)
.trim()
.normalize('NFKD')
.toLowerCase()
.replace(/[.\s/]/g, '-')
.replace(/[^A-Za-z\d-]/g, '')
const branchSlug = createSlug(process.env.DRONE_SOURCE_BRANCH)
const prNumber = process.env.DRONE_PULL_REQUEST
const prIssueCommentsUrl = `https://kolaente.dev/api/v1/repos/vikunja/frontend/issues/${prNumber}/comments`

View File

@ -0,0 +1 @@
57af69409e66bc87f4f2fc5822dd8d3c2eb47c601f81af1ac4a56f3e2d80837b1a2de06f4ff57695ec379b7c15b881e3 ./scripts/deploy-preview-netlify.mjs

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

@ -0,0 +1,58 @@
<script lang="ts" setup>
import {logEvent} from 'histoire/client'
import {reactive} from 'vue'
import {createRouter, createMemoryHistory} from 'vue-router'
import BaseButton from './BaseButton.vue'
function setupApp({ app }) {
// Router mock
app.use(createRouter({
history: createMemoryHistory(),
routes: [
{ path: '/', name: 'home', component: { render: () => null } },
],
}))
}
const state = reactive({
disabled: false,
})
</script>
<template>
<Story :setup-app="setupApp" :layout="{ type: 'grid', width: '200px' }">
<Variant title="custom">
<template #controls>
<HstCheckbox v-model="state.disabled" title="Disabled" />
</template>
<BaseButton :disabled="state.disabled">
Hello!
</BaseButton>
</Variant>
<Variant title="disabled">
<BaseButton disabled>
Hello!
</BaseButton>
</Variant>
<Variant title="router link">
<BaseButton :to="'home'">
Hello!
</BaseButton>
</Variant>
<Variant title="external link">
<BaseButton href="https://vikunja.io">
Hello!
</BaseButton>
</Variant>
<Variant title="button">
<BaseButton @click="logEvent('Click', $event)">
Hello!
</BaseButton>
</Variant>
</Story>
</template>

View File

@ -61,12 +61,12 @@ export type BaseButtonTypes = typeof BASE_BUTTON_TYPES_MAP[keyof typeof BASE_BUT
import {unrefElement} from '@vueuse/core'
import {ref, type HTMLAttributes} from 'vue'
import type {RouteLocationNamedRaw} from 'vue-router'
import type {RouteLocationRaw} from 'vue-router'
export interface BaseButtonProps extends HTMLAttributes {
type?: BaseButtonTypes
disabled?: boolean
to?: RouteLocationNamedRaw
to?: RouteLocationRaw
href?: string
}

View File

@ -0,0 +1,179 @@
<template>
<transition
name="expandable-slide"
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
>
<div
v-if="initialHeight"
class="expandable-initial-height"
:style="{ maxHeight: `${initialHeight}px` }"
:class="{ 'expandable-initial-height--expanded': open }"
>
<slot />
</div>
<div v-else-if="open" class="expandable">
<slot />
</div>
</transition>
</template>
<script setup lang="ts">
// the logic of this component is loosly based on this article
// https://gomakethings.com/how-to-add-transition-animations-to-vanilla-javascript-show-and-hide-methods/#putting-it-all-together
import {computed, ref} from 'vue'
import {getInheritedBackgroundColor} from '@/helpers/getInheritedBackgroundColor'
const props = defineProps({
/** Wheather the Expandable is open or not */
open: {
type: Boolean,
default: false,
},
/** If there is too much content, content will be cut of here. */
initialHeight: {
type: Number,
default: undefined,
},
/** The hidden content is indicated by a gradient. This is the color that the gradient fades to.
* Makes only sense if `initialHeight` is set. */
backgroundColor: {
type: String,
},
})
const wrapper = ref<HTMLElement | null>(null)
const computedBackgroundColor = computed(() => {
if (wrapper.value === null) {
return props.backgroundColor || '#fff'
}
return props.backgroundColor || getInheritedBackgroundColor(wrapper.value)
})
/**
* Get the natural height of the element
*/
function getHeight(el: HTMLElement) {
const { display } = el.style // save display property
el.style.display = 'block' // Make it visible
const height = `${el.scrollHeight}px` // Get its height
el.style.display = display // revert to original display property
return height
}
/**
* force layout of element changes
* https://gist.github.com/paulirish/5d52fb081b3570c81e3a
*/
function forceLayout(el: HTMLElement) {
el.offsetTop
}
/* ######################################################################
# The following functions are called by the js hooks of the transitions.
# They follow the orignal hook order of the vue transition component
# see: https://vuejs.org/guide/built-ins/transition.html#javascript-hooks
###################################################################### */
function beforeEnter(el: HTMLElement) {
el.style.height = '0'
el.style.willChange = 'height'
el.style.backfaceVisibility = 'hidden'
forceLayout(el)
}
// the done callback is optional when
// used in combination with CSS
function enter(el: HTMLElement) {
const height = getHeight(el) // Get the natural height
el.style.height = height // Update the height
}
function afterEnter(el: HTMLElement) {
removeHeight(el)
}
function enterCancelled(el: HTMLElement) {
removeHeight(el)
}
function beforeLeave(el: HTMLElement) {
// Give the element a height to change from
el.style.height = `${el.scrollHeight}px`
forceLayout(el)
}
function leave(el: HTMLElement) {
// Set the height back to 0
el.style.height = '0'
el.style.willChange = ''
el.style.backfaceVisibility = ''
}
function afterLeave(el: HTMLElement) {
removeHeight(el)
}
function leaveCancelled(el: HTMLElement) {
removeHeight(el)
}
function removeHeight(el: HTMLElement) {
el.style.height = ''
}
</script>
<style lang="scss" scoped>
$transition-time: 300ms;
.expandable-slide-enter-active,
.expandable-slide-leave-active {
transition:
opacity $transition-time ease-in-quint,
height $transition-time ease-in-out-quint;
overflow: hidden;
}
.expandable-slide-enter,
.expandable-slide-leave-to {
opacity: 0;
}
.expandable-initial-height {
padding: 5px;
margin: -5px;
overflow: hidden;
position: relative;
&::after {
content: "";
display: block;
background-image: linear-gradient(
to bottom,
transparent,
ease-in-out
v-bind(computedBackgroundColor)
);
position: absolute;
height: 40px;
width: 100%;
bottom: 0;
}
}
.expandable-initial-height--expanded {
height: 100% !important;
&::after {
display: none;
}
}
</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);
}
}

View File

@ -0,0 +1,26 @@
<script lang="ts" setup>
import {logEvent} from 'histoire/client'
import XButton from './button.vue'
</script>
<template>
<Story :layout="{ type: 'grid', width: '200px' }">
<Variant title="primary">
<XButton @click="logEvent('Click', $event)" variant="primary">
Order pizza!
</XButton>
</Variant>
<Variant title="secondary">
<XButton @click="logEvent('Click', $event)" variant="secondary">
Order spaghetti!
</XButton>
</Variant>
<Variant title="tertiary">
<XButton @click="logEvent('Click', $event)" variant="tertiary">
Order tortellini!
</XButton>
</Variant>
</Story>
</template>

View File

@ -0,0 +1,14 @@
<script lang="ts" setup>
import {reactive} from 'vue'
import ColorPicker from './ColorPicker.vue'
const state = reactive({
color: '#f2f2f2',
})
</script>
<template>
<Story :layout="{ type: 'grid', width: '200px' }">
<ColorPicker v-model="state.color" />
</Story>
</template>

View File

@ -37,6 +37,7 @@
<script setup lang="ts">
import {computed, ref, toRef, watch} from 'vue'
import {createRandomID} from '@/helpers/randomId'
import XButton from '@/components/input/button.vue'
const DEFAULT_COLORS = [
'#1973ff',

View File

@ -0,0 +1,11 @@
<script lang="ts" setup>
import Card from './card.vue'
</script>
<template>
<Story :layout="{ type: 'grid', width: '200px' }">
<Card>
Card content
</Card>
</Story>
</template>

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

@ -1,9 +1,8 @@
<template>
<div class="task-add">
<div class="field is-grouped">
<div class="task-add" ref="taskAdd">
<div class="add-task__field field is-grouped">
<p class="control has-icons-left is-expanded">
<textarea
:disabled="loading || undefined"
class="add-task-textarea input"
:class="{'textarea-empty': newTaskTitle === ''}"
:placeholder="$t('list.list.addPlaceholder')"
@ -33,27 +32,34 @@
</x-button>
</p>
</div>
<p class="help is-danger" v-if="errorMessage !== ''">
{{ errorMessage }}
</p>
<quick-add-magic v-else/>
<Expandable :open="errorMessage !== '' || taskAddFocused || taskAddHovered && debouncedTaskAddHovered">
<p class="pt-3 mt-0 help is-danger" v-if="errorMessage !== ''">
{{ errorMessage }}
</p>
<quick-add-magic v-else class="quick-add-magic" />
</Expandable>
</div>
</template>
<script setup lang="ts">
import {computed, ref} from 'vue'
import {useI18n} from 'vue-i18n'
import {refDebounced, useElementHover, useFocusWithin} from '@vueuse/core'
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue'
import {RELATION_KIND} from '@/types/IRelationKind'
import type {ITask} from '@/modelTypes/ITask'
import Expandable from '@/components/base/Expandable.vue'
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue'
import {parseSubtasksViaIndention} from '@/helpers/parseSubtasksViaIndention'
import TaskRelationService from '@/services/taskRelation'
import TaskRelationModel from '@/models/taskRelation'
import {RELATION_KIND} from '@/types/IRelationKind'
import {getLabelsFromPrefix} from '@/modules/parseTaskText'
import {useAuthStore} from '@/stores/auth'
import {useTaskStore} from '@/stores/tasks'
import {useAutoHeightTextarea} from '@/composables/useAutoHeightTextarea'
import {getLabelsFromPrefix} from '@/modules/parseTaskText'
const props = defineProps({
defaultPosition: {
@ -71,9 +77,24 @@ const {t} = useI18n({useScope: 'global'})
const authStore = useAuthStore()
const taskStore = useTaskStore()
const taskAdd = ref<HTMLTextAreaElement | null>(null)
// enable only if we don't have a modal
// onStartTyping(() => {
// if (newTaskInput.value === null || document.activeElement === newTaskInput.value) {
// return
// }
// newTaskInput.value.focus()
// })
const { focused: taskAddFocused } = useFocusWithin(taskAdd)
const taskAddHovered = useElementHover(taskAdd)
const debouncedTaskAddHovered = refDebounced(taskAddHovered, 500)
const errorMessage = ref('')
function resetEmptyTitleError(e) {
function resetEmptyTitleError(e: KeyboardEvent) {
if (
(e.which <= 90 && e.which >= 48 || e.which >= 96 && e.which <= 105)
&& newTaskTitle.value !== ''
@ -192,7 +213,9 @@ defineExpose({
</script>
<style lang="scss" scoped>
.task-add {
.task-add,
// overwrite bulma styles
.task-add .add-task__field {
margin-bottom: 0;
}
@ -220,4 +243,8 @@ defineExpose({
white-space: nowrap;
text-overflow: ellipsis;
}
.quick-add-magic {
padding-top: 0.75rem;
}
</style>

View File

@ -31,7 +31,7 @@
</template>
<script setup lang="ts">
import {ref,computed, watch, type PropType} from 'vue'
import {ref, computed, watch, type PropType} from 'vue'
import CustomTransition from '@/components/misc/CustomTransition.vue'
import Editor from '@/components/input/AsyncEditor'
@ -67,7 +67,7 @@ const taskStore = useTaskStore()
const loading = computed(() => taskStore.isLoading)
watch(
() => props.modelValue,
props.modelValue,
(value) => {
task.value = value
},

View File

@ -1,5 +1,5 @@
<template>
<div v-if="available">
<div v-if="mode !== 'disabled' && prefixes !== undefined">
<p class="help has-text-grey">
{{ $t('task.quickAddMagic.hint') }}.
<ButtonLink @click="() => visible = true">{{ $t('task.quickAddMagic.what') }}</ButtonLink>
@ -100,6 +100,5 @@ import {PREFIXES} from '@/modules/parseTaskText'
const visible = ref(false)
const mode = ref(getQuickAddMagicMode())
const available = computed(() => mode.value !== 'disabled')
const prefixes = computed(() => PREFIXES[mode.value])
</script>

View File

@ -7,7 +7,7 @@
/>
<ColorBubble
v-if="showListColor && listColor !== ''"
v-if="showListColor && listColor !== '' && currentList.id !== task.listId"
:color="listColor"
class="mr-1"
/>

View File

@ -4,11 +4,11 @@ import {debouncedWatch, tryOnMounted, useWindowSize, type MaybeRef} from '@vueus
// TODO: also add related styles
// OR: replace with vueuse function
export function useAutoHeightTextarea(value: MaybeRef<string>) {
const textarea = ref<HTMLInputElement>()
const textarea = ref<HTMLTextAreaElement | null>(null)
const minHeight = ref(0)
// adapted from https://github.com/LeaVerou/stretchy/blob/47f5f065c733029acccb755cae793009645809e2/src/stretchy.js#L34
function resize(textareaEl: HTMLInputElement | undefined) {
function resize(textareaEl: HTMLTextAreaElement | null) {
if (!textareaEl) return
let empty

View File

@ -3,12 +3,11 @@ import {useOnline as useNetworkOnline} from '@vueuse/core'
import type {ConfigurableWindow} from '@vueuse/core'
export function useOnline(options?: ConfigurableWindow) {
const isOnline = useNetworkOnline(options)
const fakeOnlineState = !!import.meta.env.VITE_IS_ONLINE
if (fakeOnlineState) {
if (isOnline.value === false && fakeOnlineState) {
console.log('Setting fake online state', fakeOnlineState)
return ref(true)
}
return fakeOnlineState
? ref(true)
: useNetworkOnline(options)
return isOnline
}

View File

@ -1 +0,0 @@
export const URL_PREFIX = '/api/v1' // _without_ slash at the end

View File

@ -0,0 +1,24 @@
function getDefaultBackground() {
const div = document.createElement('div')
document.head.appendChild(div)
const bg = window.getComputedStyle(div).backgroundColor
document.head.removeChild(div)
return bg
}
// get default style for current browser
const defaultStyle = getDefaultBackground() // typically "rgba(0, 0, 0, 0)"
// based on https://stackoverflow.com/a/62630563/15522256
export function getInheritedBackgroundColor(el: HTMLElement): string {
const backgroundColor = window.getComputedStyle(el).backgroundColor
if (backgroundColor !== defaultStyle) return backgroundColor
if (!el.parentElement) {
// we reached the top parent el without getting an explicit color
return defaultStyle
}
return getInheritedBackgroundColor(el.parentElement)
}

View File

@ -17,3 +17,8 @@ export const redirectToProvider = (provider: IProvider, redirectUrl = '') => {
window.location.href = `${provider.authUrl}?client_id=${provider.clientId}&redirect_uri=${redirectUrl}${provider.key}&response_type=code&scope=openid email profile&state=${state}`
}
export const redirectToProviderOnLogout = (provider: IProvider) => {
if (provider.logoutUrl.length > 0){
window.location.href = `${provider.logoutUrl}`
}
}

View File

@ -14,31 +14,35 @@ interface dateFoundResult {
const monthsRegexGroup = '(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)'
function matchesDateExpr(text: string, dateExpr: string): boolean {
return text.match(new RegExp('(^| )' + dateExpr, 'g')) !== null
}
export const parseDate = (text: string, now: Date = new Date()): dateParseResult => {
const lowerText: string = text.toLowerCase()
if (lowerText.includes('today')) {
if (matchesDateExpr(lowerText, 'today')) {
return addTimeToDate(text, getDateFromInterval(calculateDayInterval('today')), 'today')
}
if (lowerText.includes('tomorrow')) {
if (matchesDateExpr(lowerText, 'tomorrow')) {
return addTimeToDate(text, getDateFromInterval(calculateDayInterval('tomorrow')), 'tomorrow')
}
if (lowerText.includes('next monday')) {
if (matchesDateExpr(lowerText, 'next monday')) {
return addTimeToDate(text, getDateFromInterval(calculateDayInterval('nextMonday')), 'next monday')
}
if (lowerText.includes('this weekend')) {
if (matchesDateExpr(lowerText, 'this weekend')) {
return addTimeToDate(text, getDateFromInterval(calculateDayInterval('thisWeekend')), 'this weekend')
}
if (lowerText.includes('later this week')) {
if (matchesDateExpr(lowerText, 'later this week')) {
return addTimeToDate(text, getDateFromInterval(calculateDayInterval('laterThisWeek')), 'later this week')
}
if (lowerText.includes('later next week')) {
if (matchesDateExpr(lowerText, 'later next week')) {
return addTimeToDate(text, getDateFromInterval(calculateDayInterval('laterNextWeek')), 'later next week')
}
if (lowerText.includes('next week')) {
if (matchesDateExpr(lowerText, 'next week')) {
return addTimeToDate(text, getDateFromInterval(calculateDayInterval('nextWeek')), 'next week')
}
if (lowerText.includes('next month')) {
if (matchesDateExpr(lowerText, 'next month')) {
const date: Date = new Date()
date.setDate(1)
date.setMonth(date.getMonth() + 1)
@ -48,7 +52,7 @@ export const parseDate = (text: string, now: Date = new Date()): dateParseResult
return addTimeToDate(text, date, 'next month')
}
if (lowerText.includes('end of month')) {
if (matchesDateExpr(lowerText, 'end of month')) {
const curDate: Date = new Date()
const date: Date = new Date(curDate.getFullYear(), curDate.getMonth() + 1, 0)
date.setHours(calculateNearestHours(date))
@ -229,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 {
@ -242,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

25
src/histoire.setup.ts Normal file
View File

@ -0,0 +1,25 @@
import { defineSetupVue3 } from '@histoire/plugin-vue'
import {i18n} from './i18n'
// import './histoire.css' // Import global CSS
import './styles/global.scss'
import {createPinia} from 'pinia'
import FontAwesomeIcon from '@/components/misc/Icon'
import XButton from '@/components/input/button.vue'
import Modal from '@/components/misc/modal.vue'
import Card from '@/components/misc/card.vue'
export const setupVue3 = defineSetupVue3(({ app }) => {
// Add Pinia store
const pinia = createPinia()
app.use(pinia)
app.use(i18n)
app.component('icon', FontAwesomeIcon)
app.component('XButton', XButton)
app.component('modal', Modal)
app.component('card', Card)
})

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migrate from other services to Vikunja",
"title": "Import from other services",
"titleService": "Import your data from {name} into Vikunja",
"import": "Import your data into Vikunja",
"description": "Click on the logo of one of the third-party services below to get started.",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migrace z jiných služeb do Vikunja",
"title": "Import from other services",
"titleService": "Importujte svá data z {name} do Vikunja",
"import": "Importujte svá data do Vikunja",
"description": "Chcete-li začít, klikněte na logo jedné ze služeb třetích stran.",

1054
src/i18n/lang/da-DK.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Von einem anderen Dienst zu Vikunja migrieren",
"title": "Aus anderen Diensten importieren",
"titleService": "Importiere deine Daten von {name} in Vikunja",
"import": "Importiere deine Daten in Vikunja",
"description": "Klicke auf das Logo eines der unten aufgeführten Drittanbieterdienste, um loszulegen.",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Vomene andere Dienst zu Vikunja migriere",
"title": "Aus anderen Diensten importieren",
"titleService": "Dini Date vo {name} in Vikunja importiere",
"import": "Dini Date in Vikunja importiere",
"description": "Klick ufs Logo une vo eine vo de Drittabüüter um aahzfange.",

View File

@ -418,7 +418,7 @@
}
},
"migrate": {
"title": "Migrate from other services to Vikunja",
"title": "Import from other services",
"titleService": "Import your data from {name} into Vikunja",
"import": "Import your data into Vikunja",
"description": "Click on the logo of one of the third-party services below to get started.",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migrate from other services to Vikunja",
"title": "Import from other services",
"titleService": "Import your data from {name} into Vikunja",
"import": "Import your data into Vikunja",
"description": "Click on the logo of one of the third-party services below to get started.",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migrer dautres services vers Vikunja",
"title": "Import from other services",
"titleService": "Importe tes données depuis {name} dans Vikunja",
"import": "Importer tes données dans Vikunja",
"description": "Clique sur le logo dun des services tiers ci-dessous pour commencer.",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migra da altri servizi a Vikunja",
"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.",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migreer van andere diensten naar Vikunja",
"title": "Import from other services",
"titleService": "Importeer je gegevens van {name} naar Vikunja",
"import": "Importeer je data in Vikunja",
"description": "Click on the logo of one of the third-party services below to get started.",

View File

@ -1,6 +1,6 @@
{
"home": {
"welcomeNight": "God Morgen {username}!",
"welcomeNight": "God Natt {username}!",
"welcomeMorning": "God Morgen {username}!",
"welcomeDay": "Hei {username}!",
"welcomeEvening": "God Morgen {username}!",
@ -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:",
@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migrere fra andre tjenester til Vikunja",
"title": "Importer fra andre tjenester",
"titleService": "Importer dine data fra {name} til Vikunja",
"import": "Importer dine data til Vikunja",
"description": "Klikk på logoen til en av tjenester fra tredjeparter nedenfor for å starte.",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migruj z innych usług do Vikunja",
"title": "Import from other services",
"titleService": "Zaimportuj swoje dane z {name} do Vikunja",
"import": "Zaimportuj swoje dane do Vikunja",
"description": "Aby rozpocząć, kliknij logo jednej z usług zewnętrznych.",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migrate from other services to Vikunja",
"title": "Import from other services",
"titleService": "Import your data from {name} into Vikunja",
"import": "Importe seus dados para o Vikunja",
"description": "Click on the logo of one of the third-party services below to get started.",
@ -538,22 +538,22 @@
"guide": "Guide"
},
"multiselect": {
"createPlaceholder": "Create new",
"selectPlaceholder": "Click or press enter to select"
"createPlaceholder": "Criar novo",
"selectPlaceholder": "Clique ou pressione Enter para selecionar"
},
"datepickerRange": {
"to": "To",
"from": "From",
"fromto": "{from} to {to}",
"to": "Para",
"from": "De",
"fromto": "{from} até {to}",
"ranges": {
"today": "Today",
"thisWeek": "This Week",
"restOfThisWeek": "The Rest of This Week",
"nextWeek": "Next Week",
"next7Days": "Next 7 Days",
"lastWeek": "Last Week",
"thisMonth": "This Month",
"restOfThisMonth": "The Rest of This Month",
"today": "Hoje",
"thisWeek": "Esta semana",
"restOfThisWeek": "O resto desta semana",
"nextWeek": "Próxima semana",
"next7Days": "Próximos 7 dias",
"lastWeek": "Semana passada",
"thisMonth": "Este mês",
"restOfThisMonth": "O resto deste mês",
"nextMonth": "Próximo mês",
"next30Days": "Próximos 30 dias",
"lastMonth": "Último mês",
@ -615,7 +615,7 @@
},
"detail": {
"chooseDueDate": "Click here to set a due date",
"chooseStartDate": "Click here to set a start date",
"chooseStartDate": "Clique aqui para definir uma data de início",
"chooseEndDate": "Click here to set an end date",
"move": "Move task to a different list",
"done": "Marcar tarefa como concluída!",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migrar de outros serviços para o Vikunja",
"title": "Importar de outros serviços",
"titleService": "Importar os teus dados de {name} para o Vikunja",
"import": "Importar os teus dados para o Vikunja",
"description": "Clica no logótipo de um dos serviços de terceiros abaixo para começar.",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migrate from other services to Vikunja",
"title": "Import from other services",
"titleService": "Import your data from {name} into Vikunja",
"import": "Import your data into Vikunja",
"description": "Click on the logo of one of the third-party services below to get started.",

File diff suppressed because it is too large Load Diff

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migrate from other services to Vikunja",
"title": "Import from other services",
"titleService": "Import your data from {name} into Vikunja",
"import": "Import your data into Vikunja",
"description": "Click on the logo of one of the third-party services below to get started.",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migrate from other services to Vikunja",
"title": "Import from other services",
"titleService": "Import your data from {name} into Vikunja",
"import": "Import your data into Vikunja",
"description": "Click on the logo of one of the third-party services below to get started.",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migrate from other services to Vikunja",
"title": "Import from other services",
"titleService": "Import your data from {name} into Vikunja",
"import": "Import your data into Vikunja",
"description": "Click on the logo of one of the third-party services below to get started.",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Di chuyển từ các dịch vụ khác đến Vikunja",
"title": "Import from other services",
"titleService": "Nhập dữ liệu của bạn từ {name} vào Vikunja",
"import": "Nhập dữ liệu của bạn vào Vikunja",
"description": "Nhấp vào Logo của một trong các dịch vụ bên dưới để bắt đầu.",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "从其他服务迁移到 Vikunja",
"title": "Import from other services",
"titleService": "从 {name} 导入您的数据到 Vikunja",
"import": "导入数据到 Vikunja",
"description": "点击下面的第三方服务的徽标开始操作。",

View File

@ -417,7 +417,7 @@
}
},
"migrate": {
"title": "Migrate from other services to Vikunja",
"title": "Import from other services",
"titleService": "Import your data from {name} into Vikunja",
"import": "Import your data into Vikunja",
"description": "Click on the logo of one of the third-party services below to get started.",

Some files were not shown because too many files have changed in this diff Show More