Compare commits

..

1008 Commits

Author SHA1 Message Date
kolaente 9ef592c5d0
fix: make sure there's enough space for the close button
continuous-integration/drone/pr Build is passing Details
2023-10-22 19:20:49 +02:00
kolaente 08f55292f3
fix(task): make sure the modal close button is not overlapped with the title field 2023-10-22 19:14:22 +02:00
renovate a35b0f64a2 fix(deps): update dependency lowlight to v2.9.0 (#3789)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3789
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-22 17:01:34 +00:00
renovate 6c2b30f8ef fix(deps): update dependency @types/lodash.clonedeep to v4.5.8 (#3787)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3787
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-22 15:22:23 +00:00
renovate daa720669a fix(deps): update sentry-javascript monorepo to v7.74.1 (#3778)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3778
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-22 15:12:47 +00:00
konrad 26fc9b4e4f feat: move from easymde to tiptap editor (#2222)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #2222
2023-10-22 13:48:57 +00:00
kolaente 37af478811
chore(editor): remove marked usages
continuous-integration/drone/pr Build is passing Details
2023-10-22 15:18:39 +02:00
kolaente 22223a56bd
chore(editor): remove converting markdown
continuous-integration/drone/pr Build is failing Details
2023-10-22 15:12:36 +02:00
kolaente c367b70ccc
chore(editor): remove unused components 2023-10-22 15:12:23 +02:00
kolaente 9103ad8505
chore(deps): remove unused dependencies
continuous-integration/drone/pr Build is passing Details
2023-10-22 14:46:21 +02:00
kolaente e4eaca82e1
fix(editor): add missing dependency
continuous-integration/drone/pr Build is passing Details
2023-10-22 14:29:48 +02:00
kolaente 229beec1d1
fix(editor): lint
continuous-integration/drone/pr Build is failing Details
2023-10-22 14:24:10 +02:00
kolaente 803f9c81c2
fix(editor): make tests work with changed structure
continuous-integration/drone/pr Build is failing Details
2023-10-22 14:21:28 +02:00
kolaente c6b123734b
feat(editor): add tests to check rendering of task description 2023-10-22 14:21:13 +02:00
kolaente 0154b2a475
fix(editor): allow checking a checkbox even when the editor is set to read only
continuous-integration/drone/pr Build is failing Details
2023-10-22 13:26:01 +02:00
kolaente 32ca8853bc
fix(editor): don't use global shortcut when anything is focused 2023-10-22 12:48:31 +02:00
kolaente f6d5cbcf6f
feat(editor): only load attachment images when rendering is done
continuous-integration/drone/pr Build is failing Details
2023-10-22 12:38:34 +02:00
kolaente d7503dc4a2
feat(editor): edit mode
continuous-integration/drone/pr Build is failing Details
2023-10-22 12:08:27 +02:00
kolaente 632e3c5a0b
fix(editor): duplicate name for extension
continuous-integration/drone/pr Build is failing Details
2023-10-22 11:02:03 +02:00
kolaente c61f1a45fb
fix(editor): placeholder showing or not showing
continuous-integration/drone/pr Build is failing Details
2023-10-22 11:00:42 +02:00
kolaente 2f3196ef86
fix(editor): duplicate name
continuous-integration/drone/pr Build is failing Details
2023-10-22 10:50:16 +02:00
kolaente 2864854cd4
fix(editor): don't prevent typing editor focus shortcut when other instance of an editor is focused already
continuous-integration/drone/pr Build is failing Details
2023-10-22 10:47:13 +02:00
kolaente a453449fea
fix(editor): reset on empty
continuous-integration/drone/pr Build is failing Details
2023-10-22 10:41:34 +02:00
kolaente abb6630b4b
feat(editor): add comment when pressing ctrl enter 2023-10-22 10:40:12 +02:00
kolaente 63c40b29b0
feat(editor): save when pressing ctrl enter 2023-10-22 10:38:53 +02:00
Frederick [Bot] b7ff71ba76 [skip ci] Updated translations via Crowdin 2023-10-22 00:03:33 +00:00
kolaente 19a78f1f75
fix(editor): lint
continuous-integration/drone/pr Build is failing Details
2023-10-21 19:52:56 +02:00
kolaente d6a41fa518
chore(editor): remove old editor component
continuous-integration/drone/pr Build is failing Details
2023-10-21 19:48:17 +02:00
kolaente 859fc1e94e
feat(editor): edit shortcut to set focus into the editor
continuous-integration/drone/pr Build is failing Details
2023-10-21 19:46:46 +02:00
kolaente aa715dd9e1
chore(editor): cleanup unused options 2023-10-21 19:46:25 +02:00
kolaente daa2ed3b1c
feat(editor): allow passing placeholder down
continuous-integration/drone/pr Build is failing Details
2023-10-21 19:33:32 +02:00
kolaente 1443e23f18
chore(editor): cleanup 2023-10-21 19:29:27 +02:00
kolaente 34420b623c
fix(editor): use edit enable 2023-10-21 19:29:19 +02:00
kolaente 80dc35eabb
fix(editor): actions styling 2023-10-21 19:29:00 +02:00
kolaente cb1d2b3834
fix(editor): always show placeholder when empty
continuous-integration/drone/pr Build is failing Details
2023-10-21 19:18:28 +02:00
kolaente 0ef775e9b9
feat(editor): improve overall styling
continuous-integration/drone/pr Build is failing Details
2023-10-21 19:08:11 +02:00
kolaente a7e4e3adf9
feat(editor): add placeholder 2023-10-21 19:02:55 +02:00
kolaente d005875bbf
chore(editor): make sure all tiptap dependencies are updated as one 2023-10-21 18:57:17 +02:00
kolaente dc3ee112bd
chore(editor): cleanup
continuous-integration/drone/pr Build is failing Details
2023-10-21 18:47:04 +02:00
kolaente 9b20dc1899
feat(editor): properly bubble changes when they are made
continuous-integration/drone/pr Build is failing Details
2023-10-21 18:40:30 +02:00
kolaente 22103626b8
fix(editor): make checklist indicator work again
continuous-integration/drone/pr Build is failing Details
2023-10-21 18:18:17 +02:00
kolaente 4f2d7b3ce2
feat(editor): add uploading an image on save
continuous-integration/drone/pr Build is failing Details
2023-10-21 18:03:59 +02:00
kolaente 76d31c84ad
feat(editor): add tooltips for everything
continuous-integration/drone/pr Build is failing Details
2023-10-21 17:48:35 +02:00
kolaente 66c37f10e0
chore(editor): cleanup
continuous-integration/drone/pr Build is failing Details
2023-10-21 14:10:26 +02:00
kolaente 0b2aa723a6
feat(editor): open links when clicking on them
continuous-integration/drone/pr Build is failing Details
2023-10-21 14:07:39 +02:00
kolaente d75a963d08
feat(editor): add code highlighting
continuous-integration/drone/pr Build is failing Details
2023-10-21 14:06:47 +02:00
kolaente beefc1d5ef
feat(editor): add bubble menu
continuous-integration/drone/pr Build is failing Details
2023-10-21 14:02:53 +02:00
kolaente 17c23d9463
feat(editor): make image upload work via slash command
continuous-integration/drone/pr Build is failing Details
2023-10-21 13:28:59 +02:00
kolaente 02ab1b8c0a
feat(editor): add all slash commands
continuous-integration/drone/pr Build is failing Details
2023-10-21 13:00:12 +02:00
kolaente e81c98fe5b
chore(editor): format 2023-10-21 11:52:20 +02:00
kolaente 3bf806f00c
fix(editor): add missing dependencies for commands
continuous-integration/drone/pr Build is failing Details
2023-10-21 11:46:02 +02:00
kolaente aea3f86a8f
feat(editor): add command list example
continuous-integration/drone/pr Build is failing Details
copied from 252acb32d2/demos/src/Experiments/Commands/Vue
2023-10-21 11:33:49 +02:00
kolaente 5297208d92
feat(editor): move all editor related components into one folder
continuous-integration/drone/pr Build is failing Details
2023-10-21 11:15:17 +02:00
kolaente c84bcfddba
feat(editor): add proper description for all buttons
continuous-integration/drone/pr Build is failing Details
2023-10-21 11:10:43 +02:00
kolaente 0772acbead
chore(editor): format 2023-10-21 10:53:41 +02:00
renovate 123c665d9d chore(deps): update dependency eslint to v8.52.0 (#3785)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3785
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-21 08:08:26 +00:00
kolaente 4f3efe4454
feat(editor): resolve and load attachment images from content
continuous-integration/drone/pr Build is failing Details
2023-10-20 23:03:38 +02:00
kolaente 671c658868
fix(editor): actually populate loaded data into the editor
continuous-integration/drone/pr Build is failing Details
2023-10-20 22:52:21 +02:00
kolaente 05bf7ccf0b
feat(editor): image upload
continuous-integration/drone/pr Build is failing Details
2023-10-20 22:43:10 +02:00
renovate b76acb15c7 chore(deps): update dev-dependencies (major) (#3741)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3741
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-20 19:34:11 +00:00
kolaente 953361c480
chore(deps): update lockfile
continuous-integration/drone/pr Build is failing Details
2023-10-20 17:10:55 +02:00
kolaente 8b60e5b2c8
fix(editor): add icons for clearing marks and nodes 2023-10-20 17:10:06 +02:00
kolaente faf93a6088
feat(editor): enable table 2023-10-20 17:10:06 +02:00
kolaente 8e07d9647a
chore(editor): move checklist to the other lists 2023-10-20 17:10:06 +02:00
kolaente 08959fdb77
fix(editor): focus state 2023-10-20 17:10:06 +02:00
kolaente e716fd1bf9
fix(editor): list styling 2023-10-20 17:10:06 +02:00
kolaente 63865028b8
feat(editor): make task list work 2023-10-20 17:10:06 +02:00
kolaente e760ce45e4
fix(editor): checklist button icon 2023-10-20 17:10:06 +02:00
kolaente af9eb358ee
fix(editor): permission check for table editing 2023-10-20 17:10:06 +02:00
kolaente ddcf6bf0a5
fix(editor): image button icon 2023-10-20 17:10:06 +02:00
kolaente 9c71e30efe
chore(editor): add break icon 2023-10-20 17:10:06 +02:00
kolaente c58ad47782
chore(editor): use typed props definition 2023-10-20 17:10:05 +02:00
kolaente ca0d9e6bd5
chore(editor): add horizontal line icon 2023-10-20 17:10:05 +02:00
kolaente ad3234b19f
chore(deps): update dependencies 2023-10-20 17:10:05 +02:00
Dominik Pschenitschni 24b8915983
wip: tiptap editor 2023-10-20 17:10:05 +02:00
kolaente 01c2acdf34
chore(deps): update flake
continuous-integration/drone/push Build is passing Details
2023-10-20 17:09:55 +02:00
kolaente ff2b4b8bf4
feat(notifications): add option to mark all as read
continuous-integration/drone/push Build is passing Details
2023-10-20 16:52:03 +02:00
kolaente d73c62a424
fix(task): correctly build task identifier 2023-10-20 16:52:03 +02:00
renovate cac41a1c86 fix(deps): update dependency vue to v3.3.6 (#3784)
continuous-integration/drone/push Build is failing Details
Reviewed-on: #3784
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-20 14:01:41 +00:00
kolaente aeed4b3a3b
fix(settings): allow removing the default project via settings
continuous-integration/drone/push Build is passing Details
2023-10-20 16:01:22 +02:00
renovate 8992caadf9 fix(deps): update dependency marked to v9.1.2 (#3774)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3774
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-20 13:17:59 +00:00
renovate b2b423aee5 chore(deps): update dependency node to v18.18.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-10-20 12:08:04 +00:00
konrad 5d991e539b feat: webhooks (#3783)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3783
2023-10-20 11:56:59 +00:00
renovate accde483cb fix(deps): update dependency vue to v3.3.5 (#3782)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3782
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-20 11:38:11 +00:00
renovate 2d5e560b74 chore(deps): update dev-dependencies (#3780)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3780
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-20 11:37:50 +00:00
kolaente 5d91134b48
fix(tasks): use mousedown event instead of click to close the task popup
continuous-integration/drone/push Build is passing Details
Resolves #3779
2023-10-20 13:14:46 +02:00
kolaente 0e5415a2c9
fix(webhooks): styling
continuous-integration/drone/pr Build is passing Details
2023-10-20 12:32:55 +02:00
kolaente 779aad1b2d
feat(webhooks): add form validation 2023-10-20 12:32:46 +02:00
kolaente 3d2fe4cf65
feat(webhooks): add webhook management form 2023-10-18 20:12:48 +02:00
renovate c38421466b chore(deps): update dev-dependencies (#3776)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3776
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-18 15:44:47 +00:00
Frederick [Bot] df09bca010 [skip ci] Updated translations via Crowdin 2023-10-18 00:08:50 +00:00
kolaente b9717f504d
feat(i18n): add arabic to list of selectable languages
continuous-integration/drone/push Build is passing Details
2023-10-17 18:44:08 +02:00
Frederick [Bot] bb7c4f40a0 [skip ci] Updated translations via Crowdin 2023-10-17 00:09:16 +00:00
Frederick [Bot] 0b9ef50f80 [skip ci] Updated translations via Crowdin 2023-10-16 00:36:16 +00:00
Frederick [Bot] cd295960a4 [skip ci] Updated translations via Crowdin 2023-10-14 00:08:49 +00:00
renovate eb591fdd3c chore(deps): update dev-dependencies (#3769)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3769
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-13 13:59:06 +00:00
renovate 23e1899fce fix(deps): update dependency @github/hotkey to v2.1.1 (#3770)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3770
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-13 13:32:18 +00:00
renovate 22968ba639 fix(deps): update dependency pinia to v2.1.7 (#3771)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3771
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-13 13:17:47 +00:00
renovate b345f0ad61 fix(deps): update sentry-javascript monorepo to v7.74.0 (#3772)
continuous-integration/drone/push Build is failing Details
Reviewed-on: #3772
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-13 12:27:20 +00:00
Frederick [Bot] 4df34701ab [skip ci] Updated translations via Crowdin 2023-10-13 00:08:52 +00:00
renovate a5f7487bd0 fix(deps): update dependency marked to v9.1.1 (#3768)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3768
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-11 21:42:37 +00:00
kolaente ae001c6ca7
fix(user): allow openid users to request their deletion
continuous-integration/drone/push Build is passing Details
Resolves https://community.vikunja.io/t/delete-user-not-possible-when-using-oidc/1689/4
2023-10-11 19:07:11 +02:00
kolaente f0b340a9c7
feat(task): save currently opened task with control/meta + s
continuous-integration/drone/push Build is passing Details
2023-10-11 17:44:17 +02:00
renovate 40538df392 fix(deps): update dependency @github/hotkey to v2.1.0 (#3766)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3766
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-11 09:01:48 +00:00
renovate fc17b16c60 chore(deps): update dependency sass to v1.69.2 (#3767)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3767
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-11 07:34:29 +00:00
renovate 6c59b4e2d2 fix(deps): update dependency highlight.js to v11.9.0 (#3763)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3763
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-10 19:21:25 +00:00
renovate e8a38ed482 fix(deps): update vueuse to v10.5.0 (#3762)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3762
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-10 18:28:10 +00:00
renovate f5604dcac6 chore(deps): update dependency node to v18.18.1
continuous-integration/drone/push Build is passing Details
2023-10-10 16:24:22 +00:00
renovate 049c644959 chore(deps): update dev-dependencies (#3761)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3761
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-10 16:23:29 +00:00
kolaente 07b1e9a6b7
chore: add pr lockdown
continuous-integration/drone/push Build is passing Details
2023-10-10 18:02:40 +02:00
renovate 7aedf6ee1f chore(deps): update pnpm to v8.9.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-10-09 12:09:54 +00:00
renovate bc9bfe3300 fix(deps): update dependency marked to v9.1.0 (#3760)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3760
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-05 07:06:20 +00:00
renovate c2005c6c71 chore(deps): update dev-dependencies (#3757)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3757
Reviewed-by: konrad <k@knt.li>
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-04 06:48:23 +00:00
renovate d7cbade64e chore(deps): update node.js to v20.8 (#3756)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3756
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-03 14:59:12 +00:00
renovate 06b00b77ed fix(deps): update dependency dompurify to v3.0.6 (#3754)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3754
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-03 14:58:26 +00:00
renovate f2392cef7e fix(deps): update dependency vue-router to v4.2.5 (#3755)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3755
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-03 14:19:27 +00:00
renovate e6f2b36d88 fix(deps): update dependency dayjs to v1.11.10 (#3753)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3753
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-03 14:16:01 +00:00
kolaente 7d2fcd26f2 chore(deps): update lockfile
continuous-integration/drone/push Build is passing Details
2023-10-03 13:34:11 +00:00
renovate 369e22f224 fix(deps): update dependency ufo to v1.3.1 2023-10-03 13:34:11 +00:00
kolaente 0ff5b90ebd chore(deps): update lockfile
continuous-integration/drone/push Build is passing Details
2023-10-03 12:16:07 +00:00
kolaente e89245e42d fix(deps): update dependency @infectoone/vue-ganttastic to v2.2.0 2023-10-03 12:16:07 +00:00
kolaente 35717a1e29 fix(deps): update dependency @kyvg/vue3-notification to v3 2023-10-03 12:16:07 +00:00
kolaente e46cf2fa1b fix(deps): update dependency vue-i18n to v9.5.0 2023-10-03 12:16:07 +00:00
kolaente 1ad6d5a66b fix(deps): update dependency @intlify/unplugin-vue-i18n to v1 2023-10-03 12:16:07 +00:00
kolaente 4754bb99f0 fix(deps): update dependency marked to v9 2023-10-03 12:16:07 +00:00
kolaente 608e99fffc chore(deps): update pnpm to v8.8.0 2023-10-03 12:16:07 +00:00
kolaente d6741d19e3 fix(deps): update sentry-javascript monorepo to v7.73.0 2023-10-03 12:16:07 +00:00
kolaente ec52be0353 fix(deps): update dependency axios to v1.5.1 2023-10-03 12:16:07 +00:00
renovate 2d5ab4f5f0 chore(deps): update dependency node to v18.18.0
continuous-integration/drone/push Build is passing Details
2023-10-03 11:45:51 +00:00
Giacomo Rossetto a71755e408 feat(gantt): implement dynamic sizing on small date ranges (#3750)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3750
Reviewed-by: konrad <k@knt.li>
Co-authored-by: Giacomo Rossetto <jackyman_cs4@live.it>
Co-committed-by: Giacomo Rossetto <jackyman_cs4@live.it>
2023-10-03 11:30:24 +00:00
kolaente 66c7a05cdb
fix(project): correctly show project color next to project title in list view
continuous-integration/drone/push Build is passing Details
Resolves https://community.vikunja.io/t/color-bubbles-not-showing-after-import/1648
2023-09-29 20:46:02 +02:00
kolaente 287daf9125
fix(auth): silently discard invalid auth tokens and log the user out
continuous-integration/drone/push Build is passing Details
2023-09-29 10:38:00 +02:00
kolaente 8507808058
fix(tasks): ignore empty lines when adding multiple tasks at once
continuous-integration/drone/push Build is passing Details
Resolves #3732 (comment)
2023-09-29 10:06:38 +02:00
kolaente 93c155dd2f
fix(quick actions): always open quick actions with hotkey, even if other inputs are focused
continuous-integration/drone/push Build is passing Details
Resolves #3743
2023-09-29 09:48:06 +02:00
kolaente b1c4748969
fix(background): unsplash author credit in dark mode
continuous-integration/drone/push Build is passing Details
2023-09-29 09:33:30 +02:00
kolaente 0887860b2a
fix(task): priority label spacing
continuous-integration/drone/push Build is passing Details
2023-09-29 09:32:00 +02:00
kolaente 0b1c8ed4dd
fix(attachments): layout and coloring in dark mode
continuous-integration/drone/push Build is passing Details
2023-09-29 09:28:51 +02:00
kolaente 3988a3f9f8
fix(task): correct spacing to task and project title
continuous-integration/drone/push Build is passing Details
2023-09-29 09:19:17 +02:00
Frederick [Bot] 11b65e844c [skip ci] Updated translations via Crowdin 2023-09-28 00:08:46 +00:00
renovate 5c23343172 chore(deps): update dev-dependencies (#3747)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3747
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-09-27 07:34:49 +00:00
renovate 01a4335c7c chore(deps): update node.js to v20.7 (#3736)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3736
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-09-27 07:34:22 +00:00
Frederick [Bot] 4a2ecf5fe7 [skip ci] Updated translations via Crowdin 2023-09-24 00:29:29 +00:00
renovate 0235b14997 chore(deps): update dev-dependencies (#3746)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3746
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-09-15 11:59:29 +00:00
renovate 8eafa23269 chore(deps): update dev-dependencies (#3740)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3740
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-09-13 12:35:39 +00:00
kolaente 5c95a721f4
fix(filter): don't show other filters in project selection in saved filter
continuous-integration/drone/push Build is passing Details
Resolves https://github.com/go-vikunja/frontend/issues/125
2023-09-13 11:41:47 +02:00
kolaente a6eb804fae
fix(gantt): open task with double click from the gantt chart
continuous-integration/drone/push Build is passing Details
2023-09-12 15:12:16 +02:00
Frederick [Bot] 09ffd9414b [skip ci] Updated translations via Crowdin 2023-09-08 00:29:37 +00:00
kolaente b126a7f7ff
fix(navigation): don't hide color bubble in navigation on touch devices
continuous-integration/drone/push Build is failing Details
Related discussion: https://community.vikunja.io/t/board-color-sticker/1607
2023-09-07 16:36:11 +02:00
kolaente f256fc3843
fix(list view): align nested subtasks with the parent text
continuous-integration/drone/push Build is failing Details
2023-09-07 13:47:52 +02:00
kolaente e41712647d
feat(list view): show subtasks nested
continuous-integration/drone/push Build is failing Details
Resolves #363
2023-09-07 13:43:15 +02:00
Frederick [Bot] 842e2c2811 [skip ci] Updated translations via Crowdin 2023-09-07 00:28:16 +00:00
kolaente 2d61a349ac
feat(task): immediately set focus on the task search input when opening the related tasks menu
continuous-integration/drone/push Build is failing Details
2023-09-06 18:50:38 +02:00
kolaente 54c527c23f
feat(tasks): make the whole task in list view clickable
continuous-integration/drone/push Build is failing Details
Resolves #3172
Closes #3176
Closes #3180
2023-09-06 18:31:30 +02:00
kolaente 4d8c6622d2
fix(ci): use correct secret key to push
continuous-integration/drone/push Build is failing Details
2023-09-06 18:03:09 +02:00
kolaente 3f3d4b1682
feat(labels): assign random color when creating labels
continuous-integration/drone/push Build is failing Details
Resolves F-591
Related discussion: https://community.vikunja.io/t/assign-a-random-color-to-a-new-label/348/7
2023-09-06 17:10:36 +02:00
kolaente 9c46d064ac
feat(quick add magic): allow using the project identifier via quick add magic
continuous-integration/drone/push Build is failing Details
Related discussion: https://community.vikunja.io/t/using-shorter-list-names-in-quick-add-magic/895
2023-09-06 16:51:23 +02:00
kolaente 0d3143d465
fix(quick add magic): headline 2023-09-06 16:45:12 +02:00
kolaente 337c3e5e3e
fix: lint
continuous-integration/drone/push Build is failing Details
2023-09-06 16:31:07 +02:00
kolaente 3bb5308141
feat(task): group related task action buttons
continuous-integration/drone/push Build is failing Details
2023-09-06 16:30:00 +02:00
kolaente 3fec92283b
fix(task): priority label sizing and positioning in different environments
continuous-integration/drone/push Build is failing Details
2023-09-06 15:58:52 +02:00
kolaente beb016400e
feat(task): move task priority to the front when showing tasks inline
continuous-integration/drone/push Build is failing Details
2023-09-06 15:53:40 +02:00
kolaente 7746d39161
fix(task): remove wrong repeat types
continuous-integration/drone/push Build is failing Details
Repeating "monthly" or "yearly" was never what people expected, only 30 or 365 days which is not always correct. This change removes these settings since the repeating modes will be re-done anyway.

Related to #3585 (comment)
2023-09-06 15:41:48 +02:00
kolaente b187e8c1b6
fix(ci): pin used node version to 20.5 to avoid build issues
continuous-integration/drone/push Build is failing Details
Related https://github.com/vitejs/vite/issues/14299
2023-09-06 15:33:04 +02:00
kolaente 0ecda46af9
chore(deps): update dependencies
continuous-integration/drone/push Build is failing Details
2023-09-06 15:30:00 +02:00
kolaente 59dc927b5c
feat(i18n): update translations only once a day
continuous-integration/drone/push Build is failing Details
2023-09-06 15:24:44 +02:00
kolaente a13953ee14
fix(i18n): add upload files config
continuous-integration/drone/push Build is failing Details
2023-09-06 15:22:51 +02:00
kolaente a4b836d395
feat(i18n): run translation update directly
continuous-integration/drone/push Build was killed Details
2023-09-06 15:19:32 +02:00
kolaente 16b46b0f4d
feat(i18n): update crowdin sync to use v2 api
continuous-integration/drone/push Build is failing Details
2023-09-06 15:18:27 +02:00
kolaente 184110b986
fix(gantt): update the gantt view when switching between projects
continuous-integration/drone/push Build is failing Details
Resolves https://community.vikunja.io/t/listing-subprojects-tasks/1567/5
2023-09-06 13:25:27 +02:00
kolaente 1918947c0b
fix(tasks): reset page number when applying filters
continuous-integration/drone/push Build is failing Details
Resolves https://community.vikunja.io/t/when-filter-conditions-change-pages-arent-updated-according-to-new-list-length/1601
2023-09-06 10:50:52 +02:00
kolaente 4e5823183e
fix(tasks): update api route
continuous-integration/drone/push Build is failing Details
2023-09-06 10:41:39 +02:00
kolaente b9e17ea870
fix(api tokens): show a token after it was created 2023-09-06 09:59:27 +02:00
Frederick [Bot] a8a6ec5ab0 [skip ci] Updated translations via Crowdin 2023-09-06 00:29:43 +00:00
Frederick [Bot] 3e9b872894 [skip ci] Updated translations via Crowdin 2023-09-05 00:29:24 +00:00
kolaente c4adcf4655
chore: include version json string in release zip
continuous-integration/drone/push Build is passing Details
2023-09-04 22:19:37 +02:00
kolaente b1fe3fe29b
fix: don't render route modal when no properties are defined
continuous-integration/drone/push Build is passing Details
2023-09-04 21:33:50 +02:00
kolaente 5720a86bc3
fix(task): don't reload the kanban board when opening a task
continuous-integration/drone/push Build is passing Details
2023-09-04 21:01:42 +02:00
kolaente 86eff7d49e
fix(task): don't reload the kanban board when opening a task
continuous-integration/drone/push Build is failing Details
2023-09-04 20:27:55 +02:00
kolaente 7a9aa7771b
fix(tasks): play pop sound directly and not from store
continuous-integration/drone/push Build is passing Details
This solves two problems:

1. Previously, changing anything on a done task would play the pop sound all the time, because the store only knew the new done status was "done" and not if it was done previously already.
2. Safari will prevent playing a sound without user interaction. This means the user has to interact directly with the method playing the sound which was not the case when the sound was played from the store.

Resolves #3292
2023-09-04 20:14:43 +02:00
kolaente abbc11528e
feat(tasks): update due date text every minute
continuous-integration/drone/push Build is passing Details
Related discussion: https://community.vikunja.io/t/text-describing-time-past-due-date-is-never-refreshed/1376/3
2023-09-04 14:00:22 +02:00
kolaente 725fd1ad46
feat: improve error message for invalid API url
continuous-integration/drone/push Build is passing Details
Resolves #3680
2023-09-04 13:37:17 +02:00
kolaente 44754fac0f
chore(ci): sign drone config
continuous-integration/drone/push Build is passing Details
2023-09-04 13:11:59 +02:00
kolaente 7f2d92138e
fix: lint
continuous-integration/drone/push Build is pending Details
2023-09-04 13:11:31 +02:00
kolaente 95be0d1d32
fix(build): don't download Puppeteer when building for prod
continuous-integration/drone/push Build is pending Details
2023-09-04 13:07:48 +02:00
kolaente f63c39a578
feat(assignees): improve avatar list consistency
continuous-integration/drone/push Build is failing Details
Resolves #3354
2023-09-04 13:03:39 +02:00
kolaente 270e32290a
fix(quick add magic): ignore common task indention when adding multiple tasks at once
continuous-integration/drone/push Build is failing Details
Resolves #3732
2023-09-04 11:24:10 +02:00
kolaente 9cf8696b84
fix(docker): set correct default value for custom logo url
continuous-integration/drone/push Build is failing Details
2023-09-04 10:22:44 +02:00
Frederick [Bot] b97e13b6b4 [skip ci] Updated translations via Crowdin 2023-09-04 00:28:15 +00:00
konrad 04ba1011cc feat: add setting for default bucket
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3735
2023-09-03 15:14:44 +00:00
kolaente 52c0efe0ce
feat(kanban): add icon for bucket collapse
continuous-integration/drone/pr Build is passing Details
2023-09-03 16:32:29 +02:00
kolaente c803020537
feat(kanban): add setting for default bucket 2023-09-03 16:32:29 +02:00
kolaente 3373b5fc45
feat(kanban): save done bucket with project instead of bucket 2023-09-03 16:32:29 +02:00
kolaente f6d1db3595
fix: tests
continuous-integration/drone/push Build is passing Details
2023-09-03 16:30:36 +02:00
Frederick [Bot] ce6f099912 [skip ci] Updated translations via Crowdin 2023-09-03 00:29:23 +00:00
kolaente ed8fb71ff0
feat: add demo mode warning message
continuous-integration/drone/push Build is passing Details
Resolves #2453
2023-09-01 18:09:19 +02:00
konrad 28f2551d87 feat: api tokens
continuous-integration/drone/push Build is failing Details
Reviewed-on: #3733
2023-09-01 14:34:56 +00:00
kolaente cec480ad80
fix(api tokens): lint
continuous-integration/drone/pr Build is passing Details
2023-09-01 15:59:16 +02:00
kolaente 830a3745ba
feat(api tokens): show warning if token has expired
continuous-integration/drone/pr Build was killed Details
2023-09-01 13:32:00 +02:00
kolaente 49104c65b6
fix(api tokens): expiry of tokens in a number of days 2023-09-01 13:28:32 +02:00
kolaente 984978fe6d
feat(api tokens): format permissions and groups human-readable 2023-09-01 13:25:37 +02:00
kolaente bd7b973559
feat(api tokens): add deleting api tokens 2023-09-01 13:18:00 +02:00
kolaente 0bb85870db
feat(api tokens): allow custom selection of expiry dates 2023-09-01 13:07:20 +02:00
kolaente 021f92303d
feat(api tokens): validate title field when creating a new token 2023-09-01 12:56:23 +02:00
kolaente e47ad021a3
feat(api tokens): add token creation form 2023-09-01 12:47:32 +02:00
kolaente a20eef2453
feat(api tokens): add basic api token overview 2023-09-01 11:15:48 +02:00
Frederick [Bot] 7b57b10804 [skip ci] Updated translations via Crowdin 2023-08-31 00:29:36 +00:00
Frederick [Bot] 83a7032b6f [skip ci] Updated translations via Crowdin 2023-08-30 00:29:17 +00:00
renovate 49261a6fcc chore(deps): update dev-dependencies (#3726)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3726
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-08-29 11:59:08 +00:00
kolaente 5630c90dee
fix(task): show related tasks form with shortcut even when there are already other related tasks
continuous-integration/drone/push Build is passing Details
Resolves https://github.com/go-vikunja/frontend/issues/122
2023-08-29 13:57:12 +02:00
konrad 47d589002c feat: quick actions improvments
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3728
2023-08-29 11:24:00 +00:00
kolaente 99e2161c09
fix: lint
continuous-integration/drone/pr Build is passing Details
2023-08-29 12:46:30 +02:00
kolaente 20f61baf03
fix(quick actions): search for tasks within a project when specifying a project with quick add magic
continuous-integration/drone/pr Build is failing Details
2023-08-29 12:45:05 +02:00
kolaente 4e6b99544e
fix(quick actions): don't show projects when searching for labels or tasks
continuous-integration/drone/pr Build is failing Details
2023-08-29 12:38:59 +02:00
kolaente d57e1909c4
feat(quick actions): show labels as labels and tasks with all of their details 2023-08-29 12:33:43 +02:00
kolaente 99d8fbdfa7
feat(quick actions): show tasks for a label when selecting it
continuous-integration/drone/pr Build is failing Details
2023-08-29 11:11:37 +02:00
kolaente 442d0342a9
fix(quick actions): project search
continuous-integration/drone/pr Build is passing Details
2023-08-29 10:08:47 +02:00
kolaente a4b369470a
fix(quick actions): invalid class prop 2023-08-29 09:57:13 +02:00
kolaente 0ca73e0851
fix(quick actions): always search for projects 2023-08-29 09:41:53 +02:00
kolaente 9fc829115f
fix(quick actions): project filter 2023-08-29 09:34:08 +02:00
kolaente 1e19548563
chore(quick actions): format 2023-08-29 09:33:56 +02:00
kolaente c327d86a71
feat(quick actions): show task identifier 2023-08-29 09:33:41 +02:00
kolaente 3044560759
feat(quick actions): show done tasks last 2023-08-29 09:21:11 +02:00
kolaente c3f85fcb19
chore: format 2023-08-29 09:19:52 +02:00
renovate 53434952d3 chore(deps): update dev-dependencies (#3721)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3721
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-08-27 15:27:00 +00:00
renovate e9b0640660 fix(deps): update dependency @vueuse/core to v10.4.0 (#3723)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3723
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-08-27 15:00:38 +00:00
renovate ae57e5d314
chore(deps): update pnpm to v8.7.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-08-27 16:33:22 +02:00
kolaente 6e7928b2e4
fix(i18n): hungarian translation
continuous-integration/drone/push Build is passing Details
2023-08-27 16:32:06 +02:00
kolaente 47639b00f8
feat(i18n): add hungarian translation for selection
continuous-integration/drone/push Build is failing Details
2023-08-27 10:28:31 +02:00
Frederick [Bot] e63cecceca [skip ci] Updated translations via Crowdin 2023-08-27 00:29:16 +00:00
Frederick [Bot] 55e2e323ed [skip ci] Updated translations via Crowdin 2023-08-26 00:29:30 +00:00
kolaente f7e22c8c56
fix(auth): correctly redirect the user to the last visited page after login
continuous-integration/drone/push Build is passing Details
Resolves #3682
2023-08-24 12:15:45 +02:00
kolaente a9fb306e46
fix(i18n): fall back to browser language if the configured user language is invalid
continuous-integration/drone/push Build is passing Details
2023-08-24 11:37:23 +02:00
kolaente 58a1f46668
fix(projects): don't suggest to create a new task in an empty filter
continuous-integration/drone/push Build is passing Details
2023-08-24 11:32:28 +02:00
kolaente 6cbbe17bd8
fix(filters): don't allow marking a filter as favorite
continuous-integration/drone/push Build is passing Details
2023-08-24 11:30:57 +02:00
kolaente c01957aae2
fix: lint
continuous-integration/drone/push Build is passing Details
2023-08-24 11:27:31 +02:00
kolaente 1ad03877fb
fix(menu): separate favorite and saved filter projects from other projects
continuous-integration/drone/push Build is failing Details
Resolves #3710
Resolves https://github.com/go-vikunja/frontend/issues/119
2023-08-24 11:27:20 +02:00
kolaente fc72a82a2a
fix(task): duplicate attribute
continuous-integration/drone/push Build is failing Details
2023-08-24 11:18:17 +02:00
kolaente 63ef09b020
fix(filters): incorrect translation string 2023-08-24 11:18:03 +02:00
DIMITRIOS CHRYSOCHERIS 311b1d7594
chore: improve checking for API url '/' suffix (#121)
continuous-integration/drone/push Build is failing Details
2023-08-23 21:56:08 +02:00
davidangel cade3df3e9 feat: allow custom logo via environment variable (#3685)
continuous-integration/drone/push Build is passing Details
Related discussion: https://community.vikunja.io/t/change-vikunja-logo-and-color-scheme/621

Reviewed-on: #3685
Reviewed-by: konrad <k@knt.li>
Co-authored-by: davidangel <david@davidangel.net>
Co-committed-by: davidangel <david@davidangel.net>
2023-08-23 16:13:29 +00:00
kolaente 37975c1931 chore(deps): update lockfile
continuous-integration/drone/push Build is passing Details
2023-08-23 06:37:45 +00:00
renovate 0d500182e7 chore(deps): update dev-dependencies 2023-08-23 06:37:45 +00:00
Frederick [Bot] f647d6e9b4 [skip ci] Updated translations via Crowdin 2023-08-23 00:28:13 +00:00
renovate dbed4caca7 fix(deps): update dependency ufo to v1.2.0
continuous-integration/drone/push Build is failing Details
2023-08-21 11:06:04 +00:00
renovate 6d79c9b2ed chore(deps): update pnpm to v8.6.12
continuous-integration/drone/push Build is failing Details
2023-08-21 11:03:59 +00:00
renovate 24f0822a12 fix(deps): update dependency @vueuse/core to v10.3.0
continuous-integration/drone/push Build is failing Details
2023-08-21 11:03:34 +00:00
renovate f3ba778fd3 fix(deps): update dependency pinia to v2.1.6
continuous-integration/drone/push Build is running Details
2023-08-21 11:03:08 +00:00
renovate 55a7255728 fix(deps): update font awesome to v6.4.2
continuous-integration/drone/push Build is passing Details
2023-08-21 10:58:33 +00:00
renovate 2b47e5faec chore(deps): update node.js to v18.17.1
continuous-integration/drone/push Build is passing Details
2023-08-21 10:58:19 +00:00
kolaente 9f82ec4162
chore: update lockfile
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build encountered an error Details
2023-08-21 12:22:58 +02:00
kolaente 64c90c7fe8
chore: update flake 2023-08-21 12:22:30 +02:00
renovate 9fe3d2b2bc chore(deps): update dev-dependencies
continuous-integration/drone/pr Build is failing Details
2023-08-21 09:06:05 +00:00
Frederick [Bot] 0b1ec9f287 [skip ci] Updated translations via Crowdin 2023-08-16 00:09:52 +00:00
Frederick [Bot] baff1c6fc9 [skip ci] Updated translations via Crowdin 2023-08-15 00:08:33 +00:00
Frederick [Bot] ac3f0cc266 [skip ci] Updated translations via Crowdin 2023-08-07 00:08:30 +00:00
renovate 7e1cfebf6a chore(deps): update dependency @types/node to v18.17.0
continuous-integration/drone/push Build is passing Details
2023-07-23 01:29:55 +00:00
Frederick [Bot] 88203e8b7d [skip ci] Updated translations via Crowdin 2023-07-23 00:09:21 +00:00
renovate d466d50712 chore(deps): update dev-dependencies
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-22 01:04:58 +00:00
renovate cf945f2841 fix(deps): update sentry-javascript monorepo to v7.60.0
continuous-integration/drone/push Build is failing Details
2023-07-21 14:06:11 +00:00
renovate 74df69fc94 chore(deps): update dev-dependencies
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-21 12:04:48 +00:00
Frederick [Bot] 563ee8f5bc [skip ci] Updated translations via Crowdin 2023-07-21 00:09:38 +00:00
renovate 026db7acad chore(deps): update node.js to v18.17.0
continuous-integration/drone/push Build is passing Details
2023-07-20 07:24:20 +00:00
renovate 61e97bfe1c fix(deps): update sentry-javascript monorepo to v7.59.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-19 17:04:49 +00:00
renovate 6530d26b82 chore(deps): update pnpm to v8.6.9
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-18 11:04:21 +00:00
renovate 1e24fe8bab fix(deps): update sentry-javascript monorepo to v7.59.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-18 09:04:49 +00:00
renovate f786c2b8a2 chore(deps): update dev-dependencies
continuous-integration/drone/push Build is passing Details
2023-07-18 08:50:37 +00:00
renovate e596e2c3bc fix(deps): update sentry-javascript monorepo to v7.59.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-18 08:04:48 +00:00
Frederick [Bot] a5e49d9417 [skip ci] Updated translations via Crowdin 2023-07-18 00:08:41 +00:00
renovate 668b910190 chore(deps): update pnpm to v8.6.8
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-17 10:04:41 +00:00
renovate 2bdc532f89 fix(deps): update dependency codemirror to v5.65.14
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-17 08:04:36 +00:00
renovate 933c7d8acc chore(deps): update dev-dependencies
continuous-integration/drone/push Build is passing Details
2023-07-16 04:28:21 +00:00
Frederick [Bot] 253e716390 [skip ci] Updated translations via Crowdin 2023-07-16 00:09:30 +00:00
renovate d19a5d9714 chore(deps): update dev-dependencies
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-15 00:04:50 +00:00
renovate 90cad1c8dd chore(deps): update dev-dependencies
continuous-integration/drone/push Build is passing Details
2023-07-14 01:09:00 +00:00
Frederick [Bot] 057017c8eb [skip ci] Updated translations via Crowdin 2023-07-14 00:08:42 +00:00
kolaente d7ce8dd320
fix(quick add magic): repeating intervals in words
continuous-integration/drone/push Build is passing Details
Resolves #3676
2023-07-13 18:20:30 +02:00
kolaente 25b110ce48
fix(quick add magic): annually and variants spelling
continuous-integration/drone/push Build is passing Details
Related to #3676
2023-07-13 18:05:19 +02:00
renovate 33fe5e4f20 fix(deps): update sentry-javascript monorepo to v7.58.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-13 12:04:38 +00:00
renovate 129ef769a3 fix(deps): update sentry-javascript monorepo to v7.58.0
continuous-integration/drone/push Build is passing Details
2023-07-13 06:47:22 +00:00
renovate 9030a9f7c1 chore(deps): update dev-dependencies
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-13 02:05:04 +00:00
Frederick [Bot] 3748a496d5 [skip ci] Updated translations via Crowdin 2023-07-13 00:08:55 +00:00
renovate 890e7e1f52 chore(deps): update dependency vite to v4.4.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-12 00:05:46 +00:00
renovate 9e0f2b0249 chore(deps): update dev-dependencies
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-11 11:04:54 +00:00
renovate 9a34c522b2 fix(deps): update dependency dompurify to v3.0.5
continuous-integration/drone/push Build is passing Details
2023-07-11 10:24:42 +00:00
renovate 60dd698fad chore(deps): update dev-dependencies to v6
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-11 10:05:43 +00:00
kolaente 15ecafdf04
fix: don't try to load buckets for project id 0
continuous-integration/drone/push Build is passing Details
2023-07-11 10:42:20 +02:00
kolaente 8902c15f7e
fix: correctly resolve kanban board in the background when moving a task
continuous-integration/drone/push Build is failing Details
Resolves F-951
2023-07-10 18:10:14 +02:00
kolaente d5358793de
chore: provide better error messages when refreshing user info fails
continuous-integration/drone/push Build is passing Details
2023-07-10 12:20:40 +02:00
renovate 33798b8d88 chore(deps): update pnpm to v8.6.7
continuous-integration/drone/push Build is passing Details
2023-07-10 06:43:02 +00:00
renovate c686e8677b chore(deps): update dependency caniuse-lite to v1.0.30001514
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-10 00:05:28 +00:00
renovate 5acc1696a9 fix(deps): update dependency @intlify/unplugin-vue-i18n to v0.12.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-08 19:04:46 +00:00
renovate c4976b6a22 chore(deps): update dependency vite to v4.4.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-08 00:06:23 +00:00
renovate d88ff594e1 fix(deps): update dependency marked to v5.1.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-07 15:04:29 +00:00
kolaente 66f0df0333
chore: release preparation
continuous-integration/drone/push Build is passing Details
2023-07-07 13:38:53 +02:00
kolaente b742c55287
fix(projects): update project duplicate api definitions
continuous-integration/drone/push Build is passing Details
2023-07-07 13:00:48 +02:00
kolaente 82c9a91d39
fix(project): don't try to read title of undefined project
continuous-integration/drone/push Build is passing Details
2023-07-07 12:43:40 +02:00
kolaente cd820a6cb2
fix(project): duplicate a project without new parent
continuous-integration/drone/push Build is passing Details
2023-07-07 12:42:26 +02:00
kolaente 2c4da79c1b
fix(task): make an attachment cover image
continuous-integration/drone/push Build is passing Details
2023-07-07 12:36:27 +02:00
renovate c4f6465569 fix(deps): update dependency @intlify/unplugin-vue-i18n to v0.12.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-07 08:04:45 +00:00
renovate 7d84601f6d chore(deps): update dev-dependencies
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-07 06:04:40 +00:00
renovate 8f94b7490c fix(deps): update dependency vue-router to v4.2.4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-06 17:04:48 +00:00
renovate cbce7cd142 chore(deps): update dev-dependencies
continuous-integration/drone/push Build is passing Details
2023-07-06 05:12:56 +00:00
renovate 28e775be42 chore(deps): update dev-dependencies
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-07-06 02:04:50 +00:00
kolaente 24ad2f892d
feat: improve handling of an invalid api url
continuous-integration/drone/push Build is failing Details
Resolves #1964
2023-07-05 17:46:52 +02:00
kolaente 7c1934aad0
fix: don't try to set config from non-json responses
continuous-integration/drone/push Build is passing Details
2023-07-05 17:37:07 +02:00
kolaente ae2b0f97c4
fix: make update available button use the correct text color all the time
continuous-integration/drone/push Build is passing Details
2023-07-05 17:26:13 +02:00
renovate 70e0696300 fix(deps): update dependency vue-router to v4.2.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-05 14:05:11 +00:00
Frederick [Bot] 60e49468cf [skip ci] Updated translations via Crowdin 2023-07-05 00:09:35 +00:00
renovate 10e566164e chore(deps): update dev-dependencies
continuous-integration/drone/push Build is passing Details
2023-07-04 06:13:08 +00:00
renovate 3c1b54c1a3 chore(deps): update dev-dependencies
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-04 00:05:54 +00:00
renovate b80c6cf326 chore(deps): update pnpm to v8.6.6
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-03 14:04:18 +00:00
kolaente d2e6ab4505
chore(deps): update all dev dependencies at once per day
continuous-integration/drone/push Build is passing Details
2023-07-03 11:59:56 +02:00
renovate 32ed4c7da9 chore(deps): update dependency vitest to v0.32.3
continuous-integration/drone/push Build is passing Details
2023-07-03 09:48:54 +00:00
renovate 70ae19a903 chore(deps): update dependency css-has-pseudo to v6
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-03 09:05:03 +00:00
renovate cc2e0e79d3 chore(deps): update dependency caniuse-lite to v1.0.30001511
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-03 03:04:36 +00:00
Frederick [Bot] 49bdd00133 [skip ci] Updated translations via Crowdin 2023-07-02 00:28:57 +00:00
renovate 43eb742352 fix(deps): update dependency @intlify/unplugin-vue-i18n to v0.12.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-01 18:04:39 +00:00
renovate 34b6692f25 chore(deps): update dependency esbuild to v0.18.11
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-07-01 07:04:37 +00:00
renovate 152aefd365 chore(deps): update dependency eslint to v8.44.0
continuous-integration/drone/push Build is passing Details
2023-07-01 04:37:27 +00:00
renovate 2297872879 fix(deps): update dependency dayjs to v1.11.9
continuous-integration/drone/push Build is passing Details
2023-07-01 04:37:17 +00:00
renovate 75ca7ecd61 chore(deps): update dependency @types/node to v18.16.19
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-07-01 01:04:56 +00:00
Frederick [Bot] 0909d2cfe5 [skip ci] Updated translations via Crowdin 2023-07-01 00:28:02 +00:00
renovate a60662b72b chore(deps): update dependency rollup to v3.26.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-30 05:04:32 +00:00
Frederick [Bot] 478b2c043e [skip ci] Updated translations via Crowdin 2023-06-30 00:28:59 +00:00
renovate 6bc54d7a92 fix(deps): update dependency dompurify to v3.0.4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-29 17:04:46 +00:00
renovate e8e664b256 chore(deps): update dependency typescript to v5.1.6
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-29 01:04:35 +00:00
Frederick [Bot] 72fd932020 [skip ci] Updated translations via Crowdin 2023-06-29 00:27:59 +00:00
renovate e5090b117f fix(deps): update sentry-javascript monorepo to v7.57.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-28 16:04:38 +00:00
renovate 1b498a238c fix(deps): update dependency floating-vue to v2.0.0-beta.24
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-28 14:04:36 +00:00
kolaente 2a14325f62
feat(projects): allow setting a saved filter for tasks shown on the overview page
continuous-integration/drone/push Build is passing Details
Resolves vikunja/api#1545
Resolves https://community.vikunja.io/t/customizable-overview-page/685
2023-06-28 15:20:43 +02:00
kolaente ac6c4cf2bc
fix(project): make sure the correct tasks are loaded when switching between projects
continuous-integration/drone/push Build is passing Details
Resolves https://community.vikunja.io/t/filter-table-view-not-sorting/1416/3
2023-06-28 14:41:43 +02:00
renovate 66bad4b2b1 fix(deps): update dependency @vueuse/core to v10.2.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-28 12:04:38 +00:00
renovate cdac38eabc chore(deps): update dependency vue-tsc to v1.8.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-28 10:04:55 +00:00
renovate 79918620c3 chore(deps): update dependency @tsconfig/node18 to v18
continuous-integration/drone/push Build is passing Details
2023-06-28 05:25:57 +00:00
renovate dd1ae53d00 chore(deps): update dependency typescript to v5.1.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-27 23:05:05 +00:00
renovate 9894490616 chore(deps): update dependency esbuild to v0.18.10
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-27 01:04:35 +00:00
Frederick [Bot] a218eab609 [skip ci] Updated translations via Crowdin 2023-06-27 00:29:30 +00:00
renovate c046bb95b3 chore(deps): update dependency rollup to v3.25.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-26 21:04:31 +00:00
renovate 12ad0d2ed3 chore(deps): update dependency vue-tsc to v1.8.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-26 19:04:34 +00:00
renovate 8e505b6f51 chore(deps): update typescript-eslint monorepo to v5.60.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-26 18:04:37 +00:00
renovate 3589251f55 chore(deps): update dependency cypress to v12.16.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-26 17:04:37 +00:00
renovate b3b0d8d6e6 chore(deps): update pnpm to v8.6.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-26 10:04:29 +00:00
renovate 06572e8f0a chore(deps): update dependency rollup to v3.25.2
continuous-integration/drone/push Build is passing Details
2023-06-26 06:21:16 +00:00
renovate fe7e06079e chore(deps): update dependency caniuse-lite to v1.0.30001508
continuous-integration/drone/push Build is passing Details
2023-06-26 06:19:37 +00:00
renovate c2b04a2b81 chore(deps): update pnpm to v8.6.4
continuous-integration/drone/push Build is passing Details
2023-06-26 06:19:18 +00:00
renovate d2148df6c8 chore(deps): update dependency esbuild to v0.18.9
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-26 06:04:41 +00:00
Frederick [Bot] 305c5b32ee [skip ci] Updated translations via Crowdin 2023-06-24 00:28:58 +00:00
kolaente a446310986
fix(task): break long task titles after 4 lines only
continuous-integration/drone/push Build is passing Details
Resolves https://github.com/go-vikunja/frontend/issues/84
Resolves https://community.vikunja.io/t/text-wrap-on-lists/1441
2023-06-23 14:28:06 +02:00
kolaente 8a22d1811e
fix(link share): default share view should be list, not project
continuous-integration/drone/push Build is passing Details
2023-06-23 14:18:52 +02:00
Frederick [Bot] d34a872d40 [skip ci] Updated translations via Crowdin 2023-06-23 00:28:56 +00:00
kolaente 06126de139
fix(i18n): typo
continuous-integration/drone/push Build is passing Details
2023-06-22 06:14:18 +02:00
renovate 047435bb68 chore(deps): update dependency sass to v1.63.6
continuous-integration/drone/push Build is passing Details
2023-06-22 03:35:29 +00:00
renovate 4be631888a chore(deps): update dependency @vue/test-utils to v2.4.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-22 01:06:29 +00:00
Frederick [Bot] be11397163 [skip ci] Updated translations via Crowdin 2023-06-22 00:29:37 +00:00
kolaente f3986c710b
feat(i18n): use chinese name for chinese translation
continuous-integration/drone/push Build is failing Details
2023-06-21 20:16:03 +02:00
kolaente 9c8266fb0d
feat(i18n): enable Japanese translation 2023-06-21 20:15:48 +02:00
kolaente 7148b56eea
feat(i18n): enable Danish translation 2023-06-21 20:14:21 +02:00
kolaente ff6645d2ab
feat(i18n): enable Spanish translation
continuous-integration/drone/push Build is passing Details
2023-06-21 20:12:56 +02:00
renovate bd17afe466 chore(deps): update dependency @vitejs/plugin-legacy to v4.0.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-21 14:05:23 +00:00
kolaente 25bd26bea6
chore(editor): disable deprecated marked options
continuous-integration/drone/push Build is passing Details
2023-06-21 15:12:45 +02:00
kolaente 83c0ef4e8b
fix(project): set maxRight on projects after opening a task
continuous-integration/drone/push Build is failing Details
2023-06-21 15:09:31 +02:00
renovate f55c42f124 chore(deps): update dependency postcss-preset-env to v8.5.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-21 08:13:39 +00:00
renovate 4189fdadd9 chore(deps): update dependency esbuild to v0.18.6
continuous-integration/drone/push Build is passing Details
2023-06-21 05:43:09 +00:00
renovate 2670cecf70 chore(deps): update dependency sass to v1.63.5
continuous-integration/drone/push Build is passing Details
2023-06-21 05:42:25 +00:00
renovate e54b5e88cc chore(deps): update node.js to v18.16.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-06-20 21:03:45 +00:00
renovate 03c6e343c2 chore(deps): update dependency cypress to v12.15.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-20 19:05:45 +00:00
renovate 9acde8d017 fix(deps): update dependency pinia to v2.1.4
continuous-integration/drone/push Build is passing Details
2023-06-20 14:48:20 +00:00
kolaente 813d2b56a0
fix: don't try to map non-array data
continuous-integration/drone/push Build is passing Details
2023-06-20 16:21:45 +02:00
kolaente 1005182a50
feat(reminders): add e2e tests for task reminders
continuous-integration/drone/push Build is passing Details
2023-06-20 15:56:39 +02:00
kolaente 4eba9479b0
feat(link share): add e2e tests for link share hash 2023-06-20 15:56:13 +02:00
kolaente dbe1ad9353
fix: set and use correct type for destructured props
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-20 15:24:02 +02:00
kolaente b6cd424aa3
fix: set vue-ignore 2023-06-20 15:22:19 +02:00
kolaente 6651adf6de
chore(deps): update lockfile
continuous-integration/drone/pr Build was killed Details
2023-06-20 14:41:40 +02:00
kolaente 3aa502e07d
fix: use props destructuring everywhere 2023-06-20 14:40:41 +02:00
kolaente 78a268ab07
fix(building): let the compiler ignore props interface 2023-06-20 14:40:41 +02:00
renovate 4661c2e90e
fix(deps): update dependency vue to v3.3.4 2023-06-20 14:40:38 +02:00
renovate fbfa265dbf fix(deps): update dependency vue to v3.3.4
continuous-integration/drone/pr Build is failing Details
2023-06-20 10:05:53 +00:00
renovate c1ad1b0639 chore(deps): update dependency vue-tsc to v1.8.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-20 09:05:22 +00:00
kolaente c27661107f
fix(project): correctly load background when switching from or to a project view
continuous-integration/drone/push Build is passing Details
Resolves https://community.vikunja.io/t/background-does-not-load/1437
2023-06-20 10:54:13 +02:00
renovate 464cc0ed8c chore(deps): update dependency esbuild to v0.18.5
continuous-integration/drone/push Build is passing Details
2023-06-20 05:38:25 +00:00
renovate f8e2ef210f chore(deps): update typescript-eslint monorepo to v5.60.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-20 01:06:33 +00:00
Frederick [Bot] 61379ed4c6 [skip ci] Updated translations via Crowdin 2023-06-20 00:29:35 +00:00
renovate f1f99d065c fix(deps): update sentry-javascript monorepo to v7.56.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-19 15:05:55 +00:00
konrad da3eaf0d35 feat: persist link share auth rule in url hash (#3336)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3336
2023-06-19 14:26:32 +00:00
kolaente 34182b8bbb
fix: follow the happy path
continuous-integration/drone/pr Build is passing Details
2023-06-19 15:28:06 +02:00
kolaente 20660564c1
feat: change the link share hash name 2023-06-19 15:28:06 +02:00
kolaente c2ffe3a9dc
feat: check link share auth from store instead 2023-06-19 15:28:06 +02:00
kolaente a33e2f6c00
chore: follow the happy path 2023-06-19 15:28:06 +02:00
kolaente 0ce150af23
chore: move const 2023-06-19 15:28:06 +02:00
kolaente 06a1ff6f4b
chore: reduce nesting 2023-06-19 15:28:06 +02:00
kolaente 7c964c29d4
fix: return redirect 2023-06-19 15:28:06 +02:00
kolaente b9f0635d9f
feat: rename link share hash prefix 2023-06-19 15:28:05 +02:00
kolaente 61baf02e26
chore: import const instead of redeclaring it 2023-06-19 15:28:05 +02:00
kolaente 59b05e9836
chore: rename getRedirectRoute 2023-06-19 15:28:05 +02:00
kolaente f68bb2625e
feat: persist link share auth rule in url hash
This allows sharing links to a task directly. We're using hashes instead
of query parameters because hash values are usually not logged in access
logs.

With this change, when a user uses a link share, the link share hash
will be appended to all urls while browsing. When a link share hash is
encountered in the current url and the user is not authenticated, they
will be redirected to the link share auth page, get authenticated and
then get redirected to whatever url they were previously on.
2023-06-19 15:28:05 +02:00
renovate 25f9f5dceb chore(deps): update pnpm to v8.6.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-19 13:05:05 +00:00
renovate 35d68619f4 chore(deps): update caniuse-and-related
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-19 00:07:04 +00:00
kolaente 929d4f4023
chore: catch error when trying to play pop sound
continuous-integration/drone/push Build is passing Details
Safari does not allow playing sound without user interaction, so we'll just silently catch and ignore the error until we have a better solution.
2023-06-18 18:58:57 +02:00
kolaente a92eb31ab3
fix(settings): don't try to sort timezones if there are none
continuous-integration/drone/push Build is passing Details
2023-06-18 18:53:01 +02:00
kolaente 2006abd0a6
fix(task): call getting task identifier directly instead of using model function
continuous-integration/drone/push Build is passing Details
2023-06-18 18:46:18 +02:00
renovate e4504748c4 chore(deps): update dependency vite-plugin-sentry to v1.3.0
continuous-integration/drone/push Build is passing Details
2023-06-18 15:29:34 +00:00
kolaente 854228034d
chore(task): use ref for task instead of reactive
continuous-integration/drone/push Build is passing Details
2023-06-18 17:02:52 +02:00
kolaente 88ce29aa77
fix(sentry): use correct environment from vite env mode
continuous-integration/drone/push Build is passing Details
2023-06-18 16:26:23 +02:00
kolaente 0ca1b3a7f5
fix(sentry): don't fail the build when sentry upload fails
continuous-integration/drone/push Build is passing Details
2023-06-18 16:22:49 +02:00
kolaente a118580704
fix: don't try to map data from empty responses
continuous-integration/drone/push Build is passing Details
2023-06-18 16:10:46 +02:00
kolaente 771aad5420
chore(sentry): ignore missing commits
continuous-integration/drone/push Build is passing Details
2023-06-18 16:04:42 +02:00
kolaente 411ae58e59
chore(ci): sign drone config
continuous-integration/drone/push Build is passing Details
2023-06-18 15:44:16 +02:00
kolaente c3501f5060
chore(sentry): remove debug options 2023-06-18 15:41:49 +02:00
konrad 5ca31d00ee feat: add vite-plugin sentry (#1991)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #1991
2023-06-18 13:40:21 +00:00
kolaente 1e2039325f
chore(ci): sign drone config
continuous-integration/drone/pr Build is passing Details
2023-06-18 15:23:56 +02:00
kolaente 472cca8ab8
chore(sentry): remove sourcemaps after upload via plugin
continuous-integration/drone/pr Build is pending Details
2023-06-18 15:12:29 +02:00
kolaente 2fad45e016
chore(sentry): use correct chunks option
continuous-integration/drone/pr Build is pending Details
2023-06-18 15:05:10 +02:00
kolaente 68a137acf9
chore(sentry): only load sentry when enabled 2023-06-18 15:01:49 +02:00
kolaente 95ba8b8a11
chore(sentry): alwys use the same version 2023-06-18 15:00:00 +02:00
kolaente 96c9407414
feat(sentry): only load sentry when it's enabled 2023-06-18 14:49:25 +02:00
kolaente 653415e764
chore(deps): install dependencies after rebase
continuous-integration/drone/pr Build is pending Details
2023-06-18 14:38:30 +02:00
Dominik Pschenitschni 73947f0ba4
feat: add vite-plugin sentry 2023-06-18 14:33:21 +02:00
renovate 389ca1b692 chore(deps): update dependency vue-tsc to v1.8.0
continuous-integration/drone/push Build is failing Details
2023-06-18 08:30:20 +00:00
renovate 9c0e140e2e chore(deps): update dependency eslint to v8.43.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-16 22:05:48 +00:00
renovate 51d08a1637 fix(deps): update dependency floating-vue to v2.0.0-beta.22
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-16 19:05:33 +00:00
kolaente 35de8a40d8
fix(docker): copy patches prior to installing dependencies so that the installation actually works
continuous-integration/drone/push Build is passing Details
2023-06-16 20:35:08 +02:00
kolaente 80772f7578
fix(ci): directly build docker images and not use releases to avoid caching issues
continuous-integration/drone/push Build is failing Details
2023-06-16 20:07:43 +02:00
kolaente faa62985df
fix: correctly sync filters on upcoming tasks page
continuous-integration/drone/push Build is passing Details
Resolves #3600
2023-06-16 19:49:43 +02:00
kolaente 154d43a392
fix(reminders): don't assigne the task
continuous-integration/drone/push Build is passing Details
2023-06-16 19:42:55 +02:00
renovate 7fe5565654 chore(deps): update dependency vitest to v0.32.2
continuous-integration/drone/push Build is passing Details
2023-06-16 17:21:20 +00:00
kolaente 1fcd1cdd4b
fix(reminders): assignment to const when changing a reminder
continuous-integration/drone/push Build is passing Details
2023-06-16 19:20:40 +02:00
kolaente ba057f3527
feat(reminders): add preset two hours before due / start / end date 2023-06-16 19:12:07 +02:00
kolaente dd7b77e12d
feat(reminders): add on the due / start / end date as a reminder preset 2023-06-16 19:12:06 +02:00
renovate 3845a45934 chore(deps): update dependency vitest to v0.32.1
continuous-integration/drone/push Build is passing Details
2023-06-16 16:41:52 +00:00
renovate 564808bd35 fix(deps): update dependency @vueuse/core to v10.2.0
continuous-integration/drone/push Build is passing Details
2023-06-16 16:41:30 +00:00
renovate c0a66e4746 fix(deps): update dependency floating-vue to v2.0.0-beta.21
continuous-integration/drone/push Build is passing Details
2023-06-16 16:41:08 +00:00
renovate 28242cfb23 chore(deps): update dependency esbuild to v0.18.4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-16 16:05:32 +00:00
renovate 818fb2b524 chore(deps): update dependency esbuild to v0.18.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-16 01:05:45 +00:00
Frederick [Bot] ad95bdd039 [skip ci] Updated translations via Crowdin 2023-06-16 00:29:42 +00:00
renovate 3faed19298 chore(deps): update dependency @rushstack/eslint-patch to v1.3.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-15 01:05:19 +00:00
renovate 9114a86813 chore(deps): update dependency postcss-preset-env to v8.5.0
continuous-integration/drone/push Build is passing Details
2023-06-14 21:21:31 +00:00
renovate bae9a5c9be fix(deps): update sentry-javascript monorepo to v7.55.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build encountered an error Details
2023-06-14 15:05:21 +00:00
renovate fe2d6d4467 chore(deps): update dependency sass to v1.63.4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-14 01:05:12 +00:00
Frederick [Bot] 96acea90ed [skip ci] Updated translations via Crowdin 2023-06-14 00:29:44 +00:00
renovate 21c98d5166 chore(deps): update dependency histoire to v0.16.2
continuous-integration/drone/push Build is passing Details
2023-06-13 17:14:09 +00:00
renovate b3cb36c1e1 fix(deps): update sentry-javascript monorepo to v7.55.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-06-13 15:06:33 +00:00
kolaente 79ceaf6a2b
fix(task): repeat mode now saves correctly
continuous-integration/drone/push Build is passing Details
2023-06-13 12:33:35 +02:00
kolaente 5694b39489
feat(reminders): show resolved reminder time in a tooltip and properly bubble updated task down to the reminder component
continuous-integration/drone/push Build is passing Details
2023-06-13 12:30:07 +02:00
kolaente 32e5f9f757
fix(reminders): don't sync negative relative reminder amounts in ui
continuous-integration/drone/push Build is passing Details
2023-06-13 12:10:10 +02:00
kolaente 928b338cf2
fix(reminders): don't assume 30 days are always a month
continuous-integration/drone/push Build is passing Details
2023-06-13 12:06:00 +02:00
kolaente 1a792e0667
feat(reminders): only show relative reminders when there's a date to relate them to
continuous-integration/drone/push Build is passing Details
2023-06-13 12:03:28 +02:00
renovate 6407644138 chore(deps): update dependency esbuild to v0.18.2
continuous-integration/drone/push Build is failing Details
2023-06-13 05:54:06 +00:00
renovate 2db88b583b chore(deps): update dependency @types/node to v18.16.18
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-13 03:05:22 +00:00
kolaente bef25c49d5
feat: new image for the unauthenticated views
continuous-integration/drone/push Build is passing Details
There have been so many big changes lately, I think it's time for a new image.
2023-06-12 21:58:17 +02:00
renovate f01ea20a38 chore(deps): update typescript-eslint monorepo to v5.59.11
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-12 18:06:30 +00:00
kolaente 3c9083b90d
feat: add message to add to home screen on mobile
continuous-integration/drone/push Build is passing Details
2023-06-12 19:37:58 +02:00
konrad 169feaaf0f feat(user): persist frontend settings in the api (#3594)
continuous-integration/drone/push Build is passing Details
Implements saving of frontend settings for 04e2c51fac.

Resolves https://github.com/go-vikunja/frontend/issues/105
Resolves vikunja/api#1250
Resolves vikunja/api#1452
Reviewed-on: #3594
2023-06-12 16:22:51 +00:00
kolaente 5d59392566
fix: lint
continuous-integration/drone/pr Build is passing Details
2023-06-12 18:06:07 +02:00
kolaente 6593380013
fix: load the correct language
continuous-integration/drone/pr Build is failing Details
2023-06-12 18:01:56 +02:00
kolaente 69e94e58c4
fix: tests
continuous-integration/drone/pr Build was killed Details
2023-06-12 16:35:47 +02:00
kolaente cd8e497b24
fix(user): lint
continuous-integration/drone/pr Build is failing Details
2023-06-12 16:22:15 +02:00
kolaente aab2020e68
chore(user): cleanup 2023-06-12 16:20:46 +02:00
kolaente a050419fdf
fix(user): set the language when saving 2023-06-12 16:19:47 +02:00
kolaente f0c3980700
fix(user): fix flickering of default settings 2023-06-12 16:18:01 +02:00
kolaente 68597c9709
feat(user): use user language from store after logging in 2023-06-12 16:08:31 +02:00
kolaente 5325f6d7d9
feat(user): migrate color scheme settings to persistance in db 2023-06-12 15:57:18 +02:00
renovate 8c687350a0 chore(deps): update dependency esbuild to v0.18.1
continuous-integration/drone/push Build is passing Details
2023-06-12 06:45:04 +00:00
renovate bac679caf7 chore(deps): update dependency rollup to v3.25.1
continuous-integration/drone/push Build is passing Details
2023-06-12 06:42:03 +00:00
renovate 4f8ff17138 chore(deps): update dependency caniuse-lite to v1.0.30001500
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-12 06:05:17 +00:00
Frederick [Bot] 83e7138a18 [skip ci] Updated translations via Crowdin 2023-06-12 00:28:51 +00:00
renovate 8e44b87d07 chore(deps): update pnpm to v8.6.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-11 20:04:39 +00:00
kolaente 4b0022664a
feat(user): set default settings when loading persisted 2023-06-11 17:43:55 +02:00
kolaente d8ad934643
feat(user): save quick add magic mode in api 2023-06-11 17:37:49 +02:00
kolaente 77ee1bfc3e
feat(user): migrate pop sound setting to store in api 2023-06-11 17:31:04 +02:00
renovate 8728647f00 chore(deps): update dependency vite-plugin-pwa to v0.16.4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-11 12:05:13 +00:00
kolaente bd7d09c17c
fix(repeat): prevent disappearing repeat mode settings when modes other than default repeat mode were selected
continuous-integration/drone/push Build is passing Details
Resolves #3585
2023-06-11 09:48:54 +02:00
renovate 77bedbd1cf chore(deps): update dependency rollup to v3.25.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-11 06:05:08 +00:00
renovate 2773612420 chore(deps): update dependency rollup-plugin-visualizer to v5.9.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-10 19:05:24 +00:00
kolaente 48cfdddff7
chore(reminders): remove reminderDates property
continuous-integration/drone/push Build is passing Details
2023-06-10 19:05:22 +02:00
konrad 3f8e457d52 feat: edit relative reminders (#3248)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3248
2023-06-10 17:04:09 +00:00
kolaente 098b5fa2b1
fix(reminders): properly parse relative reminders which don't have an amount
continuous-integration/drone/pr Build is passing Details
2023-06-10 18:54:39 +02:00
kolaente 5e4eb4a728
fix(reminders): custom relative highlight now only when a custom relative reminder was actually selected 2023-06-10 18:54:10 +02:00
kolaente 8930f61548
fix(reminders): flatpickr styling improvements 2023-06-10 18:46:15 +02:00
kolaente 9a736cf65f
fix(reminders): style flatpickr so that it blends in more
continuous-integration/drone/pr Build is passing Details
2023-06-10 18:39:33 +02:00
kolaente 2677f6254d
feat(reminders): highlight which preset or custom date is selected
continuous-integration/drone/pr Build is passing Details
2023-06-10 17:35:50 +02:00
kolaente bfcb36e093
fix(reminders): align remove icon with the rest
continuous-integration/drone/pr Build is passing Details
2023-06-10 17:29:30 +02:00
kolaente 9ec29cad30
fix: lint
continuous-integration/drone/pr Build is passing Details
2023-06-10 17:26:06 +02:00
renovate c4f609a0c8 chore(deps): update dependency esbuild to v0.18.0
continuous-integration/drone/push Build is passing Details
2023-06-10 15:00:24 +00:00
renovate 7e7535b860 chore(deps): update dependency @types/node to v18.16.17
continuous-integration/drone/push Build is passing Details
2023-06-10 15:00:05 +00:00
renovate df9181b34e chore(deps): update dependency rollup to v3.24.1
continuous-integration/drone/push Build is passing Details
2023-06-10 14:59:45 +00:00
renovate e6a56f2822 fix(deps): update dependency marked to v5.1.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-10 04:06:05 +00:00
renovate 3633d68269 chore(deps): update dependency sass to v1.63.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-09 16:05:26 +00:00
kolaente dd3a5fe6b5
fix(reminders): duplicate reminder for each change
continuous-integration/drone/pr Build is failing Details
2023-06-09 14:46:34 +02:00
kolaente 04642ae1ec
fix(reminders): set date over relative reminder
continuous-integration/drone/pr Build is failing Details
2023-06-09 14:43:02 +02:00
kolaente eac19e28d6
feat(reminders): add more spacing
continuous-integration/drone/pr Build is failing Details
2023-06-09 14:39:35 +02:00
kolaente 11f94e4037
feat(reminders): make adding new reminders less confusing
continuous-integration/drone/pr Build is failing Details
2023-06-09 14:37:26 +02:00
kolaente 39cc7a00d8
feat(reminders): make relative presets actually work
continuous-integration/drone/pr Build is failing Details
2023-06-09 14:31:32 +02:00
kolaente 02da1e171e
feat(reminders): add confirm button 2023-06-09 14:27:11 +02:00
kolaente ae177c73ea
feat(reminders): move reminder settings to a popup 2023-06-09 14:23:32 +02:00
kolaente e6c4c18974
feat(reminders): translate all reminder form strings
continuous-integration/drone/pr Build is failing Details
2023-06-09 13:54:17 +02:00
kolaente 95487d7569
feat(reminders): add proper time picker for relative dates
continuous-integration/drone/pr Build is failing Details
2023-06-09 13:19:47 +02:00
kolaente 7b2a688b6e
feat(datepicker): separate datepicker popup and datepicker logic in different components
continuous-integration/drone/pr Build is failing Details
2023-06-09 12:07:23 +02:00
Frederick [Bot] f5b3b21ce0 [skip ci] Updated translations via Crowdin 2023-06-09 00:29:38 +00:00
kolaente 979561342a
fix(kanban): decrease task count per bucket when deleting a task
continuous-integration/drone/push Build is passing Details
2023-06-08 17:09:48 +02:00
kolaente ad27f588a2
feat(kanban): use total task count from the api instead of manually calculating it per bucket
continuous-integration/drone/push Build is passing Details
This fixes an ux issue where the total count would show a wrong number of total tasks because that was the number of tasks which were loaded at the time. In combination with bucket limits, this caused error messages when the user would attempt to drag tasks into a bucket which appeared not full but was.
2023-06-08 16:57:58 +02:00
kolaente c7a989d7dc
fix(kanban): don't export buckets as readonly because that makes it impossible to update them, even from within the store
continuous-integration/drone/push Build is passing Details
This fixes a bug where the task on the kanban board would not get updated because the "tasks" property of all buckets were still read only if they were exported once as readonly. This has been unnoticed in the past because the visual representation of the board still perfectly matched what the user was doing and what was saved in the api - just not what was stored in pina.
2023-06-08 16:54:52 +02:00
renovate 0e674d8300 chore(deps): update dependency @rushstack/eslint-patch to v1.3.1
continuous-integration/drone/push Build is passing Details
2023-06-08 06:45:00 +00:00
renovate 121fd70235 chore(deps): update dependency sass to v1.63.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-08 02:06:22 +00:00
renovate d4cd90da45 chore(deps): update dependency sass to v1.63.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-07 21:05:13 +00:00
kolaente c74612f24a
fix: lint
continuous-integration/drone/push Build is passing Details
2023-06-07 21:53:40 +02:00
kolaente 64f9f4fd88
fix: disable autocomplete in assignee search
continuous-integration/drone/push Build is failing Details
Resolves https://github.com/go-vikunja/frontend/issues/114
2023-06-07 21:46:18 +02:00
kolaente b50adaf4b5
fix(navigation): highlight saved filters in project view and prevent them from being dragged around
continuous-integration/drone/push Build is passing Details
2023-06-07 20:58:49 +02:00
renovate 7b92028e67 chore(deps): update dependency cypress to v12.14.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-07 17:05:24 +00:00
renovate 08d84f7994 chore(deps): update dependency rollup to v3.24.0
continuous-integration/drone/push Build is passing Details
2023-06-07 06:51:59 +00:00
renovate f95b138b9f fix(deps): update dependency marked to v5.0.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-07 05:05:20 +00:00
Frederick [Bot] e6aecbd8dc [skip ci] Updated translations via Crowdin 2023-06-07 00:29:36 +00:00
renovate eab0600f63 chore(deps): update dependency vitest to v0.32.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-06 18:05:23 +00:00
kolaente 46f5dcb4dc
fix(navigation): show text ellipsis for very long project titles
continuous-integration/drone/push Build is passing Details
2023-06-06 17:33:55 +02:00
kolaente 0dc7e83dc4
fix(navigation): menu item overflow 2023-06-06 17:32:18 +02:00
kolaente 82c10b87c8
fix(navigation): hide archived subprojects
continuous-integration/drone/push Build is passing Details
2023-06-06 17:29:08 +02:00
kolaente 5888946861
fix(navigation): sidebar top spacing
continuous-integration/drone/push Build is passing Details
2023-06-06 17:27:05 +02:00
renovate e24607ed3a chore(deps): update dependency @types/codemirror to v5.60.8
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-06 02:05:16 +00:00
Frederick [Bot] d1ae6a8b84 [skip ci] Updated translations via Crowdin 2023-06-06 00:29:33 +00:00
renovate fc052cf8f5 chore(deps): update typescript-eslint monorepo to v5.59.9
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-05 18:05:53 +00:00
konrad d9f608e8b4 feat: improve user assignments via quick add magic (#3348)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3348
2023-06-05 15:03:14 +00:00
konrad a988565227 feat: hide quick add magic help behind a tooltip (#3353)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3353
2023-06-05 15:02:42 +00:00
Dominik Pschenitschni b76fffb788 Update 'src/i18n/lang/en.json'
continuous-integration/drone/push Build is passing Details
2023-06-05 14:31:30 +00:00
kolaente 25c3b7bcbf chore: move styles to components
continuous-integration/drone/pr Build is passing Details
2023-06-05 14:20:41 +00:00
kolaente dfa6cd777b fix: add more padding to the textarea 2023-06-05 14:20:41 +00:00
kolaente 21ad8301f2 chore: add comment on overriding 2023-06-05 14:20:41 +00:00
kolaente 7110c9a5ce chore: move positioning css 2023-06-05 14:20:41 +00:00
kolaente a4c8fccb11 chore: remove nesting 2023-06-05 14:20:41 +00:00
Dominik Pschenitschni c294f9d28d fix: lint 2023-06-05 14:20:41 +00:00
kolaente 422d7fc693 feat: highlight hint icon when hovering the input 2023-06-05 14:20:41 +00:00
kolaente abb5128426 feat: add transition to input icons 2023-06-05 14:20:41 +00:00
kolaente 2174608801 fix: improve tooltip text 2023-06-05 14:20:41 +00:00
kolaente a6cdf6c4bd fix: improve tooltip icon contrast 2023-06-05 14:20:41 +00:00
kolaente 2c9693a83e chore: remove wrapper div 2023-06-05 14:20:41 +00:00
kolaente 6989558963 feat: move quick add magic to a popup behind an icon 2023-06-05 14:20:41 +00:00
kolaente 7fb85dacec feat: allow hiding the quick add magic help tooltip with a button 2023-06-05 14:20:41 +00:00
steffeydev 57218d1454 fix: allow icon changes configuration via env (#3567)
continuous-integration/drone/push Build is passing Details
`window.ALLOW_ICON_CHANGES` needs to be written as a boolean during the docker deploy instead of a string. The strings `"true"` and `"false"` both evaluate to `true` in JS, so we need to use the boolean `true` and `false` for the assertion in `Logo.vue` to be meaningful.

Co-authored-by: SteffeyDev <steffeydev@icloud.com>
Reviewed-on: #3567
Reviewed-by: konrad <k@knt.li>
Co-authored-by: steffeydev <steffeydev@icloud.com>
Co-committed-by: steffeydev <steffeydev@icloud.com>
2023-06-05 14:19:55 +00:00
kolaente 9df6950d1a
feat: start adding relative reminder picker with more options
continuous-integration/drone/pr Build is failing Details
2023-06-05 16:16:10 +02:00
kolaente cd2b7fe185 fix: lint
continuous-integration/drone/pr Build is passing Details
2023-06-05 14:09:19 +00:00
kolaente 52987060b1 chore: group return parameter 2023-06-05 14:09:19 +00:00
kolaente aeb73a374f chore: make fuzzy matching a paramater 2023-06-05 14:09:19 +00:00
kolaente bc416f282f fix: make type singular 2023-06-05 14:09:19 +00:00
kolaente f88c373742 chore(i18n): clarify translation string 2023-06-05 14:09:19 +00:00
kolaente 10ac1ff66a chore: use startsWith for prefix matching 2023-06-05 14:09:19 +00:00
kolaente ae025e30c6 fix: clarify user search setting 2023-06-05 14:09:19 +00:00
kolaente a1dd1d6664 chore: remove user margin from the component 2023-06-05 14:09:19 +00:00
kolaente 57c64bbf71 chore: remove user margin from the component 2023-06-05 14:09:19 +00:00
kolaente 218a19d907 feat(quick add magic): allow fuzzy matching of assignees when the api results are unambigous 2023-06-05 14:09:19 +00:00
kolaente 7b6a13dd52 fix: ensure all matched quick add magic parts are correctly removed from the task 2023-06-05 14:09:19 +00:00
kolaente 4ff0c81e37 fix: lint 2023-06-05 14:09:19 +00:00
kolaente 6a15489610 feat(assignees): show user avatar in search results 2023-06-05 14:09:19 +00:00
kolaente 59c942af73 feat: show initial list of users when opening the assignees view 2023-06-05 14:09:19 +00:00
kolaente 302ba2bec7 chore: clarify users when can still be found even if they disabled it 2023-06-05 14:09:19 +00:00
kolaente 34d1e4bddd fix(quick add magic): cleanup all assignee properties 2023-06-05 14:09:19 +00:00
kolaente 02c24a4814 fix(quick add magic): use the project user service to find assignees for quick add magic 2023-06-05 14:09:19 +00:00
kolaente 0724776ccb fix(quick add magic): don't replace the prefix in every occurrence when it is present in the matched part 2023-06-05 14:09:19 +00:00
renovate 11979cbee0 chore(deps): update caniuse-and-related
continuous-integration/drone/push Build is passing Details
2023-06-05 13:24:09 +00:00
renovate 2a490bf8ef chore(deps): update dependency happy-dom to v9.20.3
continuous-integration/drone/push Build is passing Details
2023-06-05 13:23:57 +00:00
renovate b5d3d1a7b7 chore(deps): update pnpm to v8.6.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-05 13:04:47 +00:00
renovate 554ffe3b9d chore(deps): update dependency rollup to v3.23.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-04 19:08:49 +00:00
renovate 0f57be107b chore(deps): update dependency vite-plugin-pwa to v0.16.3
continuous-integration/drone/push Build is passing Details
2023-06-03 09:33:26 +00:00
renovate 269aa6b426 chore(deps): update dependency eslint to v8.42.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-03 00:05:32 +00:00
renovate b316b8f2ba chore(deps): update dependency vite-plugin-pwa to v0.16.1
continuous-integration/drone/push Build is passing Details
2023-06-02 13:20:52 +00:00
renovate cad68e269c fix(deps): update dependency dayjs to v1.11.8
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-02 13:05:16 +00:00
kolaente efb3407b87
feat: allow disabling icon changes
continuous-integration/drone/push Build is passing Details
2023-06-02 13:51:47 +02:00
renovate 6f1ff02c04 chore(deps): update dependency vitest to v0.31.4
continuous-integration/drone/push Build is passing Details
2023-06-02 11:40:07 +00:00
renovate 93c66b0613 chore(deps): update dependency @4tw/cypress-drag-drop to v2.2.4
continuous-integration/drone/push Build is passing Details
2023-06-02 11:39:31 +00:00
renovate c14644a300 chore(deps): update dependency postcss-preset-env to v8.4.2
continuous-integration/drone/push Build is passing Details
2023-06-02 11:39:20 +00:00
renovate 02d2300608 fix(deps): update sentry-javascript monorepo to v7.54.0
continuous-integration/drone/push Build is passing Details
2023-06-02 11:38:49 +00:00
renovate ff918608c5 chore(deps): update dependency typescript to v5.1.3
continuous-integration/drone/push Build is passing Details
2023-06-02 11:38:39 +00:00
renovate aa591ee2ed chore(deps): update workbox monorepo to v7 (major) (#3556)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3556
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-06-02 11:03:36 +00:00
kolaente f4a7943680
fix: bubble changes from the editor immediately and move the delay to callers
continuous-integration/drone/push Build is passing Details
This gives the callers more control over when to save data and show/hide additional controls based on the input text
2023-06-02 12:40:21 +02:00
kolaente 68fd4698ac
fix: don't try to set a user language if none is saved 2023-06-02 11:43:42 +02:00
Frederick [Bot] dd039f31fe [skip ci] Updated translations via Crowdin 2023-06-01 00:28:46 +00:00
kolaente 6c2dc483a2
fix: redundant ) 2023-05-31 16:27:20 +02:00
Dominik Pschenitschni 811254e6a9
wip: base review
continuous-integration/drone/pr Build is failing Details
(cherry picked from commit 3bb64f078cf333f174d247dc404355f2c8437cfd)
2023-05-31 16:25:33 +02:00
cernst 85ffed4d9a
fix: review findings 2023-05-31 16:25:33 +02:00
cernst 5fb45afb12
fix: review findings 2023-05-31 16:25:33 +02:00
cernst fb14eca634
fix: review findings 2023-05-31 16:25:32 +02:00
cernst 14e2698833
feat: edit relative reminders 2023-05-31 16:25:32 +02:00
cernst 0d6c0c8399
chore; extract code to reminder-period.vue 2023-05-31 16:25:32 +02:00
cernst 5d38b8327f
feat: allow to edit existing relative reminders 2023-05-31 16:25:32 +02:00
cernst f747d5b2fc
feat: Use new Reminders API instead of reminder_dates 2023-05-31 16:25:30 +02:00
kolaente 8a75790453
chore: remove triggered notifications as it's not supported anywhere
continuous-integration/drone/push Build encountered an error Details
2023-05-31 15:21:09 +02:00
kolaente acb212ab24
feat: set the current language to the one saved by the user on login 2023-05-31 15:17:54 +02:00
kolaente 4ba02ebbb6
fix: don't try to convert a null date
continuous-integration/drone/push Build is passing Details
Resolves #3371
2023-05-31 15:07:23 +02:00
kolaente 244da46e38
fix(navigation): nav item width for items without sub projects
continuous-integration/drone/push Build is passing Details
2023-05-31 14:37:57 +02:00
kolaente f40035dc79
chore: update nix flake
continuous-integration/drone/push Build is passing Details
2023-05-31 13:44:14 +02:00
renovate 5f71e406fc fix(deps): update dependency marked to v5.0.4
continuous-integration/drone/push Build is passing Details
2023-05-31 05:15:46 +00:00
Frederick [Bot] 3d11a4f03a [skip ci] Updated translations via Crowdin 2023-05-31 00:30:36 +00:00
renovate 1dfd2dc4b7 fix(deps): update dependency vue-router to v4.2.2
continuous-integration/drone/push Build is passing Details
2023-05-30 22:46:00 +00:00
renovate e9701660d3 chore(deps): update dependency vite-plugin-pwa to v0.15.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-30 21:04:22 +00:00
renovate c8dbb4c7ef chore(deps): update typescript-eslint monorepo to v5.59.8
continuous-integration/drone/push Build is passing Details
2023-05-30 20:31:05 +00:00
renovate 1241d90268 chore(deps): update workbox monorepo to v6.6.1 (#3553)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3553
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-05-30 20:30:46 +00:00
renovate 3de5b65977 chore(deps): update dependency vitest to v0.31.2
continuous-integration/drone/push Build is passing Details
2023-05-30 18:45:05 +00:00
renovate 4a353553c3 chore(deps): update pnpm to v8.6.0
continuous-integration/drone/push Build is passing Details
2023-05-30 18:42:51 +00:00
renovate 1240f31c0a chore(deps): update workbox monorepo to v6.6.0 (#3548)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3548
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-05-30 18:12:03 +00:00
kolaente 01ac84ce1e
fix: don't require variant prop on loading component as it already has a default one set
continuous-integration/drone/push Build is passing Details
2023-05-30 20:00:02 +02:00
kolaente 4c969f0a42
fix: don't allow creating a new label from filter view
continuous-integration/drone/push Build is passing Details
Resolves #1035
2023-05-30 19:54:01 +02:00
kolaente 8e2c76a33e
feat: optimize print view for project views
continuous-integration/drone/push Build is passing Details
2023-05-30 19:50:37 +02:00
renovate b3666ec27e chore(deps): update dependency @vitejs/plugin-legacy to v4.0.4
continuous-integration/drone/push Build is failing Details
2023-05-30 16:36:54 +00:00
renovate 2c6862c509 chore(deps): update dependency vite-plugin-pwa to v0.15.1
continuous-integration/drone/push Build is failing Details
2023-05-30 16:36:44 +00:00
renovate 9f8c43818c chore(deps): update dependency @types/node to v18.16.16
continuous-integration/drone/push Build is failing Details
2023-05-30 16:36:17 +00:00
renovate 0debca91c8 chore(deps): update dependency @faker-js/faker to v8.0.2
continuous-integration/drone/push Build is passing Details
2023-05-30 16:36:08 +00:00
renovate 7b6c9fcd24 chore(deps): update dependency postcss to v8.4.24
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-30 15:05:47 +00:00
renovate 55675bf41b fix(deps): update sentry-javascript monorepo to v7.53.1
continuous-integration/drone/push Build is failing Details
2023-05-30 14:25:03 +00:00
renovate bb24b06031 chore(deps): update dependency vite to v4.3.9
continuous-integration/drone/push Build is passing Details
2023-05-30 14:24:37 +00:00
renovate dbce0376d5 fix(deps): update dependency marked to v5.0.3
continuous-integration/drone/push Build is failing Details
2023-05-30 14:24:15 +00:00
renovate 40db144a41 fix(deps): update dependency @intlify/unplugin-vue-i18n to v0.11.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-30 11:08:12 +00:00
kolaente f7ba3bd08f
fix: increase default auto-save timeout to 5 seconds
continuous-integration/drone/push Build is failing Details
Related discussion: https://community.vikunja.io/t/task-description-constantly-saving-loosing-content/1350
2023-05-30 12:19:14 +02:00
konrad ac1d374191 feat: remove namespaces, make projects infinitely nestable (#3323)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3323
2023-05-30 10:09:39 +00:00
kolaente 391992effb
fix: missing await
continuous-integration/drone/pr Build is passing Details
2023-05-30 11:37:45 +02:00
kolaente 2e9ade11c3
fix: missing variant prop for loading component
continuous-integration/drone/pr Build was killed Details
2023-05-30 11:05:10 +02:00
kolaente f11a8c543b
fix(tests): project archived filter checkbox selector 2023-05-30 11:00:30 +02:00
kolaente e30a4452f2
fix(tests): new project input field 2023-05-30 10:57:08 +02:00
kolaente 6cc11e64ab
fix: undefined parent project when none was selected 2023-05-30 10:56:42 +02:00
kolaente 7b05ed9d3d
fix: avoid crashing browser processes during tests
continuous-integration/drone/pr Build was killed Details
2023-05-30 10:42:32 +02:00
Frederick [Bot] dba35c0107 [skip ci] Updated translations via Crowdin 2023-05-29 00:28:01 +00:00
Frederick [Bot] bfbc874b1d [skip ci] Updated translations via Crowdin 2023-05-28 00:29:34 +00:00
kolaente dbccdb239a
chore(tests): enable experimental memory managment for cypress tests
continuous-integration/drone/pr Build is failing Details
2023-05-24 18:32:23 +02:00
kolaente f13db9268a
fix: translation string 2023-05-24 17:41:14 +02:00
kolaente ed8de7e3eb
fix: lint
continuous-integration/drone/pr Build is failing Details
2023-05-24 15:54:37 +02:00
kolaente b34118485c
feat: allow creating a new project directly as a child project from another one 2023-05-24 15:54:37 +02:00
kolaente 9c3259c660
chore: don't recalculate everything 2023-05-24 15:54:37 +02:00
kolaente a3e289c06c
chore: remove type annotation for computed 2023-05-24 15:54:37 +02:00
kolaente 31b7c1f217
fix: don't set the current project when setting a project 2023-05-24 15:54:37 +02:00
kolaente c30dcff451
chore: don't show selection for parent project when no projects are available 2023-05-24 15:54:37 +02:00
kolaente 086f50d4fe
chore: re-add top menu spacing 2023-05-24 15:54:36 +02:00
kolaente 46e825820c
fix: sort in store 2023-05-24 15:54:36 +02:00
kolaente a3e2cbeb27
feat: replace color dot with handle icon on hover 2023-05-24 15:54:36 +02:00
kolaente a342ae67de
chore: use project id type 2023-05-24 15:54:36 +02:00
kolaente e4d97e0520
chore: don't set the current project to null if it's undefined already 2023-05-24 15:54:36 +02:00
kolaente b69a05689b
chore: move duplicate project logic to composable 2023-05-24 15:54:36 +02:00
kolaente 6b824a49ab
chore: redirect to new project after creating from store 2023-05-24 15:54:36 +02:00
kolaente 652db56d42
chore: remove unused code 2023-05-24 15:54:36 +02:00
kolaente afaf1846ec
chore: don't wrap a computed in another computed 2023-05-24 15:54:36 +02:00
kolaente ba452ab883
fix: move parent project handling out of useProject 2023-05-24 15:54:36 +02:00
kolaente 39f699a61a
fix: rename getParentProjects method to make it clear what it does 2023-05-24 15:54:36 +02:00
kolaente 4ab547810c
fix: return updated project instead of the old one 2023-05-24 15:54:35 +02:00
kolaente bbaddb9406
fix: remove leftovers of childIds 2023-05-24 15:54:35 +02:00
kolaente a2cc9ddc88
fix: properly determine if there are projects 2023-05-24 15:54:35 +02:00
kolaente 175e31ca62
fix: recreate project instead of editing before 2023-05-24 15:54:35 +02:00
kolaente d414b65e7d
fix: remove unnecessary fallback 2023-05-24 15:54:35 +02:00
kolaente 78158bcba5
fix: remove getProjectById and replace all usages of it 2023-05-24 15:54:35 +02:00
kolaente 9402344b7e
fix: add default for level 2023-05-24 15:54:35 +02:00
kolaente 3eca9f6180
fix: only bind child projects data down 2023-05-24 15:54:35 +02:00
kolaente 26e3d42ed5
fix: move parent project child id mutation to store 2023-05-24 15:54:35 +02:00
kolaente 6e095436e9
chore: rename flag 2023-05-24 15:54:35 +02:00
kolaente 1344026494
fix: move the collapsable placeholder to the button 2023-05-24 15:54:35 +02:00
kolaente 1a94496801
fix: bottom margin of project header 2023-05-24 15:54:34 +02:00
kolaente 48570808e5
fix: use the color bubble as handle if the project has a color 2023-05-24 15:54:34 +02:00
kolaente a7440ed296
chore: use stores directly 2023-05-24 15:54:34 +02:00
kolaente 12ebefd86a
chore: move v-if 2023-05-24 15:54:34 +02:00
kolaente 6c9cbaadc8
chore: set project id from the outside 2023-05-24 15:54:34 +02:00
kolaente 9b10693172
chore: replace section with a div 2023-05-24 15:54:34 +02:00
kolaente db1c6d6a41
chore: move all options to component props 2023-05-24 15:54:34 +02:00
kolaente c56787443f
chore: add types for emit 2023-05-24 15:54:34 +02:00
kolaente cb218ec0c3
feat: add setting for infinite nesting 2023-05-24 15:54:34 +02:00
kolaente 0dd6f82a0e
fix: use menu tag everywhere 2023-05-24 15:54:34 +02:00
kolaente 225091864f
fix: collapsing child projects 2023-05-24 15:54:34 +02:00
kolaente ebd9c4702e
feat: don't use child_projects property from api 2023-05-24 15:54:33 +02:00
kolaente 4ad9773022
chore: format 2023-05-24 15:54:33 +02:00
kolaente 0a17df87e9
fix: don't show child projects when the project is only a favorite 2023-05-24 15:54:33 +02:00
kolaente b567146d69
chore: move more logic to ProjectsNavigationItem.vue 2023-05-24 15:54:33 +02:00
kolaente 65522a57f1
chore: move ProjectsNavigationWrapper back to navigation.vue 2023-05-24 15:54:33 +02:00
kolaente 1d936618fa
feat: load all projects earlier than in the navigation and use the loading state of the store 2023-05-24 15:54:33 +02:00
kolaente 76814a2d3f
chore: move loading styles to variant into the component 2023-05-24 15:54:33 +02:00
kolaente 4134fcbd75
chore: remove old comment 2023-05-24 15:54:33 +02:00
kolaente 49fac7db1c
chore: use <menu> instead of <ul> 2023-05-24 15:54:33 +02:00
kolaente e25273df48
fix: indention 2023-05-24 15:54:33 +02:00
kolaente 638f6bea24
chore: improve prop type definition 2023-05-24 15:54:33 +02:00
kolaente ddcd6a17dc
chore: only apply padding where needed 2023-05-24 15:54:32 +02:00
kolaente 4e21b463df
chore: remove old todo 2023-05-24 15:54:32 +02:00
kolaente 3db4e011d4
feat: move navigation item to component 2023-05-24 15:54:32 +02:00
kolaente a0d39e6081
chore: use long variable name 2023-05-24 15:54:32 +02:00
kolaente a803bc637e
chore: rename alias 2023-05-24 15:54:32 +02:00
kolaente d4e452545a
chore: remove unused class 2023-05-24 15:54:32 +02:00
kolaente 9d73ac661f
fix: remove leftover suspense 2023-05-24 15:54:32 +02:00
kolaente 55e912221b
chore: use klona to clone project objet 2023-05-24 15:54:32 +02:00
kolaente d85be26761
fix: passing readonly projects data to navigation 2023-05-24 15:54:32 +02:00
kolaente ac78e85e17
chore: move loader class 2023-05-24 15:54:32 +02:00
kolaente 131022da42
chore: export favorite projects from store 2023-05-24 15:54:32 +02:00
kolaente 336db56316
chore: remove unnecessary map 2023-05-24 15:54:32 +02:00
kolaente b5d9afd0f7
chore: export not archived root projects 2023-05-24 15:54:31 +02:00
kolaente 0be83db40f
fix: show favorite on hover 2023-05-24 15:54:31 +02:00
kolaente 03f4d0b8bc
fix: don't show > for top-level projects 2023-05-24 15:54:31 +02:00
kolaente ee8f80cc70
feat: allow selecting a parent project when editing a project 2023-05-24 15:54:31 +02:00
kolaente ce887c38f3
feat: allow selecting a parent project when creating a project 2023-05-24 15:54:31 +02:00
kolaente 799c0be830
feat: allow selecting a parent project when duplicating a project 2023-05-24 15:54:31 +02:00
kolaente 760efa854d
feat: don't handle child projects and instead only save the ids 2023-05-24 15:54:31 +02:00
kolaente 26bec05174
fix: make computed side-effect free 2023-05-24 15:54:31 +02:00
kolaente c32a198a34
chore: refactor get parents project and move to projects store 2023-05-24 15:54:31 +02:00
kolaente 6a8c656dbb
feat: show all parent projects in project search 2023-05-24 15:54:31 +02:00
kolaente 63ba2982c9
feat: show all parent projects in task detail view 2023-05-24 15:54:30 +02:00
kolaente 9d9fb959d8
fix: add await 2023-05-24 15:54:30 +02:00
kolaente 8ed201c83f
fix(filters): load projects after updating a filter 2023-05-24 15:54:30 +02:00
kolaente bfb40c9166
fix(filters): load projects after deleting a filter 2023-05-24 15:54:30 +02:00
kolaente 5ea450844c
fix(filters): load projects after creating a filter 2023-05-24 15:54:30 +02:00
kolaente 36bec9e64f
chore(task): move toggleFavorite to store 2023-05-24 15:54:30 +02:00
kolaente a95014dc5d
feat(projects): move hasProjects check to store 2023-05-24 15:54:30 +02:00
kolaente 2579c33ee1
feat: wrap projects navigation in a <Suspense> so that we can use top level await 2023-05-24 15:54:30 +02:00
kolaente 6f1baa3219
chore: use long variable name 2023-05-24 15:54:30 +02:00
kolaente 4dee3a90e9
chore: rename archived message key 2023-05-24 15:54:30 +02:00
kolaente 326b6eda6f
fix: use correct shortcut to open projects overview 2023-05-24 15:54:30 +02:00
kolaente 85e882cc59
fix: simplify sort 2023-05-24 15:54:29 +02:00
kolaente e4379f0a22
chore: export projects as array directly from projects store 2023-05-24 15:54:29 +02:00
kolaente 2bb7ff1803
chore: rename prop 2023-05-24 15:54:29 +02:00
kolaente 5dd6e9a077
feat(tests): add project tests derived from old namespace tests 2023-05-24 15:54:29 +02:00
kolaente f7629c28f4
fix(projects): make sure the project hierarchy is properly updated when moving projects between parents 2023-05-24 15:54:29 +02:00
kolaente be2a38b48e
feat(navigation): show favorite projects on top 2023-05-24 15:54:29 +02:00
kolaente 3ba5f531bb
fix(navigation): make sure updating a project's state works for sub projects as well. 2023-05-24 15:54:29 +02:00
kolaente 10f1e69bc3
fix(navigation): make marking a project as favorite work 2023-05-24 15:54:29 +02:00
kolaente fd7d90b017
fix(navigation): make sure the Favorites project shows up when marking or unmarking a task as favorite 2023-05-24 15:54:29 +02:00
kolaente d898316918
fix(navigation): favorites project 2023-05-24 15:54:29 +02:00
kolaente a6f524e7af
fix(task detail view): make project display show the task's project 2023-05-24 15:54:29 +02:00
kolaente 5e65814b8c
fix: make check if projects are available work again 2023-05-24 15:54:28 +02:00
kolaente aaa9d553d0
fix: cleanup unused translation strings 2023-05-24 15:54:28 +02:00
kolaente 5685890493
fix: make tests work again 2023-05-24 15:54:28 +02:00
kolaente 2e336150e0
chore: cleanup namespace leftovers 2023-05-24 15:54:28 +02:00
kolaente 749dcdcd70
fix(navigation): hide left ul border 2023-05-24 15:54:28 +02:00
kolaente ab94343d07
feat(navigation): make dragging a project under another project work 2023-05-24 15:54:28 +02:00
kolaente fa71cec5c8
feat(navigation): allow dragging a project out from its parent project 2023-05-24 15:54:28 +02:00
kolaente c6f3829387
feat(navigation): make dragging a project to a parent work 2023-05-24 15:54:28 +02:00
kolaente 7171b63947
fix(navigation): hover state of other menu items 2023-05-24 15:54:28 +02:00
kolaente 06c4c0d921
feat(navigation): add hiding child projects 2023-05-24 15:54:28 +02:00
kolaente f2ca2d850d
feat: translate inbox project title 2023-05-24 15:54:28 +02:00
kolaente 638d187a24
chore: format 2023-05-24 15:54:28 +02:00
kolaente b188d40d3c
feat(navigation): correctly show child projects 2023-05-24 15:54:27 +02:00
kolaente 3ad948305f
fix(navigation): make the styles work again 2023-05-24 15:54:27 +02:00
kolaente be1f1d94c9
fix(navigation): watcher 2023-05-24 15:54:27 +02:00
kolaente 06e8cdb9d2
feat: rebuild main navigation so that it works recursively with projects 2023-05-24 15:54:27 +02:00
kolaente 10311b79df
fix: remove namespace routes 2023-05-24 15:54:27 +02:00
kolaente ad2690b21c
fix: remove namespace store reference 2023-05-24 15:54:27 +02:00
kolaente 1bd17d6e50
feat: remove all namespace leftovers 2023-05-24 15:54:27 +02:00
kolaente a5e710bfe5
fix: route to create new project 2023-05-24 15:54:27 +02:00
kolaente e1bdabc8d6
feat: move namespaces list to projects list 2023-05-24 15:54:27 +02:00
renovate c6ef99dde2 chore(deps): update dependency cypress to v12.13.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-24 00:04:34 +00:00
renovate 49b508a783 fix(deps): update sentry-javascript monorepo to v7.53.0
continuous-integration/drone/push Build is passing Details
2023-05-23 15:50:03 +00:00
renovate 52128925f5 chore(deps): update typescript-eslint monorepo to v5.59.7
continuous-integration/drone/push Build is passing Details
2023-05-23 15:49:52 +00:00
renovate cf0c7f9d08 chore(deps): update dependency rollup to v3.23.0
continuous-integration/drone/push Build is failing Details
2023-05-23 15:49:28 +00:00
renovate 57d5140301 chore(deps): update dependency @types/node to v18.16.14
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-23 12:04:31 +00:00
renovate dbd9106621 chore(deps): update dependency postcss-preset-env to v8.4.1
continuous-integration/drone/push Build is passing Details
2023-05-23 11:17:17 +00:00
renovate e4fef0e88e chore(deps): update dependency eslint to v8.41.0
continuous-integration/drone/push Build is passing Details
2023-05-23 11:16:31 +00:00
renovate 7ef0074ecc chore(deps): update dependency caniuse-lite to v1.0.30001489
continuous-integration/drone/push Build is passing Details
2023-05-23 11:16:09 +00:00
renovate 17c35f6d42 chore(deps): update dependency happy-dom to v9.20.1
continuous-integration/drone/push Build is failing Details
2023-05-23 11:15:58 +00:00
renovate 3a0844adba chore(deps): update dependency @rushstack/eslint-patch to v1.3.0
continuous-integration/drone/push Build is failing Details
2023-05-23 11:15:13 +00:00
renovate 5b5b9022e0 chore(deps): update dependency vite-plugin-pwa to v0.15.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-23 01:09:13 +00:00
Frederick [Bot] 0b0bd7dff6 [skip ci] Updated translations via Crowdin 2023-05-23 00:29:34 +00:00
renovate 079e3782d1 chore(deps): update dependency rollup to v3.22.0
continuous-integration/drone/push Build is failing Details
2023-05-19 10:30:44 +00:00
renovate a0ae9ae54c chore(deps): update dependency @types/marked to v5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-05-19 09:06:04 +00:00
renovate a1b9a0ec4c fix(deps): update dependency @kyvg/vue3-notification to v2.9.1
continuous-integration/drone/push Build is failing Details
2023-05-19 08:07:44 +00:00
renovate 1fa690670d fix(deps): update dependency vue-router to v4.2.1
continuous-integration/drone/push Build is failing Details
2023-05-19 08:06:23 +00:00
renovate 3f0a87a5ec chore(deps): update dependency vite to v4.3.8
continuous-integration/drone/push Build is failing Details
2023-05-19 08:05:46 +00:00
renovate caf02f78bf chore(deps): update dependency @types/marked to v4.3.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-05-18 23:05:09 +00:00
renovate 9b9fd14d27 chore(deps): update dependency vitest to v0.31.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-17 15:05:09 +00:00
renovate 7f77efbfab chore(deps): update dependency @types/node to v18.16.11
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-05-16 20:04:54 +00:00
renovate 53967d20cc chore(deps): update dependency vite to v4.3.7
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-05-16 17:05:08 +00:00
renovate ef3411f39a chore(deps): update dependency @types/node to v18.16.10
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-05-16 11:04:50 +00:00
renovate 66e63f1363 chore(deps): update typescript-eslint monorepo to v5.59.6
continuous-integration/drone/push Build is passing Details
2023-05-16 10:46:23 +00:00
renovate 2fe21f6b28 fix(deps): update sentry-javascript monorepo to v7.52.1
continuous-integration/drone/push Build is passing Details
2023-05-16 10:46:09 +00:00
renovate 67df372636 chore(deps): update dependency eslint-plugin-vue to v9.13.0
continuous-integration/drone/push Build is failing Details
2023-05-16 10:45:49 +00:00
renovate df80e9da23 chore(deps): update dependency rollup to v3.21.8
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-05-16 08:04:56 +00:00
renovate 13ab2efd0f chore(deps): update dependency @faker-js/faker to v8.0.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-05-15 17:04:44 +00:00
renovate 0ffe96cf59 chore(deps): update dependency vite to v4.3.6
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-05-15 16:04:58 +00:00
renovate 1808d0971d fix(deps): update sentry-javascript monorepo to v7.52.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-05-15 14:05:00 +00:00
renovate ec83a28d78 chore(deps): update pnpm to v8.5.1
continuous-integration/drone/push Build is passing Details
2023-05-15 10:33:06 +00:00
renovate f0320b3a58 chore(deps): update dependency caniuse-lite to v1.0.30001487
continuous-integration/drone/push Build is passing Details
2023-05-15 10:30:51 +00:00
renovate d93a1a4f4f chore(deps): update dependency happy-dom to v9.18.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-15 00:05:29 +00:00
renovate a9f9ddf6b9 chore(deps): update dependency @types/node to v18.16.9
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-13 15:05:04 +00:00
renovate 6a8fe35fcf chore(deps): update dependency rollup to v3.21.7
continuous-integration/drone/push Build is passing Details
2023-05-13 14:48:00 +00:00
renovate 94661e9e09 chore(deps): update dependency vue-tsc to v1.6.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-13 08:04:55 +00:00
renovate 318f63d098 chore(deps): update dependency esbuild to v0.17.19
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-13 01:04:45 +00:00
renovate e2c9e83c2a chore(deps): update dependency @vitejs/plugin-vue to v4.2.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-12 11:04:45 +00:00
renovate cd434a0e3e chore(deps): update dependency @types/node to v18.16.8
continuous-integration/drone/push Build is passing Details
2023-05-12 07:06:52 +00:00
renovate 9f293af804 chore(deps): update dependency @vue/tsconfig to v0.4.0
continuous-integration/drone/push Build is failing Details
2023-05-12 07:06:24 +00:00
renovate b175e00cfe chore(deps): update dependency @tsconfig/node18 to v2.0.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-12 05:04:41 +00:00
Frederick [Bot] 19dd82d62a [skip ci] Updated translations via Crowdin 2023-05-12 00:29:44 +00:00
renovate b3ddc9465a chore(deps): update dependency @vitejs/plugin-vue to v4.2.2
continuous-integration/drone/push Build is passing Details
2023-05-11 19:57:40 +00:00
renovate 6b38f17d32 fix(deps): update dependency vue-router to v4.2.0
continuous-integration/drone/push Build is passing Details
2023-05-11 19:57:25 +00:00
renovate 86449d4912 fix(deps): update dependency marked to v5.0.2
continuous-integration/drone/push Build is failing Details
2023-05-11 19:57:07 +00:00
renovate 145d756251 chore(deps): update dependency @faker-js/faker to v8
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-11 18:05:10 +00:00
renovate 838a11a2f6 chore(deps): update dependency eslint-plugin-vue to v9.12.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-05-10 14:04:39 +00:00
renovate 3bfd3210b0 chore(deps): update dependency @types/node to v18.16.7
continuous-integration/drone/push Build is failing Details
2023-05-10 09:46:06 +00:00
renovate e933bfa99e fix(deps): update sentry-javascript monorepo to v7.51.2
continuous-integration/drone/push Build is failing Details
2023-05-10 09:14:43 +00:00
renovate f6a37a54d0 fix(deps): update dependency pinia to v2.0.36
continuous-integration/drone/push Build is failing Details
2023-05-10 09:14:33 +00:00
primeapple e00c9bb1af feat: add hotkeys for priority, delete and favorite on the `TaskDetailView` (#3400)
continuous-integration/drone/push Build is failing Details
Reviewed-on: #3400
Reviewed-by: konrad <k@knt.li>
Co-authored-by: primeapple <toni.mueller.web@mailbox.org>
Co-committed-by: primeapple <toni.mueller.web@mailbox.org>
2023-05-10 09:14:07 +00:00
kolaente 018707c3d5
fix(ci): disable puppeteer chrome download
continuous-integration/drone/push Build is failing Details
2023-05-10 10:42:44 +02:00
renovate 386727f6c5 chore(deps): update dependency @types/node to v18.16.6
continuous-integration/drone/push Build is passing Details
2023-05-10 08:09:35 +00:00
renovate a29ce36d6c chore(deps): update typescript-eslint monorepo to v5.59.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-05-10 07:05:09 +00:00
renovate 7aed16bd6f chore(deps): update dependency rollup to v3.21.6
continuous-integration/drone/push Build is failing Details
2023-05-10 06:03:37 +00:00
renovate b1f3ca6e59 chore(deps): update pnpm to v8.5.0
continuous-integration/drone/push Build is failing Details
2023-05-10 06:03:09 +00:00
renovate 4c0b8a06c5 chore(deps): update dependency cypress to v12.12.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-05-09 23:05:25 +00:00
renovate 60647c50ac chore(deps): update dependency caniuse-lite to v1.0.30001486
continuous-integration/drone/push Build is failing Details
2023-05-08 07:11:25 +00:00
renovate 59eaf1849e chore(deps): update dependency happy-dom to v9.10.9
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-05-08 00:05:41 +00:00
renovate fb57339050 chore(deps): update dependency eslint-plugin-vue to v9.11.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-07 17:04:47 +00:00
renovate f9831a6ad8 fix(deps): update dependency marked to v5.0.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-06 21:04:51 +00:00
renovate f25c67f80a chore(deps): update dependency eslint to v8.40.0
continuous-integration/drone/push Build is passing Details
2023-05-06 15:26:02 +00:00
renovate d3b0b97192 chore(deps): update dependency @types/node to v18.16.5
continuous-integration/drone/push Build is passing Details
2023-05-06 15:25:42 +00:00
renovate fa3be219a8 fix(deps): update dependency dompurify to v3.0.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-05-06 13:04:56 +00:00
renovate c22702d911 chore(deps): update dependency @types/node to v18.16.4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-05 14:04:43 +00:00
renovate b25c5ff547 chore(deps): update dependency vite to v4.3.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-05 11:04:43 +00:00
renovate 2e0a097806 chore(deps): update dependency rollup to v3.21.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-05 05:04:46 +00:00
renovate c2083f7924 fix(deps): update sentry-javascript monorepo to v7.51.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-04 16:05:24 +00:00
renovate 8923261e5b fix(deps): update dependency ufo to v1.1.2
continuous-integration/drone/push Build is passing Details
2023-05-04 07:35:00 +00:00
renovate 5391df56b0 chore(deps): update dependency vue-tsc to v1.6.4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-04 01:04:54 +00:00
renovate d2b1f5780e chore(deps): update dependency vitest to v0.31.0
continuous-integration/drone/push Build is passing Details
2023-05-03 19:52:49 +00:00
renovate 1717e968e1 chore(deps): update dependency rollup to v3.21.4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-03 19:04:31 +00:00
renovate 2c29bb3971 chore(deps): update pnpm to v8.4.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-02 14:04:09 +00:00
renovate 37b8218a0a chore(deps): update node.js to v20 (#3411)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3411
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-05-02 13:35:04 +00:00
konrad ca7bbb5b91 chore(ci): remove netlify dependency (#3459)
continuous-integration/drone/push Build is passing Details
Co-authored-by: kolaente <k@knt.li>
Reviewed-on: #3459
2023-05-02 10:10:14 +00:00
renovate 2f3c008d2b chore(deps): update dependency vite to v4.3.4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-02 08:04:40 +00:00
renovate c2722b7c3d fix(deps): update dependency @vueuse/core to v10.1.2
continuous-integration/drone/push Build is failing Details
2023-05-02 07:18:12 +00:00
renovate 312abd907f chore(deps): update dependency rollup to v3.21.3
continuous-integration/drone/push Build is passing Details
2023-05-02 07:18:02 +00:00
renovate 1b73c1ed64 chore(deps): update dependency vue-tsc to v1.6.3
continuous-integration/drone/push Build is passing Details
2023-05-02 07:17:43 +00:00
renovate d442d6653b fix(deps): update dependency marked to v5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-02 05:05:40 +00:00
renovate 758b8d6e2b chore(deps): update typescript-eslint monorepo to v5.59.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-01 18:04:39 +00:00
renovate 416fd2e2a7 chore(deps): update dependency @types/marked to v4.3.0
continuous-integration/drone/push Build is passing Details
2023-05-01 16:45:51 +00:00
renovate 15a8335f1a chore(deps): update dependency vue-tsc to v1.6.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-01 14:04:42 +00:00
renovate c689583669 chore(deps): update dependency netlify-cli to v14.3.1
continuous-integration/drone/push Build is passing Details
2023-05-01 10:44:11 +00:00
renovate 7a43a7acc9 chore(deps): update dependency happy-dom to v9.10.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-01 00:04:51 +00:00
renovate 8843418161 fix(deps): update dependency date-fns to v2.30.0
continuous-integration/drone/push Build is passing Details
2023-04-30 06:57:42 +00:00
renovate 7c1eab13ae chore(deps): update dependency rollup to v3.21.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-30 06:04:44 +00:00
renovate 5a69036da7 fix(deps): update dependency highlight.js to v11.8.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-29 14:04:50 +00:00
renovate 2ad3458873 chore(deps): update dependency @types/node to v18.16.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-29 07:04:40 +00:00
renovate eb464343e8 chore(deps): update dependency rollup to v3.21.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-29 06:04:42 +00:00
kolaente 2f18d0cbad
fix(docker): don't set nginx worker rlimit
continuous-integration/drone/push Build is passing Details
Resolves https://community.vikunja.io/t/helm-chart-frontend-pod-does-not-start-because-of-permission-issues-in-raspberry-pie-4-k3s/1286
2023-04-28 10:31:15 +02:00
renovate 6cd463a514 chore(deps): pin dependency @tsconfig/node18 to 2.0.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-28 08:04:35 +00:00
kolaente 05b70632c5
fix: tsconfig as per https://github.com/vuejs/tsconfig#configuration-for-node-environments
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-28 09:30:45 +02:00
kolaente ca9fe6ff21
fix: tsconfig as per https://github.com/vuejs/tsconfig#configuration-for-node-environments
continuous-integration/drone/pr Build is failing Details
2023-04-28 09:16:54 +02:00
renovate e5754300de chore(deps): update dependency @vue/tsconfig to v0.3.2
continuous-integration/drone/pr Build is failing Details
2023-04-28 07:04:31 +00:00
renovate 65134048bf chore(deps): update dependency @vitejs/plugin-vue to v4.2.1
continuous-integration/drone/push Build is passing Details
2023-04-28 06:04:35 +00:00
renovate 8339a99747 chore(deps): update dependency @types/node to v18.16.2
continuous-integration/drone/push Build is passing Details
2023-04-28 05:17:36 +00:00
renovate 3e1ae41e70 fix(deps): update dependency axios to v1.4.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-28 00:04:55 +00:00
renovate f757ba3441 chore(deps): update dependency vue-tsc to v1.6.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-27 19:04:37 +00:00
renovate 6499c9cb5b fix(deps): update sentry-javascript monorepo to v7.50.0
continuous-integration/drone/push Build is passing Details
2023-04-27 09:39:32 +00:00
renovate 28e5440d8b fix(deps): update dependency codemirror to v5.65.13
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-27 09:04:38 +00:00
renovate fef8c4d0f4 chore(deps): update dependency vue-tsc to v1.6.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-26 21:06:27 +00:00
renovate 99e5059c64 chore(deps): update dependency cypress to v12.11.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-26 18:04:42 +00:00
renovate 5df4f39d95 chore(deps): update dependency vite to v4.3.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-26 15:04:55 +00:00
renovate 7ec5a70ccb chore(deps): update dependency sass to v1.62.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-26 10:05:07 +00:00
renovate 72fcab6e78 chore(deps): update dependency @types/node to v18.16.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-26 09:04:23 +00:00
kolaente 292c90425e
fix: lint
continuous-integration/drone/push Build is passing Details
2023-04-26 10:19:49 +02:00
kolaente b80f070431
feat: show avatar and full name in team overview
continuous-integration/drone/push Build is failing Details
2023-04-25 18:32:36 +02:00
renovate 03936c0403 chore(deps): update dependency vite to v4.3.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-25 16:04:40 +00:00
kolaente 62825d2e64
fix: add spacing between checkbox and title of related task
continuous-integration/drone/push Build is passing Details
Related to https://github.com/go-vikunja/frontend/issues/111
2023-04-25 17:33:47 +02:00
renovate 5cd5caef45 chore(deps): update dependency @vue/eslint-config-typescript to v11.0.3
continuous-integration/drone/push Build is passing Details
2023-04-25 15:17:58 +00:00
renovate 798e8b529d chore(deps): update dependency @vitejs/plugin-legacy to v4.0.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-25 15:04:36 +00:00
renovate 0e3766c5a5 chore(deps): update typescript-eslint monorepo to v5.59.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-25 13:05:07 +00:00
renovate 90207a4427 chore(deps): update dependency @vitejs/plugin-vue to v4.2.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-25 10:05:12 +00:00
renovate 60993a886a chore(deps): update dependency caniuse-lite to v1.0.30001481
continuous-integration/drone/push Build is passing Details
2023-04-24 06:16:59 +00:00
renovate a6b42f9181 chore(deps): update dependency happy-dom to v9.9.2
continuous-integration/drone/push Build is passing Details
2023-04-24 06:16:43 +00:00
renovate 98fbd7c53c chore(deps): update dependency netlify-cli to v14
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-24 01:05:19 +00:00
renovate 8d533f50e8 chore(deps): update dependency rollup to v3.21.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-23 20:04:46 +00:00
renovate 707459ec77 chore(deps): update dependency @types/node to v18.16.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-23 06:04:51 +00:00
renovate faf7db649e chore(deps): update dependency esbuild to v0.17.18
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-22 21:04:36 +00:00
renovate 202e71be48 chore(deps): update dependency vue-tsc to v1.4.4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-22 20:04:41 +00:00
renovate d6e8b418d3 chore(deps): update dependency vue-tsc to v1.4.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-22 16:04:50 +00:00
renovate a9f41f6114 chore(deps): update dependency eslint to v8.39.0
continuous-integration/drone/push Build is passing Details
2023-04-22 15:45:11 +00:00
renovate f6f0d52518 fix(deps): update dependency @vueuse/core to v10.1.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-22 10:04:50 +00:00
renovate ccb9be42c2 chore(deps): update dependency vue-tsc to v1.4.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-21 11:04:30 +00:00
renovate 179009bfe3 chore(deps): update dependency @types/node to v18.15.13
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-21 07:04:39 +00:00
renovate 8c2bd94a9f chore(deps): update dependency vue-tsc to v1.4.1
continuous-integration/drone/push Build is passing Details
2023-04-21 06:13:48 +00:00
renovate 7757166d75 chore(deps): update dependency rollup to v3.20.7
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-21 05:04:36 +00:00
renovate 7f03002972 chore(deps): update dependency vite to v4.3.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-04-20 20:04:26 +00:00
renovate 8555006d9e chore(deps): update dependency vue-tsc to v1.4.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-04-20 18:04:40 +00:00
renovate 713ad64658 fix(deps): update sentry-javascript monorepo to v7.49.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-20 16:04:46 +00:00
renovate 0713d481e3 fix(deps): update dependency pinia to v2.0.35
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-20 11:04:28 +00:00
renovate ace0cf3588 chore(deps): update dependency vite to v4.3.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-20 09:04:31 +00:00
renovate bba3bbfe89 chore(deps): update dependency @types/dompurify to v3.0.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-20 08:04:42 +00:00
renovate 754afc5496 chore(deps): update dependency @types/node to v18.15.12
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-20 00:05:04 +00:00
renovate f1e8892ab5 chore(deps): update dependency postcss to v8.4.23
continuous-integration/drone/push Build is passing Details
2023-04-19 20:32:32 +00:00
renovate c11e192c4e fix(deps): update dependency axios to v1.3.6
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-19 20:05:18 +00:00
renovate e9c704075d chore(deps): update pnpm to v8.3.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-19 13:04:16 +00:00
renovate 35edcb5672 chore(deps): update dependency rollup to v3.20.6
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-18 12:05:05 +00:00
renovate 4695798176 chore(deps): update dependency vite to v4.2.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-18 10:05:15 +00:00
renovate 7a323fd170 chore(deps): update dependency rollup to v3.20.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-18 06:04:54 +00:00
renovate 1d6e4b6e32 chore(deps): update pnpm to v8.3.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-18 01:04:56 +00:00
renovate 5524aa7998 chore(deps): update dependency cypress to v12.10.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-17 19:04:51 +00:00
renovate 15ff2008e3 chore(deps): update typescript-eslint monorepo to v5.59.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-17 18:05:11 +00:00
renovate 9bc2e6e165 chore(deps): update dependency postcss-preset-env to v8.3.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-17 07:04:46 +00:00
renovate 344001856c chore(deps): update dependency rollup to v3.20.4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-17 06:04:41 +00:00
renovate ad261fcc2f chore(deps): update dependency esbuild to v0.17.17
continuous-integration/drone/push Build is failing Details
2023-04-17 05:29:14 +00:00
renovate 5142a0ae72 chore(deps): update dependency caniuse-lite to v1.0.30001479
continuous-integration/drone/push Build is passing Details
2023-04-17 05:27:04 +00:00
renovate 6d195f96c9 chore(deps): update dependency happy-dom to v9.7.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-17 01:05:56 +00:00
Frederick [Bot] 1917b217a8 [skip ci] Updated translations via Crowdin 2023-04-17 00:25:34 +00:00
renovate 1f6b01bc73 chore(deps): update dependency rollup to v3.20.3
continuous-integration/drone/push Build is passing Details
2023-04-16 17:19:59 +00:00
renovate d47a16aa8e chore(deps): update dependency postcss to v8.4.22
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-16 14:04:37 +00:00
renovate c57d00a74b chore(deps): update dependency eslint-plugin-vue to v9.11.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-15 01:04:45 +00:00
renovate 77ea7fa0ee fix(deps): update dependency @vueuse/core to v10.0.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-14 22:04:52 +00:00
kolaente b92d780cda chore: formatting
continuous-integration/drone/push Build is passing Details
2023-04-14 21:53:04 +00:00
kolaente f14e721caf fix: rename resolveRef 2023-04-14 21:53:04 +00:00
renovate 1ff6399112 fix(deps): update dependency @vueuse/core to v10 2023-04-14 21:53:04 +00:00
renovate 503fb8da76 fix(deps): update dependency dompurify to v3.0.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-14 18:04:56 +00:00
renovate f050cb7015 chore(deps): update histoire to v0.16.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-14 11:04:40 +00:00
renovate 3670916f36 fix(deps): update sentry-javascript monorepo to v7.48.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-14 10:04:35 +00:00
Frederick [Bot] 838a063eaa [skip ci] Updated translations via Crowdin 2023-04-14 00:26:31 +00:00
renovate e1b16b11d6 chore(deps): update node.js to v18.16.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-13 01:04:28 +00:00
Dominik Pschenitschni 314cbf471f feat: better vscode vitest integration
continuous-integration/drone/push Build is passing Details
2023-04-12 15:39:49 +00:00
Dominik Pschenitschni a416d26f7c
chore: better function naming in password components
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-04-12 16:15:40 +02:00
Dominik Pschenitschni 795b26e1dd feat: improve datemathHelp.vue
continuous-integration/drone/push Build is passing Details
2023-04-12 07:33:45 +00:00
renovate 14666cf9d8 chore(deps): update dependency sass to v1.62.0
continuous-integration/drone/push Build is passing Details
2023-04-12 05:57:33 +00:00
renovate c938f31935 chore(deps): update pnpm to v8
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-11 23:05:49 +00:00
kolaente 35a52ef01b
fix(quick add magic): date parsing with a date at the beginning
continuous-integration/drone/push Build is passing Details
Resolves https://github.com/go-vikunja/frontend/issues/110
2023-04-11 18:12:08 +02:00
renovate 3b05ce3f10 chore(deps): update histoire to v0.16.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-11 14:04:49 +00:00
renovate aec4fd7a2d chore(deps): update dependency vitest to v0.30.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-11 12:04:57 +00:00
renovate 2661af3a17 fix(deps): update sentry-javascript monorepo to v7.47.0
continuous-integration/drone/push Build is passing Details
2023-04-11 12:01:30 +00:00
renovate 56f43bae3f chore(deps): update typescript-eslint monorepo to v5.58.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-10 18:04:41 +00:00
renovate 84472d2e9c chore(deps): update dependency happy-dom to v9.1.9
continuous-integration/drone/push Build is passing Details
2023-04-10 13:17:25 +00:00
renovate c5afcd63b0 chore(deps): update dependency postcss-preset-env to v8.3.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-04-10 12:05:14 +00:00
renovate 9bdb257814 chore(deps): update dependency caniuse-lite to v1.0.30001477
continuous-integration/drone/push Build is passing Details
2023-04-10 11:14:22 +00:00
renovate 5ad9891b16 chore(deps): update pnpm to v7.32.0
continuous-integration/drone/push Build is passing Details
2023-04-10 11:13:57 +00:00
renovate 7c04064917 chore(deps): update dependency vitest to v0.30.0
continuous-integration/drone/push Build is passing Details
2023-04-10 11:13:43 +00:00
renovate fb5383d86b chore(deps): update dependency esbuild to v0.17.16
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-10 05:04:46 +00:00
renovate 68af314ec0 chore(deps): update dependency eslint to v8.38.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-08 00:04:43 +00:00
renovate 8b1de5ce09 fix(deps): update dependency pinia to v2.0.34
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-07 20:04:46 +00:00
renovate 724b6fe091 chore(deps): update dependency typescript to v5.0.4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-07 18:04:25 +00:00
renovate 6648cd30c3 chore(deps): update dependency sass to v1.61.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-06 22:04:45 +00:00
kolaente 8b90b45739
fix: make sure the unread notifications indicator is correctly positioned
continuous-integration/drone/push Build is passing Details
Resolves #3358
2023-04-06 16:11:12 +02:00
renovate 39be67eecf fix(deps): update dependency axios to v1.3.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-06 09:06:06 +00:00
Frederick [Bot] 750f0ddeab [skip ci] Updated translations via Crowdin 2023-04-05 00:06:16 +00:00
renovate 6a5ece2f24 chore(deps): update pnpm to v7.31.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-04 01:04:32 +00:00
Frederick [Bot] 4ce33abfe6 [skip ci] Updated translations via Crowdin 2023-04-04 00:06:21 +00:00
renovate 5b7e1af87d chore(deps): update dependency @types/dompurify to v3.0.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-03 20:04:51 +00:00
renovate 59c6605b14 chore(deps): update typescript-eslint monorepo to v5.57.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-03 18:05:29 +00:00
Dominik Pschenitschni 820d598ecd fix(i18n): orderedList translationid
continuous-integration/drone/push Build is passing Details
2023-04-03 09:48:03 +00:00
Dominik Pschenitschni a263ec1273
fix(Expandable): spelling ⛈
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-03 11:31:39 +02:00
renovate b68892492c chore(deps): update dependency happy-dom to v9
continuous-integration/drone/push Build is passing Details
2023-04-03 05:18:07 +00:00
renovate 7c97695cec chore(deps): update dependency netlify-cli to v13.2.2
continuous-integration/drone/push Build is failing Details
2023-04-03 05:17:16 +00:00
renovate e764f34a2d chore(deps): update dependency caniuse-lite to v1.0.30001473
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-04-03 00:05:10 +00:00
renovate 6892a28bb6 chore(deps): update dependency esbuild to v0.17.15
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-01 23:04:45 +00:00
renovate 74d688b8d2 chore(deps): update dependency csstype to v3.1.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-01 21:04:41 +00:00
renovate ed84651046 chore(deps): update dependency postcss-preset-env to v8.3.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-03-31 17:04:54 +00:00
renovate 7468ed21fa chore(deps): update dependency typescript to v5.0.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-03-30 21:05:23 +00:00
renovate d8015913c3 fix(deps): update sentry-javascript monorepo to v7.46.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-30 14:04:41 +00:00
Frederick [Bot] 78789834f0 [skip ci] Updated translations via Crowdin 2023-03-30 00:06:17 +00:00
Dominik Pschenitschni 739fe0caa1
fix: move types to dev dependencies
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-29 22:23:40 +02:00
Dominik Pschenitschni 4703f9c4d5
fix: has-pseudo-class polyfill
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-29 17:22:06 +02:00
Dominik Pschenitschni fd699ad777
fix: checkbox label size based on icon 2023-03-29 17:17:51 +02:00
Dominik Pschenitschni 0acf44778d
fix: undo further nesting of interactive items
This is really bad for UX and accessability
2023-03-29 17:17:50 +02:00
Dominik Pschenitschni 8fc254d2db
feat: abstract BaseCheckbox 2023-03-29 17:17:49 +02:00
Dominik Pschenitschni 7d3b97d422 feat: prepare for pnpm 8 (#3331)
continuous-integration/drone/push Build is failing Details
Related: #3317

Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: #3331
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-03-29 12:02:34 +00:00
kolaente 4a34f245db
chore(deps): update flake
continuous-integration/drone/push Build is passing Details
2023-03-29 13:41:52 +02:00
renovate 973ea39a64 chore(deps): update dependency eslint to v8.37.0
continuous-integration/drone/push Build is passing Details
2023-03-29 07:56:52 +00:00
renovate f94a65ce7a chore(deps): update dependency @types/node to v18.15.11
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-28 22:05:12 +00:00
renovate 432fbbea78 chore(deps): update dependency cypress to v12.9.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-28 19:05:20 +00:00
renovate e483f1cd2e chore(deps): update dependency vitest to v0.29.8
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-28 14:05:19 +00:00
renovate eb34f6e136 chore(deps): update dependency postcss-preset-env to v8.2.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-28 12:05:42 +00:00
Dominik Pschenitschni 91e9eef582 fix: use strict comparison
continuous-integration/drone/push Build is passing Details
2023-03-28 10:49:34 +00:00
Dominik Pschenitschni dea1789a00
feat: type i18n improvements
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-28 12:35:19 +02:00
WofWca 30adad5ae6 feat: mark undone if task moved from isDoneBucket (#3291)
continuous-integration/drone/push Build is passing Details
Addresses #545 (not completely)

Reviewed-on: #3291
Reviewed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Reviewed-by: konrad <k@knt.li>
Co-authored-by: WofWca <wofwca@protonmail.com>
Co-committed-by: WofWca <wofwca@protonmail.com>
2023-03-28 10:21:19 +00:00
renovate 3ed6f939e5 chore(deps): update dependency vite-plugin-pwa to v0.14.7
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-28 10:05:08 +00:00
renovate a337d22c1f fix(deps): update font awesome to v6.4.0
continuous-integration/drone/push Build is passing Details
2023-03-27 18:27:34 +00:00
renovate addfcf2510 chore(deps): update typescript-eslint monorepo to v5.57.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-27 18:05:13 +00:00
renovate 303034f02c chore(deps): update pnpm to v7.30.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-27 14:04:54 +00:00
renovate 0fd44e9484 chore(deps): update dependency caniuse-lite to v1.0.30001470
continuous-integration/drone/push Build is passing Details
2023-03-27 06:06:31 +00:00
renovate 04040f20ba chore(deps): update dependency netlify-cli to v13.2.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-27 00:06:51 +00:00
konrad 6c999ad148 fix: ensure same protocol for configured api url (#3303)
continuous-integration/drone/push Build is passing Details
Resolves https://github.com/go-vikunja/frontend/issues/109

Vikunja would save the api url with `http` instead of `https` when the frontend was accessed via https. This was fine in most cases when the server would redirect all requests made to http to the secure https variant. However, in newer Firefox versions (and soon, Chrome probably as well) the browser would not follow that redirect anymore. Hence, we need to make sure to only make api requests to the same protocol. Doing API requests from an https hosted fronted to an http hosted api would probably fail already anyway.

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: #3303
Reviewed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-authored-by: konrad <k@knt.li>
Co-committed-by: konrad <k@knt.li>
2023-03-26 19:18:47 +00:00
renovate cc519e6773 chore(deps): update dependency esbuild to v0.17.14
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-26 03:05:27 +00:00
renovate f9dcae4f65 chore(deps): update dependency @types/node to v18.15.10
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-25 23:05:50 +00:00
renovate ade6c2cb18 chore(deps): update dependency postcss-preset-env to v8.1.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-25 09:05:19 +00:00
renovate 4566b62a93 chore(deps): update dependency @types/node to v18.15.9
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-25 08:05:10 +00:00
renovate 37d3ef24d2 chore(deps): update dependency @types/node to v18.15.8
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-25 00:05:22 +00:00
kolaente 71265769ce fix: update logo change only every hour
continuous-integration/drone/push Build is passing Details
2023-03-24 23:30:26 +00:00
kolaente a13c16ca03 fix: use time constant 2023-03-24 23:30:26 +00:00
kolaente a33fb72ef8 fix: use onActivated 2023-03-24 23:30:26 +00:00
kolaente c5776264c0 fix: only update daytime salutation when switching to home view 2023-03-24 23:30:26 +00:00
kolaente 078d8b39a9 fix(gantt): only update today value when changing to the gantt chart view 2023-03-24 23:30:26 +00:00
kolaente b77c7c2f45 fix: add interval to uses of useNow so that it uses less resources 2023-03-24 23:30:26 +00:00
renovate e369473dd0 chore(deps): update dependency esbuild to v0.17.13
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-24 19:05:04 +00:00
renovate 70501f9da1 chore(deps): update pnpm to v7.30.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-24 14:04:24 +00:00
renovate 9bb7019b09 chore(deps): update dependency rollup to v3.20.2
continuous-integration/drone/push Build is passing Details
2023-03-24 13:21:24 +00:00
renovate df4fe7a644 fix(deps): update sentry-javascript monorepo to v7.45.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-24 10:06:38 +00:00
renovate 2f009d0b27 chore(deps): update dependency @types/node to v18.15.7
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-24 09:05:25 +00:00
renovate 70d7def7d7 chore(deps): update dependency sass to v1.60.0
continuous-integration/drone/push Build is passing Details
2023-03-24 07:11:01 +00:00
renovate 0033407f96 chore(deps): update pnpm to v7.30.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-24 02:04:25 +00:00
renovate b10a2329ca chore(deps): update dependency @types/node to v18.15.6
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-23 22:05:08 +00:00
WofWca 6870db4a72 fix: list view: don't sort tasks after marking one "done" (#3285)
continuous-integration/drone/push Build is passing Details
See https://community.vikunja.io/t/list-view-tasks-being-sorted-after-marking-one-done-throws-you-off/1257/2

Reviewed-on: #3285
Reviewed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-authored-by: WofWca <wofwca@protonmail.com>
Co-committed-by: WofWca <wofwca@protonmail.com>
2023-03-23 20:50:17 +00:00
WofWca 3643ffe0d0 fix: improve the "pop" sound a bit
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
Trim the (noisy) silence (especially at the start, because
that delay from making a change and playing a sound is a little
annoying), with fade-in and fade-out

Edited with Audacity

File(s) history:
7f5140bbb4
955bd73fec
2023-03-23 23:38:52 +04:00
renovate 02971f6ff9 chore(deps): update dependency vite-plugin-pwa to v0.14.6
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-23 12:05:31 +00:00
renovate 7d3c34b004 chore(deps): update pnpm to v7.30.1
continuous-integration/drone/push Build is passing Details
2023-03-23 11:48:32 +00:00
renovate f3ea6fd4dc chore(deps): update dependency eslint-plugin-vue to v9.10.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-23 11:05:38 +00:00
renovate bed6b81a58 chore(deps): update dependency rollup to v3.20.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-23 09:05:02 +00:00
renovate f9bf9139b8 fix(deps): update dependency @intlify/unplugin-vue-i18n to v0.10.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-22 18:05:17 +00:00
Dominik Pschenitschni 96e2c81b7e
fix: ignore ts deprecations for now
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
see https://github.com/vuejs/tsconfig/issues/6
2023-03-22 15:47:21 +01:00
renovate e62c00a187 chore(deps): update dependency typescript to v5
continuous-integration/drone/pr Build is failing Details
2023-03-22 13:05:25 +00:00
renovate 611419888a chore(deps): update dependency vite-plugin-pwa to v0.14.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-22 12:05:18 +00:00
renovate 5cc7e282bf fix(deps): update dependency marked to v4.3.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-22 06:05:26 +00:00
renovate de0b71103c chore(deps): update dependency @vue/test-utils to v2.3.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-03-21 16:05:20 +00:00
renovate 537e9e8044 fix(deps): update sentry-javascript monorepo to v7.44.2
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-21 11:05:19 +00:00
renovate ac95c1fdc8 chore(deps): update dependency @types/node to v18.15.5
continuous-integration/drone/push Build is passing Details
2023-03-20 22:44:25 +00:00
renovate b36da9e4d9 chore(deps): update dependency @cypress/vite-dev-server to v5.0.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-20 22:05:07 +00:00
renovate e11ee3c136 chore(deps): update dependency vitest to v0.29.7
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-20 21:05:16 +00:00
renovate 887719ea24 chore(deps): update dependency @cypress/vue to v5.0.5
continuous-integration/drone/push Build is passing Details
2023-03-20 19:08:09 +00:00
renovate 14f1c3b26e fix(deps): update sentry-javascript monorepo to v7.44.1
continuous-integration/drone/push Build is passing Details
2023-03-20 19:04:28 +00:00
renovate 2142729d38 chore(deps): update typescript-eslint monorepo to v5.56.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-20 18:06:42 +00:00
renovate 9dcc2baae2 fix(deps): update sentry-javascript monorepo to v7.44.0
continuous-integration/drone/push Build is passing Details
2023-03-20 16:50:06 +00:00
renovate 37c88d2974 chore(deps): update dependency vitest to v0.29.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-20 15:05:22 +00:00
renovate 36fd0deec4 chore(deps): update dependency vitest to v0.29.4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-20 14:05:04 +00:00
renovate 4a4438d431 chore(deps): update dependency vite to v4.2.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-20 12:05:22 +00:00
renovate 28a6745346 fix(deps): update dependency @intlify/unplugin-vue-i18n to v0.9.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-20 10:05:21 +00:00
renovate 9ae0470879 chore(deps): update pnpm to v7.30.0
continuous-integration/drone/push Build is passing Details
2023-03-20 08:10:09 +00:00
renovate 927aed1161 chore(deps): update dependency netlify-cli to v13.1.6
continuous-integration/drone/push Build is passing Details
2023-03-20 08:09:18 +00:00
renovate 7f3d7a656d chore(deps): update dependency caniuse-lite to v1.0.30001468
continuous-integration/drone/push Build is passing Details
2023-03-20 08:08:31 +00:00
renovate 040a8ce095 chore(deps): update dependency rollup to v3.20.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-20 07:05:16 +00:00
Frederick [Bot] 8974939bf2 [skip ci] Updated translations via Crowdin 2023-03-19 00:05:55 +00:00
Dominik Pschenitschni 846de369f2 chore(parseSubtasksViaIndention): fix comment (#3259)
continuous-integration/drone/push Build is passing Details
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: #3259
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-03-18 07:41:36 +00:00
Frederick [Bot] 4d865af423 [skip ci] Updated translations via Crowdin 2023-03-18 00:06:08 +00:00
renovate 62ad01fc8f chore(deps): update dependency esbuild to v0.17.12
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-17 07:05:23 +00:00
renovate da0164b97d chore(deps): update histoire to v0.15.9
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-16 23:05:16 +00:00
284 changed files with 24206 additions and 19277 deletions

View File

@ -42,11 +42,12 @@ steps:
# - .cache
- name: dependencies
image: node:18-alpine
image: node:20.8-alpine
pull: always
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
CYPRESS_CACHE_FOLDER: .cache/cypress
PUPPETEER_SKIP_DOWNLOAD: true
commands:
- corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm install --fetch-timeout 100000
@ -54,7 +55,7 @@ steps:
# - restore-cache
- name: lint
image: node:18-alpine
image: node:20.8-alpine
pull: always
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
@ -65,7 +66,7 @@ steps:
- dependencies
- name: build-prod
image: node:18-alpine
image: node:20.8-alpine
pull: always
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
@ -76,7 +77,7 @@ steps:
- dependencies
- name: test-unit
image: node:18-alpine
image: node:20.8-alpine
pull: always
commands:
- corepack enable && pnpm config set store-dir .cache/pnpm
@ -86,7 +87,7 @@ steps:
- name: typecheck
failure: ignore
image: node:18-alpine
image: node:20.8-alpine
pull: always
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
@ -136,8 +137,9 @@ steps:
# - dependencies
- name: deploy-preview
image: node:18-alpine
image: williamjackson/netlify-cli
pull: always
user: root # The rest runs as root and thus the permissions wouldn't work
environment:
NETLIFY_AUTH_TOKEN:
from_secret: netlify_auth_token
@ -200,10 +202,15 @@ steps:
# - .cache
- name: build
image: node:18-alpine
image: node:20.8-alpine
pull: always
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
SENTRY_AUTH_TOKEN:
from_secret: sentry_auth_token
SENTRY_ORG: vikunja
SENTRY_PROJECT: frontend-oss
PUPPETEER_SKIP_DOWNLOAD: true
commands:
- apk add git
- corepack enable && pnpm config set store-dir .cache/pnpm
@ -219,6 +226,7 @@ steps:
image: kolaente/zip
pull: always
commands:
- cp src/version.json dist
- cd dist
- zip -r ../vikunja-frontend-unstable.zip *
- cd ..
@ -277,10 +285,14 @@ steps:
# - .cache
- name: build
image: node:18-alpine
image: node:20.8-alpine
pull: always
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
SENTRY_AUTH_TOKEN:
from_secret: sentry_auth_token
SENTRY_ORG: vikunja
SENTRY_PROJECT: frontend-oss
commands:
- apk add git
- corepack enable && pnpm config set store-dir .cache/pnpm
@ -296,6 +308,7 @@ steps:
image: kolaente/zip
pull: always
commands:
- cp src/version.json dist
- cd dist
- zip -r ../vikunja-frontend-${DRONE_TAG##v}.zip *
- cd ..
@ -347,8 +360,7 @@ type: docker
name: docker-release
depends_on:
- release-latest
- release-version
- build
trigger:
ref:
@ -376,8 +388,7 @@ steps:
repo: vikunja/frontend
tags: unstable
build_args:
- USE_RELEASE=true
- RELEASE_VERSION=unstable
- USE_RELEASE=false
platforms:
- linux/386
- linux/amd64
@ -411,8 +422,7 @@ steps:
from_secret: docker_password
repo: vikunja/frontend
build_args:
- USE_RELEASE=true
- RELEASE_VERSION=${DRONE_TAG##v}
- USE_RELEASE=false
platforms:
- linux/386
- linux/amd64
@ -465,24 +475,25 @@ name: update-translations
trigger:
branch:
- main
include:
- main
event:
- cron
include:
- cron
cron:
- update_translations
steps:
- name: download
pull: always
image: jonasfranz/crowdin
image: git.lcomrade.su/root/drone-crowdin-v2
settings:
download: true
export_dir: src/i18n/lang/
ignore_branch: true
project_identifier: vikunja
environment:
CROWDIN_KEY:
crowdin_key:
from_secret: crowdin_key
project_id: 462614
target: download
download_to: src/i18n/lang/
download_export_approved_only: true
- name: move-files
pull: always
@ -505,23 +516,22 @@ steps:
commit_message: "[skip ci] Updated translations via Crowdin"
remote: "ssh://git@kolaente.dev:9022/vikunja/frontend.git"
ssh_key:
from_secret: translation_git_push_ssh_key
from_secret: git_push_ssh_key
- name: upload
pull: always
image: jonasfranz/crowdin
image: git.lcomrade.su/root/drone-crowdin-v2
depends_on:
- clone
settings:
files:
en.json: src/i18n/lang/en.json
ignore_branch: true
project_identifier: vikunja
environment:
CROWDIN_KEY:
from_secret: crowdin_key
crowdin_key:
from_secret: crowdin_key
project_id: 462614
target: upload
upload_files:
src/i18n/lang/en.json: en.json
---
kind: signature
hmac: 303afeb09b75a57ba88720b45dc06c8bf2c7320e19d738d8299f325438246f75
hmac: 3678445f9053754ee4aae3e8888f8275e592711b84f0a1f013da117c9c1f44ce
...

23
.github/workflows/lockdown.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: 'Repo Lockdown'
on:
pull_request_target:
types: opened
permissions:
issues: write
pull-requests: write
jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/repo-lockdown@v3
with:
pr-comment: 'Hi! Thank you for your contribution.
This repo is only a mirror and unfortunately we can''t accept PRs made here. Please re-submit your changes to [our Gitea instance](https://kolaente.dev/vikunja/frontend/pulls).
Also check out the [contribution guidelines](https://vikunja.io/docs/development/#pull-requests).
Thank you for your understanding.'

14
.npmrc
View File

@ -1,2 +1,14 @@
fetch-timeout=100000
# pnpm settings
# The following settings prepare for the new default value of pnpm 8
# they can be removed directly after having moved to pnpm 8
auto-install-peers=true
fetch-timeout=100000
dedupe-peer-dependents=true
resolve-peers-from-workspace-root=true
save-workspace-protocol=rolling
resolution-mode=lowest-direct
publishConfig.linkDirectory=true
# remove some time after having moved to pnpm 8
use-lockfile-v6=true

2
.nvmrc
View File

@ -1 +1 @@
18.15.0
18.18.2

View File

@ -8,6 +8,7 @@
"lokalise.i18n-ally",
"mgmcdermott.vscode-language-babel",
"mikestead.dotenv",
"Syler.sass-indented"
"Syler.sass-indented",
"zixuanchen.vitest-explorer"
]
}

View File

@ -9,6 +9,743 @@ 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.21.0] - 2023-07-07
### Bug Fixes
* *(Expandable)* Spelling
* *(building)* Let the compiler ignore props interface
* *(ci)* Always pull latest unstable api image for testing
* *(ci)* Directly build docker images and not use releases to avoid caching issues
* *(ci)* Disable puppeteer chrome download
* *(docker)* Copy patches prior to installing dependencies so that the installation actually works
* *(docker)* Don't set nginx worker rlimit
* *(filters)* Load projects after creating a filter
* *(filters)* Load projects after deleting a filter
* *(filters)* Load projects after updating a filter
* *(gantt)* Only update today value when changing to the gantt chart view
* *(i18n)* OrderedList translationid
* *(i18n)* Typo
* *(kanban)* Decrease task count per bucket when deleting a task
* *(kanban)* Don't export buckets as readonly because that makes it impossible to update them, even from within the store
* *(link share)* Default share view should be list, not project
* *(link share)* Redirect to list view after authenticating
* *(navigation)* Favorites project
* *(navigation)* Hide archived subprojects
* *(navigation)* Hide left ul border
* *(navigation)* Highlight saved filters in project view and prevent them from being dragged around
* *(navigation)* Hover state of other menu items
* *(navigation)* Make marking a project as favorite work
* *(navigation)* Make sure the Favorites project shows up when marking or unmarking a task as favorite
* *(navigation)* Make sure updating a project's state works for sub projects as well.
* *(navigation)* Make the styles work again
* *(navigation)* Menu item overflow
* *(navigation)* Nav item width for items without sub projects
* *(navigation)* Show text ellipsis for very long project titles
* *(navigation)* Sidebar top spacing
* *(navigation)* Watcher
* *(project)* Correctly load background when switching from or to a project view
* *(project)* Don't try to read title of undefined project
* *(project)* Duplicate a project without new parent
* *(project)* Make sure the correct tasks are loaded when switching between projects
* *(project)* Set maxRight on projects after opening a task
* *(projects)* Make sure the project hierarchy is properly updated when moving projects between parents
* *(projects)* Update project duplicate api definitions
* *(quick add magic)* Cleanup all assignee properties
* *(quick add magic)* Date parsing with a date at the beginning
* *(quick add magic)* Don't replace the prefix in every occurrence when it is present in the matched part
* *(quick add magic)* Use the project user service to find assignees for quick add magic
* *(reminders)* Align remove icon with the rest
* *(reminders)* Assignment to const when changing a reminder
* *(reminders)* Custom relative highlight now only when a custom relative reminder was actually selected
* *(reminders)* Don't assigne the task
* *(reminders)* Don't assume 30 days are always a month
* *(reminders)* Don't sync negative relative reminder amounts in ui
* *(reminders)* Duplicate reminder for each change
* *(reminders)* Flatpickr styling improvements
* *(reminders)* Properly parse relative reminders which don't have an amount
* *(reminders)* Set date over relative reminder
* *(reminders)* Style flatpickr so that it blends in more
* *(repeat)* Prevent disappearing repeat mode settings when modes other than default repeat mode were selected
* *(sentry)* Don't fail the build when sentry upload fails
* *(sentry)* Use correct environment from vite env mode
* *(settings)* Don't try to sort timezones if there are none
* *(task detail view)* Make project display show the task's project
* *(task)* Break long task titles after 4 lines only
* *(task)* Call getting task identifier directly instead of using model function
* *(task)* Make an attachment cover image
* *(task)* Repeat mode now saves correctly
* *(tests)* Make sure the task is created with a bucket
* *(tests)* New project input field
* *(tests)* Project archived filter checkbox selector
* *(tests)* Wait for request instead of fixed time
* *(user)* Fix flickering of default settings
* *(user)* Lint* Fix comment
* *(user)* Set the language when saving
* Add await ([9d9fb95](9d9fb959d8f1c4a12110f1a988115116085b6aaf))
* Add default for level ([9402344](9402344b7ea70359c592412b6c341897e45c6069))
* Add interval to uses of useNow so that it uses less resources ([b77c7c2](b77c7c2f45495a0fe6d132b5f569e807074c6d12))
* Add more padding to the textarea ([dfa6cd7](dfa6cd777bc5d03cf88d62db9008aa0b366aa806))
* Add spacing between checkbox and title of related task ([62825d2](62825d2e6409e08ab3229bf693ed068198e18085))
* Allow icon changes configuration via env (#3567) ([57218d1](57218d14548bf1d4cd59f6976e84cf178023305d))
* Avoid crashing browser processes during tests ([7b05ed9](7b05ed9d3d24e07a6535f2462d215c47b6650be1))
* Bottom margin of project header ([1a94496](1a9449680114212eeb93be2aba3f10c416f67e78))
* Bubble changes from the editor immediately and move the delay to callers ([f4a7943](f4a79436809d13e1d2c5337f79358c15310d08d2))
* Checkbox label size based on icon ([fd699ad](fd699ad777c47764b35345b7ec18a854957ff5d1))
* Clarify user search setting ([ae025e3](ae025e30c659d43cce1e3f8361bd1c4c7cb860da))
* Cleanup unused translation strings ([aaa9d55](aaa9d553d080a83a9fd1bcdece366fb5832831f1))
* Collapsing child projects ([2250918](225091864f9088a07120cd3d36918f3060d57d30))
* Correctly sync filters on upcoming tasks page ([faa6298](faa62985dff877afc54c3510be8d27d493717780))
* Disable autocomplete in assignee search ([64f9f4f](64f9f4fd88a513cbc401aacbeab87695bb9f55bf))
* Don't allow creating a new label from filter view ([4c969f0](4c969f0a427e98b491c49646aaf19e19cf9ec924))
* Don't require variant prop on loading component as it already has a default one set ([01ac84c](01ac84ce1eda1de79fd752792115a71cb5c15698))
* Don't set the current project when setting a project ([31b7c1f](31b7c1f217532bf388ba95a03f469508bee46f6a))
* Don't show > for top-level projects ([03f4d0b](03f4d0b8bcba90b19302d6c6d2fbb92460b59957))
* Don't show child projects when the project is only a favorite ([0a17df8](0a17df87e950b8043578dbb7e9f12d5937802169))
* Don't try to convert a null date ([4ba02eb](4ba02ebbb6be4b96a42688b3ec8f29fe923aee0b))
* Don't try to map data from empty responses ([a118580](a11858070496614c492da321fe461b72c31afe5a))
* Don't try to map non-array data ([813d2b5](813d2b56a06cbd28a1bf0d01b64685a1b49188d0))
* Don't try to set a user language if none is saved ([68fd469](68fd4698ac443345dc7dbbf8cfebf76ec467b6ec))
* Don't try to set config from non-json responses ([7c1934a](7c1934aad0e5fcd0f785896d57efc34c6df935cd))
* Ensure all matched quick add magic parts are correctly removed from the task ([7b6a13d](7b6a13dd52dfa06e6093ae30adad1b86b66610e1))
* Ensure same protocol for configured api url (#3303) ([6c999ad](6c999ad14844b4f9ec74dc225895db6a12e4a781))
* Follow the happy path ([34182b8](34182b8bbb7a7e8eeb0ce698dc6da79785d05fc9))
* Force usage of @types for flexsearch instead of integrated types ([f60cebf](f60cebf42cb73d9fd2d9fea8de5bbeb96a724d47))
* Has-pseudo-class polyfill ([4703f9c](4703f9c4d5e3902d0fc389d447aa9a7da2e2dd4a))
* Ignore ts deprecations for now ([96e2c81](96e2c81b7ef2b7a0ff515dd01d9aeb28429cc0d5))
* Improve projectView storing and add migration ([842f204](842f204123afc3b9b4633b68de58cffb3af4f912))
* Improve the "pop" sound a bit ([3643ffe](3643ffe0d0357c89cb3517fafbb0c438188ac88d))
* Improve tooltip icon contrast ([a6cdf6c](a6cdf6c4bdceb1168f20e9d049c2e66f40c98aa1))
* Improve tooltip text ([2174608](21746088012f4fe0f750ed5e5cac916d506fb17b))
* Increase default auto-save timeout to 5 seconds ([f7ba3bd](f7ba3bd08fa9181180f99f4e5ebd5ec916fbcf19))
* Indention ([e25273d](e25273df4899867ee146159d3d18125d387f8524))
* Lint ([292c904](292c90425ef96b99671702a0b28d87d660fa53dc))
* Lint ([4ff0c81](4ff0c81e373696b0505c2c080d558a20071562f3))
* Lint ([5d59392](5d593925666a09cbfda2f62577deb670033f93fb))
* Lint ([9ec29ca](9ec29cad300fe1c25cb355fb86e165ca920df511))
* Lint ([c294f9d](c294f9d28d3e793f8151265d5a16ed2fc53aea92))
* Lint ([c74612f](c74612f24adeb4aceafe9fc9b3264b1dfe84d128))
* Lint ([cd2b7fe](cd2b7fe185632253290838e405b8a2666b15ce24))
* Lint ([ed8de7e](ed8de7e3eb78f6723d5f675cca18c014b252ed64))
* List view: don't sort tasks after marking one "done" (#3285) ([6870db4](6870db4a72568f183134a6dd2d4af687dd7c839d))
* Load the correct language ([6593380](6593380013ff6043b846126ac67e6f96442a1c5b))
* Make check if projects are available work again ([5e65814](5e65814b8c5b37f3962856f2809f7cc85756da1e))
* Make computed side-effect free ([26bec05](26bec0517417afc52db93f0fdc4c48d47ed5c131))
* Make sure redirects to a saved view work as intended ([a64c0c1](a64c0c19e5a7da36ae4993fce443e4f37e3a4572))
* Make sure the unread notifications indicator is correctly positioned ([8b90b45](8b90b45739418f447b885fc9b37438e325f61b32))
* Make tests work again ([5685890](56858904938126dbfa8ade2e88e3ec6c4fff3a6f))
* Make type singular ([bc416f2](bc416f282f13d2b81782aba4b0d68b71f26c83e8))
* Make update available button use the correct text color all the time ([ae2b0f9](ae2b0f97c4bb50d4ff493af2af132f9740d16d49))
* Missing await ([391992e](391992effbb424a107ff060e7175884740a28c62))
* Missing variant prop for loading component ([2e9ade1](2e9ade11c3a3b6cb531d053f82a598a5ab851a93))
* Move parent project child id mutation to store ([26e3d42](26e3d42ed527afd6bf695ba3ad291e1c2b545bba))
* Move parent project handling out of useProject ([ba452ab](ba452ab88339b9ace987f1a18584a7950e00a776))
* Move the collapsable placeholder to the button ([1344026](1344026494fe47ac5604bff07b537a2765e840f6))
* Move types to dev dependencies ([739fe0c](739fe0caa13dc946e1801f290d8ab5f18cdc5faf))
* Only bind child projects data down ([3eca9f6](3eca9f6180e64f892e94d27eaa192cea780563a0))
* Only update daytime salutation when switching to home view ([c577626](c5776264c069000efbb62c64dfc2143d5fc4e0df))
* Passing readonly projects data to navigation ([d85be26](d85be26761240164b6bdcbe0601b46585b74fafa))
* Properly determine if there are projects ([a2cc9dd](a2cc9ddc8821a4b9b1ee1dd6109d1b3958a06ba6))
* Rebase readd CustomTransition ([b93639e](b93639e14ecab06496086c3d2cc14f51d8f9f672))
* Recreate project instead of editing before ([175e31c](175e31ca629660d8d683b35b8e7c8052a62cd17d))
* Redundant ) ([6c2dc48](6c2dc483a20213f1f238e6224b9ecfb87faa2461))
* Remove getProjectById and replace all usages of it ([78158bc](78158bcba52d152a2ebf465242e25a55e6764470))
* Remove leftover suspense ([9d73ac6](9d73ac661fbf9315995c8a1f633708021591d2db))
* Remove leftovers of childIds ([bbaddb9](bbaddb9406910106b7d476a6550acff025e72655))
* Remove namespace routes ([10311b7](10311b79df36db44a8e96a446234c3c6d6aa6ec7))
* Remove namespace store reference ([ad2690b](ad2690b21cfc9ccc658737a726cc6b110089b635))
* Remove unnecessary fallback ([d414b65](d414b65e7d591f567067ce8085b9934207dc938a))
* Rename getParentProjects method to make it clear what it does ([39f699a](39f699a61ae91eb93c364137f76b595e7cad7561))
* Rename list to project for parsing subtasks via indention ([fc8711d](fc8711d6d841d11847cd8567999373145ce3398d))
* Rename resolveRef ([f14e721](f14e721caf9434ac119f32c5e7f107bfbdd6746c))
* Return redirect ([7c964c2](7c964c29d487b5bcd2c125f81731e3b37374641a))
* Return updated project instead of the old one ([4ab5478](4ab547810c77e747e701ea865c13157d51aba461))
* Review findings ([5fb45af](5fb45afb12479eb135323299409bd91d8be24e39))
* Review findings ([85ffed4](85ffed4d9a26fc054fee51608bb83ccf2e3032f9))
* Review findings ([fb14eca](fb14eca6340ac4c761b8f61027662328bf55ade4))
* Route to create new project ([a5e710b](a5e710bfe594e06262b9ef46fa6b56ad637b8156))
* Set and use correct type for destructured props ([dbe1ad9](dbe1ad9353e165fd1e314cc72c7a4dece1c47d38))
* Set vue-ignore ([b6cd424](b6cd424aa30be3bd715c0b7555032fc80149ae7b))
* Show favorite on hover ([0be83db](0be83db40fa96478bfdb4a69e8a995d6debb6f52))
* Simplify sort ([85e882c](85e882cc5940067414004dabd01916f559fbd0ff))
* Sort in store ([46e8258](46e825820c465ebb9f8087e3afe6d74fad8d5159))
* SortBy type import ([d73b71a](d73b71a097755cdb075955a824c26bbaba222aaf))
* Spacing ([9162002](9162002e55d9ebfd0a6c8dbe28aab0c15f95b7e2))
* Style: "favorite" button being shown on projects without hovering ([ee4974a](ee4974a4948012b03adcedd956c0d907c57431c9))
* Switching to view type now ([060a573](060a573fe9006441131fd98c4618c5d294cf39b7))
* Tests ([69e94e5](69e94e58c451a5115c713696798f8fcbf8f787b3))
* Translation string ([f13db92](f13db9268a8862204522a8d68ad7b51edb9d91e1))
* Tsconfig as per https://github.com/vuejs/tsconfig#configuration-for-node-environments ([05b7063](05b70632c55ce34ee6471cc372334fc6e14c99a0))
* Tsconfig as per https://github.com/vuejs/tsconfig#configuration-for-node-environments ([ca9fe6f](ca9fe6ff215351c3f4c8de65a333f5cfd5876488))
* Undefined parent project when none was selected ([6cc11e6](6cc11e64ab392f8e8e69070000a748c04746e550))
* Undo further nesting of interactive items ([0acf447](0acf44778d0ab2a317bcfb3e89aa0292e2d5c2ed))
* Update logo change only every hour ([7126576](71265769cefb91be9a51fceff0a04095a9dd7e72))
* Use correct shortcut to open projects overview ([326b6ed](326b6eda6fce6554ea6e215c681466374657902a))
* Use menu tag everywhere ([0dd6f82](0dd6f82a0e198056724821c2bb56c6b9807ea451))
* Use onActivated ([a33fb72](a33fb72ef86112c6f29017bb951ff4e1ee611ed6))
* Use props destructuring everywhere ([3aa502e](3aa502e07d89314e885c252e1e3d4668fa64059b))
* Use strict comparison ([91e9eef](91e9eef5829d2a5ae27099fbd54029ed0ca46818))
* Use the color bubble as handle if the project has a color ([4857080](48570808e55e51751734ddaf4532ad651920d622))
* Use time constant ([a13c16c](a13c16ca03698a24860f8453cdb231c420d0077b))
* Wording ([985f998](985f998a821229d03c7d40d1a81f7fbe5121d585))
### Dependencies
* *(deps)* Install dependencies after rebase
* *(deps)* Pin dependency @tsconfig/node18 to 2.0.0
* *(deps)* Update all dev dependencies at once per day
* *(deps)* Update caniuse-and-related
* *(deps)* Update caniuse-and-related
* *(deps)* Update dependency @4tw/cypress-drag-drop to v2.2.4
* *(deps)* Update dependency @cypress/vite-dev-server to v5.0.5
* *(deps)* Update dependency @cypress/vue to v5.0.5
* *(deps)* Update dependency @faker-js/faker to v8
* *(deps)* Update dependency @faker-js/faker to v8.0.1
* *(deps)* Update dependency @faker-js/faker to v8.0.2
* *(deps)* Update dependency @intlify/unplugin-vue-i18n to v0.10.0
* *(deps)* Update dependency @intlify/unplugin-vue-i18n to v0.11.0
* *(deps)* Update dependency @intlify/unplugin-vue-i18n to v0.12.0
* *(deps)* Update dependency @intlify/unplugin-vue-i18n to v0.12.1
* *(deps)* Update dependency @intlify/unplugin-vue-i18n to v0.9.2
* *(deps)* Update dependency @intlify/unplugin-vue-i18n to v0.9.3
* *(deps)* Update dependency @kyvg/vue3-notification to v2.9.1
* *(deps)* Update dependency @rushstack/eslint-patch to v1.3.0
* *(deps)* Update dependency @rushstack/eslint-patch to v1.3.1
* *(deps)* Update dependency @rushstack/eslint-patch to v1.3.2
* *(deps)* Update dependency @tsconfig/node18 to v18
* *(deps)* Update dependency @tsconfig/node18 to v2.0.1
* *(deps)* Update dependency @types/codemirror to v5.60.8
* *(deps)* Update dependency @types/dompurify to v3
* *(deps)* Update dependency @types/dompurify to v3.0.1
* *(deps)* Update dependency @types/dompurify to v3.0.2
* *(deps)* Update dependency @types/marked to v4.3.0
* *(deps)* Update dependency @types/marked to v4.3.1
* *(deps)* Update dependency @types/marked to v5
* *(deps)* Update dependency @types/node to v18.15.1
* *(deps)* Update dependency @types/node to v18.15.10
* *(deps)* Update dependency @types/node to v18.15.11
* *(deps)* Update dependency @types/node to v18.15.12
* *(deps)* Update dependency @types/node to v18.15.13
* *(deps)* Update dependency @types/node to v18.15.2
* *(deps)* Update dependency @types/node to v18.15.3
* *(deps)* Update dependency @types/node to v18.15.5
* *(deps)* Update dependency @types/node to v18.15.6
* *(deps)* Update dependency @types/node to v18.15.7
* *(deps)* Update dependency @types/node to v18.15.8
* *(deps)* Update dependency @types/node to v18.15.9
* *(deps)* Update dependency @types/node to v18.16.0
* *(deps)* Update dependency @types/node to v18.16.1
* *(deps)* Update dependency @types/node to v18.16.10
* *(deps)* Update dependency @types/node to v18.16.11
* *(deps)* Update dependency @types/node to v18.16.14
* *(deps)* Update dependency @types/node to v18.16.16
* *(deps)* Update dependency @types/node to v18.16.17
* *(deps)* Update dependency @types/node to v18.16.18
* *(deps)* Update dependency @types/node to v18.16.19
* *(deps)* Update dependency @types/node to v18.16.2
* *(deps)* Update dependency @types/node to v18.16.3
* *(deps)* Update dependency @types/node to v18.16.4
* *(deps)* Update dependency @types/node to v18.16.5
* *(deps)* Update dependency @types/node to v18.16.6
* *(deps)* Update dependency @types/node to v18.16.7
* *(deps)* Update dependency @types/node to v18.16.8
* *(deps)* Update dependency @types/node to v18.16.9
* *(deps)* Update dependency @types/sortablejs to v1.15.1
* *(deps)* Update dependency @vitejs/plugin-legacy to v4.0.2
* *(deps)* Update dependency @vitejs/plugin-legacy to v4.0.3
* *(deps)* Update dependency @vitejs/plugin-legacy to v4.0.4
* *(deps)* Update dependency @vitejs/plugin-legacy to v4.0.5
* *(deps)* Update dependency @vitejs/plugin-vue to v4.1.0
* *(deps)* Update dependency @vitejs/plugin-vue to v4.2.0
* *(deps)* Update dependency @vitejs/plugin-vue to v4.2.1
* *(deps)* Update dependency @vitejs/plugin-vue to v4.2.2
* *(deps)* Update dependency @vitejs/plugin-vue to v4.2.3
* *(deps)* Update dependency @vue/eslint-config-typescript to v11.0.3
* *(deps)* Update dependency @vue/test-utils to v2.3.2
* *(deps)* Update dependency @vue/test-utils to v2.4.0
* *(deps)* Update dependency @vue/tsconfig to v0.3.2
* *(deps)* Update dependency @vue/tsconfig to v0.4.0
* *(deps)* Update dependency @vueuse/core to v10
* *(deps)* Update dependency @vueuse/core to v10.0.2
* *(deps)* Update dependency @vueuse/core to v10.1.0
* *(deps)* Update dependency @vueuse/core to v10.1.2
* *(deps)* Update dependency @vueuse/core to v10.2.0
* *(deps)* Update dependency @vueuse/core to v10.2.1
* *(deps)* Update dependency axios to v1.3.5
* *(deps)* Update dependency axios to v1.3.6
* *(deps)* Update dependency axios to v1.4.0
* *(deps)* Update dependency caniuse-lite to v1.0.30001465
* *(deps)* Update dependency caniuse-lite to v1.0.30001468
* *(deps)* Update dependency caniuse-lite to v1.0.30001470
* *(deps)* Update dependency caniuse-lite to v1.0.30001473
* *(deps)* Update dependency caniuse-lite to v1.0.30001477
* *(deps)* Update dependency caniuse-lite to v1.0.30001479
* *(deps)* Update dependency caniuse-lite to v1.0.30001481
* *(deps)* Update dependency caniuse-lite to v1.0.30001486
* *(deps)* Update dependency caniuse-lite to v1.0.30001487
* *(deps)* Update dependency caniuse-lite to v1.0.30001489
* *(deps)* Update dependency caniuse-lite to v1.0.30001500
* *(deps)* Update dependency caniuse-lite to v1.0.30001508
* *(deps)* Update dependency caniuse-lite to v1.0.30001511
* *(deps)* Update dependency codemirror to v5.65.13
* *(deps)* Update dependency css-has-pseudo to v6
* *(deps)* Update dependency csstype to v3.1.2
* *(deps)* Update dependency cypress to v12.10.0
* *(deps)* Update dependency cypress to v12.11.0
* *(deps)* Update dependency cypress to v12.12.0
* *(deps)* Update dependency cypress to v12.13.0
* *(deps)* Update dependency cypress to v12.14.0
* *(deps)* Update dependency cypress to v12.15.0
* *(deps)* Update dependency cypress to v12.16.0
* *(deps)* Update dependency cypress to v12.8.0
* *(deps)* Update dependency cypress to v12.8.1
* *(deps)* Update dependency cypress to v12.9.0
* *(deps)* Update dependency date-fns to v2.30.0
* *(deps)* Update dependency dayjs to v1.11.8
* *(deps)* Update dependency dayjs to v1.11.9
* *(deps)* Update dependency dompurify to v3.0.2
* *(deps)* Update dependency dompurify to v3.0.3
* *(deps)* Update dependency dompurify to v3.0.4
* *(deps)* Update dependency esbuild to v0.17.12
* *(deps)* Update dependency esbuild to v0.17.13
* *(deps)* Update dependency esbuild to v0.17.14
* *(deps)* Update dependency esbuild to v0.17.15
* *(deps)* Update dependency esbuild to v0.17.16
* *(deps)* Update dependency esbuild to v0.17.17
* *(deps)* Update dependency esbuild to v0.17.18
* *(deps)* Update dependency esbuild to v0.17.19
* *(deps)* Update dependency esbuild to v0.18.0
* *(deps)* Update dependency esbuild to v0.18.1
* *(deps)* Update dependency esbuild to v0.18.10
* *(deps)* Update dependency esbuild to v0.18.11
* *(deps)* Update dependency esbuild to v0.18.2
* *(deps)* Update dependency esbuild to v0.18.3
* *(deps)* Update dependency esbuild to v0.18.4
* *(deps)* Update dependency esbuild to v0.18.5
* *(deps)* Update dependency esbuild to v0.18.6
* *(deps)* Update dependency esbuild to v0.18.9
* *(deps)* Update dependency eslint to v8.37.0
* *(deps)* Update dependency eslint to v8.38.0
* *(deps)* Update dependency eslint to v8.39.0
* *(deps)* Update dependency eslint to v8.40.0
* *(deps)* Update dependency eslint to v8.41.0
* *(deps)* Update dependency eslint to v8.42.0
* *(deps)* Update dependency eslint to v8.43.0
* *(deps)* Update dependency eslint to v8.44.0
* *(deps)* Update dependency eslint-plugin-vue to v9.10.0
* *(deps)* Update dependency eslint-plugin-vue to v9.11.0
* *(deps)* Update dependency eslint-plugin-vue to v9.11.1
* *(deps)* Update dependency eslint-plugin-vue to v9.12.0
* *(deps)* Update dependency eslint-plugin-vue to v9.13.0
* *(deps)* Update dependency flexsearch to v0.7.31
* *(deps)* Update dependency floating-vue to v2.0.0-beta.21
* *(deps)* Update dependency floating-vue to v2.0.0-beta.22
* *(deps)* Update dependency floating-vue to v2.0.0-beta.24
* *(deps)* Update dependency happy-dom to v9
* *(deps)* Update dependency happy-dom to v9.1.9
* *(deps)* Update dependency happy-dom to v9.10.1
* *(deps)* Update dependency happy-dom to v9.10.9
* *(deps)* Update dependency happy-dom to v9.18.3
* *(deps)* Update dependency happy-dom to v9.20.1
* *(deps)* Update dependency happy-dom to v9.20.3
* *(deps)* Update dependency happy-dom to v9.7.1
* *(deps)* Update dependency happy-dom to v9.9.2
* *(deps)* Update dependency highlight.js to v11.8.0
* *(deps)* Update dependency histoire to v0.16.2
* *(deps)* Update dependency marked to v4.3.0
* *(deps)* Update dependency marked to v5
* *(deps)* Update dependency marked to v5.0.1
* *(deps)* Update dependency marked to v5.0.2
* *(deps)* Update dependency marked to v5.0.3
* *(deps)* Update dependency marked to v5.0.4
* *(deps)* Update dependency marked to v5.0.5
* *(deps)* Update dependency marked to v5.1.0
* *(deps)* Update dependency netlify-cli to v13.1.2
* *(deps)* Update dependency netlify-cli to v13.1.6
* *(deps)* Update dependency netlify-cli to v13.2.1
* *(deps)* Update dependency netlify-cli to v13.2.2
* *(deps)* Update dependency netlify-cli to v14
* *(deps)* Update dependency netlify-cli to v14.3.1
* *(deps)* Update dependency pinia to v2.0.34
* *(deps)* Update dependency pinia to v2.0.35
* *(deps)* Update dependency pinia to v2.0.36
* *(deps)* Update dependency pinia to v2.1.4
* *(deps)* Update dependency postcss to v8.4.22
* *(deps)* Update dependency postcss to v8.4.23
* *(deps)* Update dependency postcss to v8.4.24
* *(deps)* Update dependency postcss-preset-env to v8.1.0
* *(deps)* Update dependency postcss-preset-env to v8.2.0
* *(deps)* Update dependency postcss-preset-env to v8.3.0
* *(deps)* Update dependency postcss-preset-env to v8.3.1
* *(deps)* Update dependency postcss-preset-env to v8.3.2
* *(deps)* Update dependency postcss-preset-env to v8.4.1
* *(deps)* Update dependency postcss-preset-env to v8.4.2
* *(deps)* Update dependency postcss-preset-env to v8.5.0
* *(deps)* Update dependency postcss-preset-env to v8.5.1
* *(deps)* Update dependency rollup to v3.20.0
* *(deps)* Update dependency rollup to v3.20.1
* *(deps)* Update dependency rollup to v3.20.2
* *(deps)* Update dependency rollup to v3.20.3
* *(deps)* Update dependency rollup to v3.20.4
* *(deps)* Update dependency rollup to v3.20.5
* *(deps)* Update dependency rollup to v3.20.6
* *(deps)* Update dependency rollup to v3.20.7
* *(deps)* Update dependency rollup to v3.21.0
* *(deps)* Update dependency rollup to v3.21.1
* *(deps)* Update dependency rollup to v3.21.2
* *(deps)* Update dependency rollup to v3.21.3
* *(deps)* Update dependency rollup to v3.21.4
* *(deps)* Update dependency rollup to v3.21.5
* *(deps)* Update dependency rollup to v3.21.6
* *(deps)* Update dependency rollup to v3.21.7
* *(deps)* Update dependency rollup to v3.21.8
* *(deps)* Update dependency rollup to v3.22.0
* *(deps)* Update dependency rollup to v3.23.0
* *(deps)* Update dependency rollup to v3.23.1
* *(deps)* Update dependency rollup to v3.24.0
* *(deps)* Update dependency rollup to v3.24.1
* *(deps)* Update dependency rollup to v3.25.0
* *(deps)* Update dependency rollup to v3.25.1
* *(deps)* Update dependency rollup to v3.25.2
* *(deps)* Update dependency rollup to v3.25.3
* *(deps)* Update dependency rollup to v3.26.0
* *(deps)* Update dependency rollup-plugin-visualizer to v5.9.2
* *(deps)* Update dependency sass to v1.59.3
* *(deps)* Update dependency sass to v1.60.0
* *(deps)* Update dependency sass to v1.61.0
* *(deps)* Update dependency sass to v1.62.0
* *(deps)* Update dependency sass to v1.62.1
* *(deps)* Update dependency sass to v1.63.0
* *(deps)* Update dependency sass to v1.63.2
* *(deps)* Update dependency sass to v1.63.3
* *(deps)* Update dependency sass to v1.63.4
* *(deps)* Update dependency sass to v1.63.5
* *(deps)* Update dependency sass to v1.63.6
* *(deps)* Update dependency typescript to v5
* *(deps)* Update dependency typescript to v5.0.3
* *(deps)* Update dependency typescript to v5.0.4
* *(deps)* Update dependency typescript to v5.1.3
* *(deps)* Update dependency typescript to v5.1.5
* *(deps)* Update dependency typescript to v5.1.6
* *(deps)* Update dependency ufo to v1.1.2
* *(deps)* Update dependency vite to v4.2.0
* *(deps)* Update dependency vite to v4.2.1
* *(deps)* Update dependency vite to v4.2.2
* *(deps)* Update dependency vite to v4.3.0
* *(deps)* Update dependency vite to v4.3.1
* *(deps)* Update dependency vite to v4.3.2
* *(deps)* Update dependency vite to v4.3.3
* *(deps)* Update dependency vite to v4.3.4
* *(deps)* Update dependency vite to v4.3.5
* *(deps)* Update dependency vite to v4.3.6
* *(deps)* Update dependency vite to v4.3.7
* *(deps)* Update dependency vite to v4.3.8
* *(deps)* Update dependency vite to v4.3.9
* *(deps)* Update dependency vite-plugin-pwa to v0.14.5
* *(deps)* Update dependency vite-plugin-pwa to v0.14.6
* *(deps)* Update dependency vite-plugin-pwa to v0.14.7
* *(deps)* Update dependency vite-plugin-pwa to v0.15.0
* *(deps)* Update dependency vite-plugin-pwa to v0.15.1
* *(deps)* Update dependency vite-plugin-pwa to v0.15.2
* *(deps)* Update dependency vite-plugin-pwa to v0.16.1
* *(deps)* Update dependency vite-plugin-pwa to v0.16.3
* *(deps)* Update dependency vite-plugin-pwa to v0.16.4
* *(deps)* Update dependency vite-plugin-sentry to v1.3.0
* *(deps)* Update dependency vitest to v0.29.3
* *(deps)* Update dependency vitest to v0.29.4
* *(deps)* Update dependency vitest to v0.29.5
* *(deps)* Update dependency vitest to v0.29.7
* *(deps)* Update dependency vitest to v0.29.8
* *(deps)* Update dependency vitest to v0.30.0
* *(deps)* Update dependency vitest to v0.30.1
* *(deps)* Update dependency vitest to v0.31.0
* *(deps)* Update dependency vitest to v0.31.1
* *(deps)* Update dependency vitest to v0.31.2
* *(deps)* Update dependency vitest to v0.31.4
* *(deps)* Update dependency vitest to v0.32.0
* *(deps)* Update dependency vitest to v0.32.1
* *(deps)* Update dependency vitest to v0.32.2
* *(deps)* Update dependency vitest to v0.32.3
* *(deps)* Update dependency vue to v3.3.4
* *(deps)* Update dependency vue to v3.3.4
* *(deps)* Update dependency vue-flatpickr-component to v11.0.3
* *(deps)* Update dependency vue-router to v4.2.0
* *(deps)* Update dependency vue-router to v4.2.1
* *(deps)* Update dependency vue-router to v4.2.2
* *(deps)* Update dependency vue-router to v4.2.3
* *(deps)* Update dependency vue-router to v4.2.4
* *(deps)* Update dependency vue-tsc to v1.4.0
* *(deps)* Update dependency vue-tsc to v1.4.1
* *(deps)* Update dependency vue-tsc to v1.4.2
* *(deps)* Update dependency vue-tsc to v1.4.3
* *(deps)* Update dependency vue-tsc to v1.4.4
* *(deps)* Update dependency vue-tsc to v1.6.0
* *(deps)* Update dependency vue-tsc to v1.6.1
* *(deps)* Update dependency vue-tsc to v1.6.2
* *(deps)* Update dependency vue-tsc to v1.6.3
* *(deps)* Update dependency vue-tsc to v1.6.4
* *(deps)* Update dependency vue-tsc to v1.6.5
* *(deps)* Update dependency vue-tsc to v1.8.0
* *(deps)* Update dependency vue-tsc to v1.8.1
* *(deps)* Update dependency vue-tsc to v1.8.2
* *(deps)* Update dependency vue-tsc to v1.8.3
* *(deps)* Update dev-dependencies
* *(deps)* Update dev-dependencies
* *(deps)* Update dev-dependencies
* *(deps)* Update dev-dependencies
* *(deps)* Update dev-dependencies
* *(deps)* Update flake
* *(deps)* Update font awesome to v6.4.0
* *(deps)* Update histoire to v0.15.9
* *(deps)* Update histoire to v0.16.0
* *(deps)* Update histoire to v0.16.1
* *(deps)* Update lockfile
* *(deps)* Update node.js to v18.16.0
* *(deps)* Update node.js to v18.16.1
* *(deps)* Update node.js to v20 (#3411)
* *(deps)* Update pnpm to v7.29.3
* *(deps)* Update pnpm to v7.30.0
* *(deps)* Update pnpm to v7.30.1
* *(deps)* Update pnpm to v7.30.2
* *(deps)* Update pnpm to v7.30.3
* *(deps)* Update pnpm to v7.30.5
* *(deps)* Update pnpm to v7.31.0
* *(deps)* Update pnpm to v7.32.0
* *(deps)* Update pnpm to v8
* *(deps)* Update pnpm to v8.3.0
* *(deps)* Update pnpm to v8.3.1
* *(deps)* Update pnpm to v8.4.0
* *(deps)* Update pnpm to v8.5.0
* *(deps)* Update pnpm to v8.5.1
* *(deps)* Update pnpm to v8.6.0
* *(deps)* Update pnpm to v8.6.1
* *(deps)* Update pnpm to v8.6.2
* *(deps)* Update pnpm to v8.6.3
* *(deps)* Update pnpm to v8.6.4
* *(deps)* Update pnpm to v8.6.5
* *(deps)* Update pnpm to v8.6.6
* *(deps)* Update sentry-javascript monorepo to v7.43.0
* *(deps)* Update sentry-javascript monorepo to v7.44.0
* *(deps)* Update sentry-javascript monorepo to v7.44.1
* *(deps)* Update sentry-javascript monorepo to v7.44.2
* *(deps)* Update sentry-javascript monorepo to v7.45.0
* *(deps)* Update sentry-javascript monorepo to v7.46.0
* *(deps)* Update sentry-javascript monorepo to v7.47.0
* *(deps)* Update sentry-javascript monorepo to v7.48.0
* *(deps)* Update sentry-javascript monorepo to v7.49.0
* *(deps)* Update sentry-javascript monorepo to v7.50.0
* *(deps)* Update sentry-javascript monorepo to v7.51.0
* *(deps)* Update sentry-javascript monorepo to v7.51.2
* *(deps)* Update sentry-javascript monorepo to v7.52.0
* *(deps)* Update sentry-javascript monorepo to v7.52.1
* *(deps)* Update sentry-javascript monorepo to v7.53.0
* *(deps)* Update sentry-javascript monorepo to v7.53.1
* *(deps)* Update sentry-javascript monorepo to v7.54.0
* *(deps)* Update sentry-javascript monorepo to v7.55.0
* *(deps)* Update sentry-javascript monorepo to v7.55.2
* *(deps)* Update sentry-javascript monorepo to v7.56.0
* *(deps)* Update sentry-javascript monorepo to v7.57.0
* *(deps)* Update typescript-eslint monorepo to v5.55.0
* *(deps)* Update typescript-eslint monorepo to v5.56.0
* *(deps)* Update typescript-eslint monorepo to v5.57.0
* *(deps)* Update typescript-eslint monorepo to v5.57.1
* *(deps)* Update typescript-eslint monorepo to v5.58.0
* *(deps)* Update typescript-eslint monorepo to v5.59.0
* *(deps)* Update typescript-eslint monorepo to v5.59.1
* *(deps)* Update typescript-eslint monorepo to v5.59.11
* *(deps)* Update typescript-eslint monorepo to v5.59.2
* *(deps)* Update typescript-eslint monorepo to v5.59.5
* *(deps)* Update typescript-eslint monorepo to v5.59.6
* *(deps)* Update typescript-eslint monorepo to v5.59.7
* *(deps)* Update typescript-eslint monorepo to v5.59.8
* *(deps)* Update typescript-eslint monorepo to v5.59.9
* *(deps)* Update typescript-eslint monorepo to v5.60.0
* *(deps)* Update typescript-eslint monorepo to v5.60.1
* *(deps)* Update workbox monorepo to v6.6.0 (#3548)
* *(deps)* Update workbox monorepo to v6.6.1 (#3553)
* *(deps)* Update workbox monorepo to v7 (major) (#3556)
### Features
* *(assignees)* Show user avatar in search results
* *(datepicker)* Separate datepicker popup and datepicker logic in different components
* *(i18n)* Enable Danish translation
* *(i18n)* Enable Japanese translation
* *(i18n)* Enable Spanish translation
* *(i18n)* Use chinese name for chinese translation
* *(kanban)* Use total task count from the api instead of manually calculating it per bucket
* *(link share)* Add e2e tests for link share hash
* *(navigation)* Add hiding child projects
* *(navigation)* Allow dragging a project out from its parent project
* *(navigation)* Correctly show child projects
* *(navigation)* Make dragging a project to a parent work
* *(navigation)* Make dragging a project under another project work
* *(navigation)* Show favorite projects on top
* *(projects)* Allow setting a saved filter for tasks shown on the overview page
* *(projects)* Move hasProjects check to store
* *(quick add magic)* Allow fuzzy matching of assignees when the api results are unambigous
* *(reminders)* Add confirm button
* *(reminders)* Add e2e tests for task reminders
* *(reminders)* Add more spacing
* *(reminders)* Add on the due / start / end date as a reminder preset
* *(reminders)* Add preset two hours before due / start / end date
* *(reminders)* Add proper time picker for relative dates
* *(reminders)* Highlight which preset or custom date is selected
* *(reminders)* Make adding new reminders less confusing
* *(reminders)* Make relative presets actually work
* *(reminders)* Move reminder settings to a popup
* *(reminders)* Only show relative reminders when there's a date to relate them to
* *(reminders)* Show resolved reminder time in a tooltip and properly bubble updated task down to the reminder component
* *(reminders)* Translate all reminder form strings
* *(sentry)* Only load sentry when it's enabled
* *(tests)* Add project tests derived from old namespace tests
* *(user)* Migrate color scheme settings to persistance in db
* *(user)* Migrate pop sound setting to store in api
* *(user)* Persist frontend settings in the api (#3594)* Rename files with list to project ([b9d3b5c](b9d3b5c75635577321acc1791219aed40c6c14a4))
* *(user)* Save quick add magic mode in api
* *(user)* Set default settings when loading persisted
* *(user)* Use user language from store after logging in
* Abstract BaseCheckbox ([8fc254d](8fc254d2db5738e5d370c9f346c8d0d1e31bb9d0))
* Add hotkeys for priority, delete and favorite on the `TaskDetailView` (#3400) ([e00c9bb](e00c9bb1afc8491039b5ffb50d4d8d9b38e6e086))
* Add message to add to home screen on mobile ([3c9083b](3c9083b90dd3e5f97109ba2a23d2f2f8cc7d6c7c))
* Add redirect for old list routes ([af523cf](af523cfcd71528c7e8d0b50874f4766f40f958d2))
* Add setting for infinite nesting ([cb218ec](cb218ec0c31a41ba41a713a3757f71ad550dd71c))
* Add transition to input icons ([abb5128](abb51284269d84de14d0a156c386c63dc596b9ab))
* Add vite-plugin sentry (#1991) ([5ca31d0](5ca31d00eeff28f4728a4d07b96d761a6f174207))
* Add vite-plugin sentry ([73947f0](73947f0ba4031cb0f9aff78f8a7e3316a36d59b4))
* Allow creating a new project directly as a child project from another one ([b341184](b34118485cc056146682cd4592c90e4662b307eb))
* Allow disabling icon changes ([efb3407](efb3407b8769a23f4352161d6db6267ce4b30eee))
* Allow hiding the quick add magic help tooltip with a button ([7fb85da](7fb85dacecdae597180553036243ab845d50ede5))
* Allow selecting a parent project when creating a project ([ce887c3](ce887c38f3a9e84c832bfbf62efa455df37a1a4f))
* Allow selecting a parent project when duplicating a project ([799c0be](799c0be8306cfc5150611153c59701e96d56893a))
* Allow selecting a parent project when editing a project ([ee8f80c](ee8f80cc70109a496959da167d14ffda4e2a6175))
* Allow to edit existing relative reminders ([5d38b83](5d38b8327fc323c571fced33442bdb923d6d3baa))
* Better vscode vitest integration ([314cbf4](314cbf471f8e9cff2a3fca6bbd969807401b5cda))
* Change the link share hash name ([2066056](20660564c16283c77029bc3c3125c6c3febde47e))
* Check link share auth from store instead ([c2ffe3a](c2ffe3a9dcfd1e067b8d92e1d69183c2a8acfa8f))
* Don't handle child projects and instead only save the ids ([760efa8](760efa854dcc83e74f96782339b79b8d27b853b2))
* Don't use child_projects property from api ([ebd9c47](ebd9c4702ed1c6920d47e5e42294e6d4fa3c73c0))
* Edit relative reminders (#3248) ([3f8e457](3f8e457d5250df0b3af34d8f3bb0c053b15a97be))
* Edit relative reminders ([14e2698](14e26988331ca72afae01b8264969458cdb4a509))
* Hide quick add magic help behind a tooltip (#3353) ([a988565](a988565227f57dfc728319d433532f71e61d6424))
* Highlight hint icon when hovering the input ([422d7fc](422d7fc693caf886a49d03ff48b56ae6ce825356))
* Improve datemathHelp.vue ([795b26e](795b26e1dde781e152ab03fc31fd95f9f106a452))
* Improve handling of an invalid api url ([24ad2f8](24ad2f892db0fce3458624c9dad8735130253fa0))
* Improve user assignments via quick add magic (#3348) ([d9f608e](d9f608e8b4be4da380a535edcce1782c6d21926d))
* Improve variable naming for ProjectCardGrid ([a4be973](a4be973e29e81db4e244427fc46a11b4c8c95f4c))
* Load all projects earlier than in the navigation and use the loading state of the store ([1d93661](1d936618faecb0ddcb10f7c900096a3705614dbd))
* Mark undone if task moved from isDoneBucket (#3291) ([30adad5](30adad5ae6568b5ef1125f206989d447fb999eee))
* Move namespaces list to projects list ([e1bdabc](e1bdabc8d670f7342f4f0777a30a961e3fd4601d))
* Move navigation item to component ([3db4e01](3db4e011d4b625cee940c58ee32d065b8c43f1bb))
* Move quick add magic to a popup behind an icon ([6989558](69895589636ee6369c9778f529bf1df953acb7b1))
* New image for the unauthenticated views ([bef25c4](bef25c49d535ff3940a0112a715f5b351e816468))
* Optimize print view for project views ([8e2c76a](8e2c76a33eec573afab0b754d0707f84e2cca962))
* Persist link share auth rule in url hash (#3336) ([da3eaf0](da3eaf0d357c24775ba8a4cf8f089e5042f73c00))
* Persist link share auth rule in url hash ([f68bb26](f68bb2625e5f619f365fdd421aeda2b8af879aab))
* Prepare for pnpm 8 (#3331) ([7d3b97d](7d3b97d422896e17ab9231c66e49da6c07967d7e))
* Rebuild main navigation so that it works recursively with projects ([06e8cdb](06e8cdb9d2907c846ca7c555b31571b5c1798433))
* Remove all namespace leftovers ([1bd17d6](1bd17d6e50034c159150095f1c51a966293a6726))
* Remove namespaces, make projects infinitely nestable (#3323) ([ac1d374](ac1d374191fca764a70d9851d9828a78ae27c075))
* Rename link share hash prefix ([b9f0635](b9f0635d9fcc764c7ee188c95ce59ac358f735cf))
* Rename list to project everywhere ([befa6f2](befa6f27bb607a57eb8ed49d0152b85cdab4cb95))
* Replace color dot with handle icon on hover ([a3e2cbe](a3e2cbeb27ad8b0d052df62c9f24f8dd3808ddda))
* Set the current language to the one saved by the user on login ([acb212a](acb212ab241e1ed873c943e9c5fa3bcfb2c83a91))
* Show all parent projects in project search ([6a8c656](6a8c656dbb0a4729035468aedc60fd06e80c17ed))
* Show all parent projects in task detail view ([63ba298](63ba2982c92d495de6c7e3526c3693dcfe0e3fba))
* Show avatar and full name in team overview ([b80f070](b80f07043104868d134761b34582b234d12274e1))
* Show initial list of users when opening the assignees view ([59c942a](59c942af735a40f68cfd01caadb22694113da8ae))
* Start adding relative reminder picker with more options ([9df6950](9df6950d1a4a361c075020319ce3037e19e0912d))
* Translate inbox project title ([f2ca2d8](f2ca2d850de5b4b3b3d90e3a5c41adebca2dc1a5))
* Type i18n improvements ([dea1789](dea1789a00981fb496f0c1f4c19a6f0749e4de70))
* Use new Reminders API instead of reminder_dates ([f747d5b](f747d5b2fcadb7459c389372dca4507b75cdd4fa))
* Wrap projects navigation in a <Suspense> so that we can use top level await ([2579c33](2579c33ee1d07234c3ad42d75f2c7a1f7bfdb149))
### Miscellaneous Tasks
* *(ci)* Remove netlify dependency (#3459)
* *(ci)* Sign drone config
* *(editor)* Disable deprecated marked options
* *(i18n)* Clarify translation string
* *(parseSubtasksViaIndention)* Fix comment (#3259)
* *(reminders)* Remove reminderDates property
* *(sentry)* Alwys use the same version
* *(sentry)* Ignore missing commits
* *(sentry)* Only load sentry when enabled
* *(sentry)* Remove debug options
* *(sentry)* Remove sourcemaps after upload via plugin
* *(sentry)* Use correct chunks option
* *(task)* Move toggleFavorite to store
* *(task)* Use ref for task instead of reactive
* *(tests)* Enable experimental memory managment for cypress tests
* *(user)* Cleanup* Update JSDoc example ([bfbfd6a](bfbfd6a4212d493912406c1c505b6c0a24f0f014))
* Add comment on overriding ([21ad830](21ad8301f28ba838c577acb72cb66ea00e176876))
* Add types for emit ([c567874](c56787443f6f9f6be0f8d8501dd4e6e7a768648a))
* Better function naming in password components ([a416d26](a416d26f7cfd163cadb0b6ded107b217ecad5d7c))
* Catch error when trying to play pop sound ([929d4f4](929d4f402342de309dd8e453252d22fcb9f362a6))
* Chore; extract code to reminder-period.vue ([0d6c0c8](0d6c0c8399c9fc73843bdbeb84ff19467edcaa90))
* Clarify users when can still be found even if they disabled it ([302ba2b](302ba2bec7f592f6b0b1fba84a5a1a9fd5f994de))
* Cleanup namespace leftovers ([2e33615](2e336150e086354b1623569aa98ab9c5be48c59a))
* Don't recalculate everything ([9c3259c](9c3259c660e8436f41b5494c9567319090c03bd6))
* Don't set the current project to null if it's undefined already ([e4d97e0](e4d97e05205e2c36143319ccf07ccac03f5de408))
* Don't show selection for parent project when no projects are available ([c30dcff](c30dcff45157e5b89b7bb6c2442271c15da33fc4))
* Don't wrap a computed in another computed ([afaf184](afaf1846ece65b8b2bbee971fafb31a535a4381b))
* Export favorite projects from store ([131022d](131022da427616765f8109ca8ac8f6bad1bdcbbb))
* Export not archived root projects ([b5d9afd](b5d9afd0f72aaf28b89f4877ce3ad2eabe6c3d7b))
* Export projects as array directly from projects store ([e4379f0](e4379f0a229b7b8572fddb029658713a0bbfca1d))
* Follow the happy path ([a33e2f6](a33e2f6c00f35f36aabb6b4d6e823396d29cdf3d))
* Format ([4ad9773](4ad9773022b5873fff09b7afade02c026ac5332f))
* Format ([638d187](638d187a24020d698327b0a0d04a5897672d3b79))
* Formatting ([b92d780](b92d780cda3ab7222c4a6ab7323d1dd3f679b514))
* Group return parameter ([5298706](52987060b11ac0418b6a88f1beabaee59165117d))
* Import const instead of redeclaring it ([61baf02](61baf02e26b292e3f02816a483eb7d92fb49d8ab))
* Improve prop type definition ([638f6be](638f6bea24980658d0f5fb3432d7b64c2ae06f75))
* Make fuzzy matching a paramater ([aeb73a3](aeb73a374f84f6b01d4be4cc784336a214a4cdfa))
* Move ProjectsNavigationWrapper back to navigation.vue ([65522a5](65522a57f1ceddfabeba235e17f8f81ee6bae47b))
* Move all options to component props ([db1c6d6](db1c6d6a41591c8ee5df2d2ee400aaaeda0d02bb))
* Move const ([0ce150a](0ce150af237985dda0cf44f24179ebae332e7585))
* Move duplicate project logic to composable ([b69a056](b69a05689be6e2c833c838cde052702600d245c7))
* Move loader class ([ac78e85](ac78e85e1726b6d7047db72ccbbaf29ac11d1696))
* Move loading styles to variant into the component ([76814a2](76814a2d3f68876934c5791bb4901fca5f95c00f))
* Move more logic to ProjectsNavigationItem.vue ([b567146](b567146d69f1c6a1eba6e37061bde7f627ff8654))
* Move positioning css ([7110c9a](7110c9a5ceb58e6e9675c0f91ddb34c9ab8f2cbc))
* Move styles to components ([25c3b7b](25c3b7bcbfb4ddc2163092ed7c1d5e4758967f1b))
* Move v-if ([12ebefd](12ebefd86a61ca5c82b104b4155a4989c8622713))
* Only apply padding where needed ([ddcd6a1](ddcd6a17dc659611910c2d4ed84fcff575e0ca3a))
* Re-add top menu spacing ([086f50d](086f50d4feed90aac0c458d3f53cbe59ae7402c8))
* Redirect to new project after creating from store ([6b824a4](6b824a49abe8854045c7670fcd6da50539c9fce5))
* Reduce nesting ([06a1ff6](06a1ff6f4bea4cc7447d528423de54f14583dca4))
* Refactor get parents project and move to projects store ([c32a198](c32a198a34edd3db7d6967010ce9dde401d1c864))
* Remove nesting ([a4c8fcc](a4c8fccb115f019840025659c7a8a4bac31eee04))
* Remove old comment ([4134fcb](4134fcbd752ab4cc7691907264b04cf64e11d012))
* Remove old todo ([4e21b46](4e21b463df9af5aec9a5b45c8331f5a9f9e8aeb9))
* Remove triggered notifications as it's not supported anywhere ([8a75790](8a75790453427287dc5a57ff3b59cd2b9cabd3f4))
* Remove type annotation for computed ([a3e289c](a3e289c06c992b24dcff21b1c4f8871676101d98))
* Remove unnecessary map ([336db56](336db56316dec7aeacf2174f5945764dc350769c))
* Remove unused class ([d4e4525](d4e452545afe94ed2e860cd14982462e080a4d49))
* Remove unused code ([652db56](652db56d42b39c05385ff7484fa43b0baa769759))
* Remove user margin from the component ([57c64bb](57c64bbf71342b4e9e2e9e3808412b5e0cf01006))
* Remove user margin from the component ([a1dd1d6](a1dd1d6664479e125e2f8ae87a9d2a57bf94fc9e))
* Remove wrapper div ([2c9693a](2c9693a83eeca832d49d519c5676ae30569628ca))
* Rename alias ([a803bc6](a803bc637e44893aa6921b70215a3206acdc5a91))
* Rename archived message key ([4dee3a9](4dee3a90e9a76cdd190eb28b3327bef1bcc34787))
* Rename flag ([6e09543](6e095436e9bfb6856c6aa469fc4cad93f239bad4))
* Rename getRedirectRoute ([59b05e9](59b05e9836946ed8b9dbb3926fc694641d8508a1))
* Rename prop ([2bb7ff1](2bb7ff1803d5a35bdd61a94e7a4d6fd03d5d1492))
* Replace section with a div ([9b10693](9b1069317283fc20c834eac981e0b2a500e32dba))
* Set project id from the outside ([6c9cbaa](6c9cbaadc821ab92e85b1f8e3fcb3fa85ea99670))
* Update nix flake ([f40035d](f40035dc7943e8199c553acfec838f21ea212c3e))
* Use <menu> instead of <ul> ([49fac7d](49fac7db1cbefce49712797869b956f31e8f541c))
* Use klona to clone project objet ([55e9122](55e912221be4b4765cdb3a7bd0e3dc693478ac81))
* Use long variable name ([6f1baa3](6f1baa3219093147842efe10f92482364516c84c))
* Use long variable name ([a0d39e6](a0d39e6081f35e4ba6589b7840168b0c69b3210f))
* Use project id type ([a342ae6](a342ae67de1c884895ce3304cf6eb1757a38573a))
* Use startsWith for prefix matching ([10ac1ff](10ac1ff66a2bcd797f54c83dda13745fdf359f33))
* Use stores directly ([a7440ed](a7440ed296ec0e99c9dc81e43617b3b54fc518a7))
* [skip ci] Updated translations via Crowdin
## [0.20.5] - 2023-03-12
### Bug Fixes
@ -4790,4 +5527,3 @@ Co-committed-by: renovate <renovatebot@kolaente.de>
* Fixed loading tasks when the user was not authenticated
## [0.1] - 2018-09-20

View File

@ -3,16 +3,18 @@
# │─││ │││ │ │
# ┘─┘┘─┘┘┘─┘┘─┘
FROM --platform=$BUILDPLATFORM node:18-alpine AS builder
FROM --platform=$BUILDPLATFORM node:20.8-alpine AS builder
WORKDIR /build
ARG USE_RELEASE=false
ARG RELEASE_VERSION=main
ARG RELEASE_VERSION=unstable
ENV PNPM_CACHE_FOLDER .cache/pnpm/
ENV PUPPETEER_SKIP_DOWNLOAD true
COPY package.json ./
COPY pnpm-lock.yaml ./
COPY patches ./patches/
RUN if [ "$USE_RELEASE" != true ]; then \
# https://pnpm.io/installation#using-corepack
@ -54,6 +56,9 @@ ENV VIKUNJA_LOG_FORMAT main
ENV VIKUNJA_API_URL /api/v1
ENV VIKUNJA_SENTRY_ENABLED false
ENV VIKUNJA_SENTRY_DSN https://85694a2d757547cbbc90cd4b55c5a18d@o1047380.ingest.sentry.io/6024480
ENV VIKUNJA_PROJECT_INFINITE_NESTING_ENABLED false
ENV VIKUNJA_ALLOW_ICON_CHANGES true
ENV VIKUNJA_CUSTOM_LOGO_URL "''"
COPY docker/injector.sh /docker-entrypoint.d/50-injector.sh
COPY docker/ipv6-disable.sh /docker-entrypoint.d/60-ipv6-disable.sh

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.5-brightgreen.svg)](https://dl.vikunja.io)
[![Download](https://img.shields.io/badge/download-v0.21.0-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.
@ -50,4 +50,3 @@ pnpm run build
```shell
pnpm run lint
```

View File

View File

@ -24,4 +24,5 @@ export default defineConfig({
},
viewportWidth: 1600,
viewportHeight: 900,
experimentalMemoryManagement: true,
})

View File

@ -1,42 +0,0 @@
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {TaskFactory} from '../../factories/task'
import {ProjectFactory} from '../../factories/project'
import {NamespaceFactory} from '../../factories/namespace'
import {UserProjectFactory} from '../../factories/users_project'
import {BucketFactory} from '../../factories/bucket'
describe('Editor', () => {
createFakeUserAndLogin()
beforeEach(() => {
NamespaceFactory.create(1)
ProjectFactory.create(1)
BucketFactory.create(1)
TaskFactory.truncate()
UserProjectFactory.truncate()
})
it('Has a preview with checkable checkboxes', () => {
const tasks = TaskFactory.create(1, {
description: `# Test Heading
* Bullet 1
* Bullet 2
* [ ] Checklist
* [x] Checklist checked
`,
bucket_id: 1,
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('input[type=checkbox][data-checkbox-num=0]')
.click()
cy.get('.task-view .details.content.description h3 span.is-small.has-text-success')
.contains('Saved!')
.should('exist')
cy.get('.preview.content')
.should('contain', 'Test Heading')
})
})

View File

@ -8,20 +8,20 @@ describe('The Menu', () => {
})
it('Is visible by default on desktop', () => {
cy.get('.namespace-container')
cy.get('.menu-container')
.should('have.class', 'is-active')
})
it('Can be hidden on desktop', () => {
cy.get('button.menu-show-button:visible')
.click()
cy.get('.namespace-container')
cy.get('.menu-container')
.should('not.have.class', 'is-active')
})
it('Is hidden by default on mobile', () => {
cy.viewport('iphone-8')
cy.get('.namespace-container')
cy.get('.menu-container')
.should('not.have.class', 'is-active')
})
@ -29,7 +29,7 @@ describe('The Menu', () => {
cy.viewport('iphone-8')
cy.get('button.menu-show-button:visible')
.click()
cy.get('.namespace-container')
cy.get('.menu-container')
.should('have.class', 'is-active')
})
})

View File

@ -1,145 +0,0 @@
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {ProjectFactory} from '../../factories/project'
import {NamespaceFactory} from '../../factories/namespace'
describe('Namepaces', () => {
createFakeUserAndLogin()
let namespaces
beforeEach(() => {
namespaces = NamespaceFactory.create(1)
ProjectFactory.create(1)
})
it('Should be all there', () => {
cy.visit('/namespaces')
cy.get('[data-cy="namespace-title"]')
.should('contain', namespaces[0].title)
})
it('Should create a new Namespace', () => {
const newNamespaceTitle = 'New Namespace'
cy.visit('/namespaces')
cy.get('[data-cy="new-namespace"]')
.should('contain', 'New namespace')
.click()
cy.url()
.should('contain', '/namespaces/new')
cy.get('.card-header-title')
.should('contain', 'New namespace')
cy.get('input.input')
.type(newNamespaceTitle)
cy.get('.button')
.contains('Create')
.click()
cy.get('.global-notification')
.should('contain', 'Success')
cy.get('.namespace-container')
.should('contain', newNamespaceTitle)
cy.url()
.should('contain', '/namespaces')
})
it('Should rename the namespace all places', () => {
const newNamespaces = NamespaceFactory.create(5)
const newNamespaceName = 'New namespace name'
cy.visit('/namespaces')
cy.get(`.namespace-container .menu.namespaces-lists .namespace-title:contains(${newNamespaces[0].title}) .dropdown .dropdown-trigger`)
.click()
cy.get('.namespace-container .menu.namespaces-lists .namespace-title .dropdown .dropdown-content')
.contains('Edit')
.click()
cy.url()
.should('contain', '/settings/edit')
cy.get('#namespacetext')
.invoke('val')
.should('equal', newNamespaces[0].title) // wait until the namespace data is loaded
cy.get('#namespacetext')
.type(`{selectall}${newNamespaceName}`)
cy.get('footer.card-footer .button')
.contains('Save')
.click()
cy.get('.global-notification', { timeout: 1000 })
.should('contain', 'Success')
cy.get('.namespace-container .menu.namespaces-lists')
.should('contain', newNamespaceName)
.should('not.contain', newNamespaces[0].title)
cy.get('[data-cy="namespaces-list"]')
.should('contain', newNamespaceName)
.should('not.contain', newNamespaces[0].title)
})
it('Should remove a namespace when deleting it', () => {
const newNamespaces = NamespaceFactory.create(5)
cy.visit('/')
cy.get(`.namespace-container .menu.namespaces-lists .namespace-title:contains(${newNamespaces[0].title}) .dropdown .dropdown-trigger`)
.click()
cy.get('.namespace-container .menu.namespaces-lists .namespace-title .dropdown .dropdown-content')
.contains('Delete')
.click()
cy.url()
.should('contain', '/settings/delete')
cy.get('[data-cy="modalPrimary"]')
.contains('Do it')
.click()
cy.get('.global-notification')
.should('contain', 'Success')
cy.get('.namespace-container .menu.namespaces-lists')
.should('not.contain', newNamespaces[0].title)
})
it('Should not show archived projects & namespaces if the filter is not checked', () => {
const n = NamespaceFactory.create(1, {
id: 2,
is_archived: true,
}, false)
ProjectFactory.create(1, {
id: 2,
namespace_id: n[0].id,
}, false)
ProjectFactory.create(1, {
id: 3,
is_archived: true,
}, false)
// Initial
cy.visit('/namespaces')
cy.get('.namespace')
.should('not.contain', 'Archived')
// Show archived
cy.get('[data-cy="show-archived-check"] label.check span')
.should('be.visible')
.click()
cy.get('[data-cy="show-archived-check"] input')
.should('be.checked')
cy.get('.namespace')
.should('contain', 'Archived')
// Don't show archived
cy.get('[data-cy="show-archived-check"] label.check span')
.should('be.visible')
.click()
cy.get('[data-cy="show-archived-check"] input')
.should('not.be.checked')
// Second time visiting after unchecking
cy.visit('/namespaces')
cy.get('[data-cy="show-archived-check"] input')
.should('not.be.checked')
cy.get('.namespace')
.should('not.contain', 'Archived')
})
})

View File

@ -1,9 +1,7 @@
import {ProjectFactory} from '../../factories/project'
import {NamespaceFactory} from '../../factories/namespace'
import {TaskFactory} from '../../factories/task'
export function createProjects() {
NamespaceFactory.create(1)
const projects = ProjectFactory.create(1, {
title: 'First Project'
})

View File

@ -8,37 +8,30 @@ describe('Project History', () => {
prepareProjects()
it('should show a project history on the home page', () => {
cy.intercept(Cypress.env('API_URL') + '/namespaces*').as('loadNamespaces')
cy.intercept(Cypress.env('API_URL') + '/projects*').as('loadProjectArray')
cy.intercept(Cypress.env('API_URL') + '/projects/*').as('loadProject')
const projects = ProjectFactory.create(6)
cy.visit('/')
cy.wait('@loadNamespaces')
cy.wait('@loadProjectArray')
cy.get('body')
.should('not.contain', 'Last viewed')
cy.visit(`/projects/${projects[0].id}`)
cy.wait('@loadNamespaces')
cy.wait('@loadProject')
cy.visit(`/projects/${projects[1].id}`)
cy.wait('@loadNamespaces')
cy.wait('@loadProject')
cy.visit(`/projects/${projects[2].id}`)
cy.wait('@loadNamespaces')
cy.wait('@loadProject')
cy.visit(`/projects/${projects[3].id}`)
cy.wait('@loadNamespaces')
cy.wait('@loadProject')
cy.visit(`/projects/${projects[4].id}`)
cy.wait('@loadNamespaces')
cy.wait('@loadProject')
cy.visit(`/projects/${projects[5].id}`)
cy.wait('@loadNamespaces')
cy.wait('@loadProject')
// cy.visit('/')
// cy.wait('@loadNamespaces')
// Not using cy.visit here to work around the redirect issue fixed in #1337
cy.get('nav.menu.top-menu a')
.contains('Overview')

View File

@ -58,7 +58,6 @@ describe('Project View Project', () => {
})
const projects = ProjectFactory.create(2, {
owner_id: '{increment}',
namespace_id: '{increment}',
})
cy.visit(`/projects/${projects[1].id}/`)

View File

@ -22,10 +22,10 @@ describe('Project View Table', () => {
cy.get('.project-table .filter-container .items .button')
.contains('Columns')
.click()
cy.get('.project-table .filter-container .card.columns-filter .card-content .fancycheckbox .check')
cy.get('.project-table .filter-container .card.columns-filter .card-content .fancycheckbox')
.contains('Priority')
.click()
cy.get('.project-table .filter-container .card.columns-filter .card-content .fancycheckbox .check')
cy.get('.project-table .filter-container .card.columns-filter .card-content .fancycheckbox')
.contains('Done')
.click()

View File

@ -1,6 +1,7 @@
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {TaskFactory} from '../../factories/task'
import {ProjectFactory} from '../../factories/project'
import {prepareProjects} from './prepareProjects'
describe('Projects', () => {
@ -10,23 +11,20 @@ describe('Projects', () => {
prepareProjects((newProjects) => (projects = newProjects))
it('Should create a new project', () => {
cy.visit('/')
cy.get('.namespace-title .dropdown-trigger')
.click()
cy.get('.namespace-title .dropdown .dropdown-item')
.contains('New project')
cy.visit('/projects')
cy.get('.project-header [data-cy=new-project]')
.click()
cy.url()
.should('contain', '/projects/new/1')
.should('contain', '/projects/new')
cy.get('.card-header-title')
.contains('New project')
cy.get('input.input')
cy.get('input[name=projectTitle]')
.type('New Project')
cy.get('.button')
.contains('Create')
.click()
cy.get('.global-notification', { timeout: 1000 }) // Waiting until the request to create the new project is done
cy.get('.global-notification', {timeout: 1000}) // Waiting until the request to create the new project is done
.should('contain', 'Success')
cy.url()
.should('contain', '/projects/')
@ -56,9 +54,9 @@ describe('Projects', () => {
cy.get('.project-title')
.should('contain', 'First Project')
cy.get('.namespace-container .menu.namespaces-lists .menu-list li:first-child .dropdown .menu-list-dropdown-trigger')
cy.get('.menu-container .menu-list li:first-child .dropdown .menu-list-dropdown-trigger')
.click()
cy.get('.namespace-container .menu.namespaces-lists .menu-list li:first-child .dropdown .dropdown-content')
cy.get('.menu-container .menu-list li:first-child .dropdown .dropdown-content')
.contains('Edit')
.click()
cy.get('#title')
@ -72,21 +70,21 @@ describe('Projects', () => {
cy.get('.project-title')
.should('contain', newProjectName)
.should('not.contain', projects[0].title)
cy.get('.namespace-container .menu.namespaces-lists .menu-list li:first-child')
cy.get('.menu-container .menu-list li:first-child')
.should('contain', newProjectName)
.should('not.contain', projects[0].title)
cy.visit('/')
cy.get('.card-content')
cy.get('.project-grid')
.should('contain', newProjectName)
.should('not.contain', projects[0].title)
})
it('Should remove a project', () => {
it('Should remove a project when deleting it', () => {
cy.visit(`/projects/${projects[0].id}`)
cy.get('.namespace-container .menu.namespaces-lists .menu-list li:first-child .dropdown .menu-list-dropdown-trigger')
cy.get('.menu-container .menu-list li:first-child .dropdown .menu-list-dropdown-trigger')
.click()
cy.get('.namespace-container .menu.namespaces-lists .menu-list li:first-child .dropdown .dropdown-content')
cy.get('.menu-container .menu-list li:first-child .dropdown .dropdown-content')
.contains('Delete')
.click()
cy.url()
@ -97,15 +95,15 @@ describe('Projects', () => {
cy.get('.global-notification')
.should('contain', 'Success')
cy.get('.namespace-container .menu.namespaces-lists .menu-list')
cy.get('.menu-container .menu-list')
.should('not.contain', projects[0].title)
cy.location('pathname')
.should('equal', '/')
})
it('Should archive a project', () => {
cy.visit(`/projects/${projects[0].id}`)
cy.get('.project-title-dropdown')
.click()
cy.get('.project-title-dropdown .dropdown-menu .dropdown-item')
@ -115,10 +113,59 @@ describe('Projects', () => {
.should('contain.text', 'Archive this project')
cy.get('.modal-content [data-cy=modalPrimary]')
.click()
cy.get('.namespace-container .menu.namespaces-lists .menu-list')
cy.get('.menu-container .menu-list')
.should('not.contain', projects[0].title)
cy.get('main.app-content')
.should('contain.text', 'This project is archived. It is not possible to create new or edit tasks for it.')
})
it('Should show all projects on the projects page', () => {
const projects = ProjectFactory.create(10)
cy.visit('/projects')
projects.forEach(p => {
cy.get('[data-cy="projects-list"]')
.should('contain', p.title)
})
})
it('Should not show archived projects if the filter is not checked', () => {
ProjectFactory.create(1, {
id: 2,
}, false)
ProjectFactory.create(1, {
id: 3,
is_archived: true,
}, false)
// Initial
cy.visit('/projects')
cy.get('.project-grid')
.should('not.contain', 'Archived')
// Show archived
cy.get('[data-cy="show-archived-check"] label span')
.should('be.visible')
.click()
cy.get('[data-cy="show-archived-check"] input')
.should('be.checked')
cy.get('.project-grid')
.should('contain', 'Archived')
// Don't show archived
cy.get('[data-cy="show-archived-check"] label span')
.should('be.visible')
.click()
cy.get('[data-cy="show-archived-check"] input')
.should('not.be.checked')
// Second time visiting after unchecking
cy.visit('/projects')
cy.get('[data-cy="show-archived-check"] input')
.should('not.be.checked')
cy.get('.project-grid')
.should('not.contain', 'Archived')
})
})

View File

@ -2,24 +2,58 @@ import {LinkShareFactory} from '../../factories/link_sharing'
import {ProjectFactory} from '../../factories/project'
import {TaskFactory} from '../../factories/task'
function prepareLinkShare() {
const projects = ProjectFactory.create(1)
const tasks = TaskFactory.create(10, {
project_id: projects[0].id
})
const linkShares = LinkShareFactory.create(1, {
project_id: projects[0].id,
right: 0,
})
return {
share: linkShares[0],
project: projects[0],
tasks,
}
}
describe('Link shares', () => {
it('Can view a link share', () => {
const projects = ProjectFactory.create(1)
const tasks = TaskFactory.create(10, {
project_id: projects[0].id
})
const linkShares = LinkShareFactory.create(1, {
project_id: projects[0].id,
right: 0,
})
const {share, project, tasks} = prepareLinkShare()
cy.visit(`/share/${linkShares[0].hash}/auth`)
cy.visit(`/share/${share.hash}/auth`)
cy.get('h1.title')
.should('contain', projects[0].title)
.should('contain', project.title)
cy.get('input.input[placeholder="Add a new task..."')
.should('not.exist')
cy.get('.tasks')
.should('contain', tasks[0].title)
cy.url().should('contain', `/projects/${project.id}/list#share-auth-token=${share.hash}`)
})
it('Should work when directly viewing a project with share hash present', () => {
const {share, project, tasks} = prepareLinkShare()
cy.visit(`/projects/${project.id}/list#share-auth-token=${share.hash}`)
cy.get('h1.title')
.should('contain', project.title)
cy.get('input.input[placeholder="Add a new task..."')
.should('not.exist')
cy.get('.tasks')
.should('contain', tasks[0].title)
})
it('Should work when directly viewing a task with share hash present', () => {
const {share, project, tasks} = prepareLinkShare()
cy.visit(`/tasks/${tasks[0].id}#share-auth-token=${share.hash}`)
cy.get('h1.title')
.should('contain', tasks[0].title)
})
})

View File

@ -3,12 +3,10 @@ import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {ProjectFactory} from '../../factories/project'
import {seed} from '../../support/seed'
import {TaskFactory} from '../../factories/task'
import {NamespaceFactory} from '../../factories/namespace'
import {BucketFactory} from '../../factories/bucket'
import {updateUserSettings} from '../../support/updateUserSettings'
function seedTasks(numberOfTasks = 50, startDueDate = new Date()) {
NamespaceFactory.create(1)
const project = ProjectFactory.create()[0]
BucketFactory.create(1, {
project_id: project.id,
@ -137,8 +135,7 @@ describe('Home Page Task Overview', () => {
cy.visit('/')
cy.get('.home.app-content .content')
.should('contain.text', 'You can create a new project for your new tasks:')
.should('contain.text', 'Or import your projects and tasks from other services into Vikunja:')
.should('contain.text', 'Import your projects and tasks from other services into Vikunja:')
})
it('Should not show the cta buttons for new project when there are tasks', () => {

View File

@ -4,7 +4,6 @@ import {TaskFactory} from '../../factories/task'
import {ProjectFactory} from '../../factories/project'
import {TaskCommentFactory} from '../../factories/task_comment'
import {UserFactory} from '../../factories/user'
import {NamespaceFactory} from '../../factories/namespace'
import {UserProjectFactory} from '../../factories/users_project'
import {TaskAssigneeFactory} from '../../factories/task_assignee'
import {LabelFactory} from '../../factories/labels'
@ -12,6 +11,7 @@ import {LabelTaskFactory} from '../../factories/label_task'
import {BucketFactory} from '../../factories/bucket'
import {TaskAttachmentFactory} from '../../factories/task_attachments'
import {TaskReminderFactory} from '../../factories/task_reminders'
function addLabelToTaskAndVerify(labelTitle: string) {
cy.get('.task-view .action-buttons .button')
@ -24,7 +24,7 @@ function addLabelToTaskAndVerify(labelTitle: string) {
.first()
.click()
cy.get('.global-notification', { timeout: 4000 })
cy.get('.global-notification', {timeout: 4000})
.should('contain', 'Success')
cy.get('.task-view .details.labels-list .multiselect .input-wrapper span.tag')
.should('exist')
@ -47,13 +47,11 @@ function uploadAttachmentAndVerify(taskId: number) {
describe('Task', () => {
createFakeUserAndLogin()
let namespaces
let projects
let buckets
beforeEach(() => {
// UserFactory.create(1)
namespaces = NamespaceFactory.create(1)
projects = ProjectFactory.create(1)
buckets = BucketFactory.create(1, {
project_id: projects[0].id,
@ -96,7 +94,7 @@ describe('Task', () => {
TaskFactory.create(1)
cy.visit('/projects/1/list')
cy.get('.tasks .task .fancycheckbox label.check')
cy.get('.tasks .task .fancycheckbox')
.first()
.click()
cy.get('.global-notification')
@ -110,7 +108,7 @@ describe('Task', () => {
cy.get('.tasks .task .favorite')
.first()
.click()
cy.get('.menu.namespaces-lists')
cy.get('.menu-container')
.should('contain', 'Favorites')
})
@ -124,7 +122,7 @@ describe('Task', () => {
const tasks = TaskFactory.create(1, {
id: 1,
index: 1,
description: 'Lorem ipsum dolor sit amet.'
description: 'Lorem ipsum dolor sit amet.',
})
cy.visit(`/tasks/${tasks[0].id}`)
@ -133,7 +131,6 @@ describe('Task', () => {
cy.get('.task-view h1.title.task-id')
.should('contain', '#1')
cy.get('.task-view h6.subtitle')
.should('contain', namespaces[0].title)
.should('contain', projects[0].title)
cy.get('.task-view .details.content.description')
.should('contain', tasks[0].description)
@ -146,7 +143,7 @@ describe('Task', () => {
id: 1,
index: 1,
done: true,
done_at: new Date().toISOString()
done_at: new Date().toISOString(),
})
cy.visit(`/tasks/${tasks[0].id}`)
@ -199,13 +196,13 @@ describe('Task', () => {
it('Can edit the description', () => {
const tasks = TaskFactory.create(1, {
id: 1,
description: 'Lorem ipsum dolor sit amet.'
description: 'Lorem ipsum dolor sit amet.',
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .details.content.description .editor button')
cy.get('.task-view .details.content.description .tiptap button.done-edit')
.click()
cy.get('.task-view .details.content.description .editor .vue-easymde .EasyMDEContainer .CodeMirror-scroll')
cy.get('.task-view .details.content.description .tiptap__editor .tiptap.ProseMirror')
.type('{selectall}New Description')
cy.get('[data-cy="saveEditor"]')
.contains('Save')
@ -222,7 +219,7 @@ describe('Task', () => {
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .comments .media.comment .editor .vue-easymde .EasyMDEContainer .CodeMirror-scroll')
cy.get('.task-view .comments .media.comment .tiptap__editor .tiptap.ProseMirror')
.should('be.visible')
.type('{selectall}New Comment')
cy.get('.task-view .comments .media.comment .button:not([disabled])')
@ -230,7 +227,7 @@ describe('Task', () => {
.should('be.visible')
.click()
cy.get('.task-view .comments .media.comment .editor')
cy.get('.task-view .comments .media.comment .tiptap__editor')
.should('contain', 'New Comment')
cy.get('.global-notification')
.should('contain', 'Success')
@ -239,7 +236,7 @@ describe('Task', () => {
it('Can move a task to another project', () => {
const projects = ProjectFactory.create(2)
BucketFactory.create(2, {
project_id: '{increment}'
project_id: '{increment}',
})
const tasks = TaskFactory.create(1, {
id: 1,
@ -260,7 +257,6 @@ describe('Task', () => {
.click()
cy.get('.task-view h6.subtitle')
.should('contain', namespaces[0].title)
.should('contain', projects[1].title)
cy.get('.global-notification')
.should('contain', 'Success')
@ -384,7 +380,7 @@ describe('Task', () => {
addLabelToTaskAndVerify(labels[0].title)
})
it('Can add a label to a task and it shows up on the kanban board afterwards', () => {
const tasks = TaskFactory.create(1, {
id: 1,
@ -393,18 +389,18 @@ describe('Task', () => {
})
const labels = LabelFactory.create(1)
LabelTaskFactory.truncate()
cy.visit(`/projects/${projects[0].id}/kanban`)
cy.get('.bucket .task')
.contains(tasks[0].title)
.click()
addLabelToTaskAndVerify(labels[0].title)
cy.get('.modal-content .close')
.click()
cy.get('.bucket .task')
.should('contain.text', labels[0].title)
})
@ -465,7 +461,155 @@ describe('Task', () => {
cy.get('.global-notification')
.should('contain', 'Success')
})
it('Can set a reminder', () => {
TaskReminderFactory.truncate()
const tasks = TaskFactory.create(1, {
id: 1,
done: false,
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .action-buttons .button')
.contains('Set Reminders')
.click()
cy.get('.task-view .columns.details .column button')
.contains('Add a new reminder')
.click()
cy.get('.datepicker__quick-select-date')
.contains('Tomorrow')
.click()
cy.get('.reminder-options-popup')
.should('not.be.visible')
cy.get('.global-notification')
.should('contain', 'Success')
})
it('Allows to set a relative reminder when the task already has a due date', () => {
TaskReminderFactory.truncate()
const tasks = TaskFactory.create(1, {
id: 1,
done: false,
due_date: (new Date()).toISOString(),
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .action-buttons .button')
.contains('Set Reminders')
.click()
cy.get('.task-view .columns.details .column button')
.contains('Add a new reminder')
.click()
cy.get('.datepicker__quick-select-date')
.should('not.exist')
cy.get('.reminder-options-popup .card-content')
.should('contain', '1 day before Due Date')
cy.get('.reminder-options-popup .card-content')
.contains('1 day before Due Date')
.click()
cy.get('.reminder-options-popup')
.should('not.be.visible')
cy.get('.global-notification')
.should('contain', 'Success')
})
it('Allows to set a relative reminder when the task already has a start date', () => {
TaskReminderFactory.truncate()
const tasks = TaskFactory.create(1, {
id: 1,
done: false,
start_date: (new Date()).toISOString(),
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .action-buttons .button')
.contains('Set Reminders')
.click()
cy.get('.task-view .columns.details .column button')
.contains('Add a new reminder')
.click()
cy.get('.datepicker__quick-select-date')
.should('not.exist')
cy.get('.reminder-options-popup .card-content')
.should('contain', '1 day before Start Date')
cy.get('.reminder-options-popup .card-content')
.contains('1 day before Start Date')
.click()
cy.get('.reminder-options-popup')
.should('not.be.visible')
cy.get('.global-notification')
.should('contain', 'Success')
})
it('Allows to set a custom relative reminder when the task already has a due date', () => {
TaskReminderFactory.truncate()
const tasks = TaskFactory.create(1, {
id: 1,
done: false,
due_date: (new Date()).toISOString(),
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .action-buttons .button')
.contains('Set Reminders')
.click()
cy.get('.task-view .columns.details .column button')
.contains('Add a new reminder')
.click()
cy.get('.datepicker__quick-select-date')
.should('not.exist')
cy.get('.reminder-options-popup .card-content')
.contains('Custom')
.click()
cy.get('.reminder-options-popup .card-content .reminder-period input')
.first()
.type('10')
cy.get('.reminder-options-popup .card-content .reminder-period select')
.first()
.select('days')
cy.get('.reminder-options-popup .card-content button')
.contains('Confirm')
.click()
cy.get('.reminder-options-popup')
.should('not.be.visible')
cy.get('.global-notification')
.should('contain', 'Success')
})
it('Allows to set a fixed reminder when the task already has a due date', () => {
TaskReminderFactory.truncate()
const tasks = TaskFactory.create(1, {
id: 1,
done: false,
due_date: (new Date()).toISOString(),
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .action-buttons .button')
.contains('Set Reminders')
.click()
cy.get('.task-view .columns.details .column button')
.contains('Add a new reminder')
.click()
cy.get('.datepicker__quick-select-date')
.should('not.exist')
cy.get('.reminder-options-popup .card-content')
.contains('Date and time')
.click()
cy.get('.datepicker__quick-select-date')
.contains('Tomorrow')
.click()
cy.get('.reminder-options-popup')
.should('not.be.visible')
cy.get('.global-notification')
.should('contain', 'Success')
})
it('Can set a priority for a task', () => {
const tasks = TaskFactory.create(1, {
id: 1,
@ -503,7 +647,7 @@ describe('Task', () => {
.select('50%')
cy.get('.global-notification')
.should('contain', 'Success')
cy.wait(200)
cy.get('.task-view .columns.details .column')
@ -512,7 +656,7 @@ describe('Task', () => {
.should('be.visible')
.should('have.value', '0.5')
})
it('Can add an attachment to a task', () => {
TaskAttachmentFactory.truncate()
const tasks = TaskFactory.create(1, {
@ -547,35 +691,119 @@ describe('Task', () => {
cy.get('.bucket .task .footer .icon svg.fa-paperclip')
.should('exist')
})
it('Can check items off a checklist', () => {
const tasks = TaskFactory.create(1, {
id: 1,
description: `
This is a checklist:
* [ ] one item
* [ ] another item
* [ ] third item
* [ ] fourth item
* [x] and this one is already done
`,
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>First Item</p></div>
</li>
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Second Item</p></div>
</li>
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Third Item</p></div>
</li>
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Fourth Item</p></div>
</li>
<li data-checked="true" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Fifth Item</p></div>
</li>
</ul>`,
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .checklist-summary')
.should('contain.text', '1 of 5 tasks')
cy.get('.editor .content ul > li input[type=checkbox]')
cy.get('.tiptap__editor ul > li input[type=checkbox]')
.eq(2)
.click()
cy.get('.editor .content ul > li input[type=checkbox]')
cy.get('.task-view .details.content.description h3 span.is-small.has-text-success')
.contains('Saved!')
.should('exist')
cy.get('.tiptap__editor ul > li input[type=checkbox]')
.eq(2)
.should('be.checked')
cy.get('.editor .content input[type=checkbox]')
cy.get('.tiptap__editor input[type=checkbox]')
.should('have.length', 5)
cy.get('.task-view .checklist-summary')
.should('contain.text', '2 of 5 tasks')
})
it('Should use the editor to render description', () => {
const tasks = TaskFactory.create(1, {
id: 1,
description: `
<h1>Lorem Ipsum</h1>
<p>Dolor sit amet</p>
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>First Item</p></div>
</li>
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Second Item</p></div>
</li>
</ul>`,
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.tiptap__editor ul > li input[type=checkbox]')
.should('exist')
cy.get('.tiptap__editor h1')
.contains('Lorem Ipsum')
.should('exist')
cy.get('.tiptap__editor p')
.contains('Dolor sit amet')
.should('exist')
})
it.only('Should render an image from attachment', async () => {
TaskAttachmentFactory.truncate()
const tasks = TaskFactory.create(1, {
id: 1,
description: '',
})
cy.readFile('cypress/fixtures/image.jpg', null).then(file => {
const formData = new FormData()
formData.append('files', new Blob([file]), 'image.jpg')
cy.request({
method: 'PUT',
url: `${Cypress.env('API_URL')}/tasks/${tasks[0].id}/attachments`,
headers: {
'Authorization': `Bearer ${window.localStorage.getItem('token')}`,
'Content-Type': 'multipart/form-data',
},
body: formData,
})
.then(({body}) => {
const dec = new TextDecoder('utf-8')
const {success} = JSON.parse(dec.decode(body))
TaskFactory.create(1, {
id: 1,
description: `<img src="${Cypress.env('API_URL')}/tasks/${tasks[0].id}/attachments/${success[0].id}" alt="test image">`,
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.tiptap__editor img')
.should('be.visible')
.and(($img) => {
// "naturalWidth" and "naturalHeight" are set when the image loads
expect($img[0].naturalWidth).to.be.greaterThan(0)
})
})
})
})
})
})

View File

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

View File

@ -1,4 +1,5 @@
import {UserFactory} from '../../factories/user'
import {ProjectFactory} from '../../factories/project'
const testAndAssertFailed = fixture => {
cy.intercept(Cypress.env('API_URL') + '/login*').as('login')
@ -13,26 +14,28 @@ const testAndAssertFailed = fixture => {
cy.get('div.message.danger').contains('Wrong username or password.')
}
const username = 'test'
const credentials = {
username: 'test',
password: '1234',
}
function login() {
cy.get('input[id=username]').type(credentials.username)
cy.get('input[id=password]').type(credentials.password)
cy.get('.button').contains('Login').click()
cy.url().should('include', '/')
}
context('Login', () => {
beforeEach(() => {
UserFactory.create(1, {username})
UserFactory.create(1, {username: credentials.username})
})
it('Should log in with the right credentials', () => {
const fixture = {
username: 'test',
password: '1234',
}
cy.visit('/login')
cy.get('input[id=username]').type(fixture.username)
cy.get('input[id=password]').type(fixture.password)
cy.get('.button').contains('Login').click()
cy.url().should('include', '/')
login()
cy.clock(1625656161057) // 13:00
cy.get('h2').should('contain', `Hi ${fixture.username}!`)
cy.get('h2').should('contain', `Hi ${credentials.username}!`)
})
it('Should fail with a bad password', () => {
@ -57,4 +60,15 @@ context('Login', () => {
cy.visit('/')
cy.url().should('include', '/login')
})
it('Should redirect to the previous route after logging in', () => {
const projects = ProjectFactory.create(1)
cy.visit(`/projects/${projects[0].id}/list`)
cy.url().should('include', '/login')
login()
cy.url().should('include', `/projects/${projects[0].id}/list`)
})
})

View File

@ -17,7 +17,7 @@ context('Registration', () => {
it('Should work without issues', () => {
const fixture = {
username: 'testuser',
password: '123456',
password: '12345678',
email: 'testuser@example.com',
}
@ -31,10 +31,10 @@ context('Registration', () => {
cy.get('h2').should('contain', `Hi ${fixture.username}!`)
})
it.only('Should fail', () => {
it('Should fail', () => {
const fixture = {
username: 'test',
password: '123456',
password: '12345678',
email: 'testuser@example.com',
}

View File

@ -1,18 +0,0 @@
import {faker} from '@faker-js/faker'
import {Factory} from '../support/factory'
export class NamespaceFactory extends Factory {
static table = 'namespaces'
static factory() {
const now = new Date()
return {
id: '{increment}',
title: faker.lorem.words(3),
owner_id: 1,
created: now.toISOString(),
updated: now.toISOString(),
}
}
}

View File

@ -11,7 +11,6 @@ export class ProjectFactory extends Factory {
id: '{increment}',
title: faker.lorem.words(3),
owner_id: 1,
namespace_id: 1,
created: now.toISOString(),
updated: now.toISOString(),
}

View File

@ -0,0 +1,18 @@
import {Factory} from '../support/factory'
export class TaskReminderFactory extends Factory {
static table = 'task_reminders'
static factory() {
const now = new Date()
return {
id: '{increment}',
task_id: 1,
reminder: now.toISOString(),
created: now.toISOString(),
relative_to: '',
relative_period: 0,
}
}
}

View File

@ -4,7 +4,7 @@ import {seed} from './seed'
* A factory makes it easy to seed the database with data.
*/
export class Factory {
static table = null
static table: string | null = null
static factory() {
return {}

3
docker/injector.sh Normal file → Executable file
View File

@ -11,5 +11,8 @@ 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
sed -ri "s:^(\s*window.PROJECT_INFINITE_NESTING_ENABLED\s*=)\s*.+:\1 '${VIKUNJA_PROJECT_INFINITE_NESTING_ENABLED}':g" /usr/share/nginx/html/index.html
sed -ri "s:^(\s*window.ALLOW_ICON_CHANGES\s*=)\s*.+:\1 ${VIKUNJA_ALLOW_ICON_CHANGES}:g" /usr/share/nginx/html/index.html
sed -ri "s:^(\s*window.CUSTOM_LOGO_URL\s*=)\s*.+:\1 ${VIKUNJA_CUSTOM_LOGO_URL}:g" /usr/share/nginx/html/index.html
date -uIseconds | xargs echo 'info: started at'

0
docker/ipv6-disable.sh Normal file → Executable file
View File

View File

@ -4,7 +4,6 @@
pid /tmp/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 65535;
events {
multi_accept on;

26
env.d.ts vendored
View File

@ -3,10 +3,32 @@
/// <reference types="cypress" />
/// <reference types="@histoire/plugin-vue/components" />
declare module 'postcss-focus-within/browser' {
import focusWithinInit from 'postcss-focus-within/browser'
export default focusWithinInit
}
declare module 'css-has-pseudo/browser' {
import cssHasPseudo from 'css-has-pseudo/browser'
export default cssHasPseudo
}
interface ImportMetaEnv {
readonly VITE_IS_ONLINE: boolean
readonly VIKUNJA_API_URL?: string
readonly VIKUNJA_HTTP_PORT?: number
readonly VIKUNJA_HTTPS_PORT?: number
readonly VIKUNJA_SENTRY_ENABLED?: boolean
readonly VIKUNJA_SENTRY_DSN?: string
readonly SENTRY_AUTH_TOKEN?: string
readonly SENTRY_ORG?: string
readonly SENTRY_PROJECT?: string
readonly VITE_WORKBOX_DEBUG?: boolean
readonly VITE_IS_ONLINE: boolean
}
interface ImportMeta {
readonly env: ImportMetaEnv
readonly env: ImportMetaEnv
}

View File

@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1664753041,
"narHash": "sha256-0ogaD8PaGHluARFeupofvk1Nq9gpVeZdlFM0Kcwguys=",
"lastModified": 1697730408,
"narHash": "sha256-Ww//zzukdTrwTrCUkaJA/NsaLEfUfQpWZXBdXBYfhak=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a62844b302507c7531ad68a86cb7aa54704c9cb4",
"rev": "ff0a5a776b56e0ca32d47a4a47695452ec7f7d80",
"type": "github"
},
"original": {

View File

@ -18,15 +18,22 @@
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script>
//
//
// This variable points the frontend to the api.
// It has to be the full url, including the last /api/v1 part and port.
// You can change this if your api is not reachable on the same port as the frontend.
window.API_URL = 'http://localhost:3456/api/v1'
// Enable error tracking with sentry. If this is set to true, will send anonymized data to
// Enable error tracking with sentry. If this is set to true, will send anonymized data to
// our sentry instance to notify us of potential problems.
window.SENTRY_ENABLED = false
window.SENTRY_DSN = 'https://85694a2d757547cbbc90cd4b55c5a18d@o1047380.ingest.sentry.io/6024480'
// If enabled, allows the user to nest projects infinitely, instead of the default 2 levels.
// This setting might change in the future or be removed completely.
window.PROJECT_INFINITE_NESTING_ENABLED = false
// Allow changing the logo and other icons based on various occasions throughout the year.
window.ALLOW_ICON_CHANGES = true
// Allow using a custom logo via external URL.
window.CUSTOM_LOGO_URL = ''
</script>
</body>
</html>

Binary file not shown.

Binary file not shown.

View File

@ -13,7 +13,7 @@
},
"homepage": "https://vikunja.io/",
"funding": "https://opencollective.com/vikunja",
"packageManager": "pnpm@7.29.3",
"packageManager": "pnpm@8.9.0",
"keywords": [
"todo",
"productivity",
@ -45,103 +45,138 @@
"story:preview": "histoire preview"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "6.3.0",
"@fortawesome/free-regular-svg-icons": "6.3.0",
"@fortawesome/free-solid-svg-icons": "6.3.0",
"@fortawesome/fontawesome-svg-core": "6.4.2",
"@fortawesome/free-regular-svg-icons": "6.4.2",
"@fortawesome/free-solid-svg-icons": "6.4.2",
"@fortawesome/vue-fontawesome": "3.0.3",
"@github/hotkey": "2.0.1",
"@infectoone/vue-ganttastic": "2.1.4",
"@intlify/unplugin-vue-i18n": "0.9.2",
"@kyvg/vue3-notification": "2.9.0",
"@sentry/tracing": "7.43.0",
"@sentry/vue": "7.43.0",
"@github/hotkey": "2.1.1",
"@infectoone/vue-ganttastic": "2.2.0",
"@intlify/unplugin-vue-i18n": "1.4.0",
"@kyvg/vue3-notification": "3.0.2",
"@sentry/tracing": "7.74.1",
"@sentry/vue": "7.74.1",
"@tiptap/core": "2.1.12",
"@tiptap/extension-blockquote": "2.1.12",
"@tiptap/extension-bold": "2.1.12",
"@tiptap/extension-bullet-list": "2.1.12",
"@tiptap/extension-code": "2.1.12",
"@tiptap/extension-code-block-lowlight": "2.0.3",
"@tiptap/extension-document": "2.0.3",
"@tiptap/extension-dropcursor": "2.1.12",
"@tiptap/extension-gapcursor": "2.1.12",
"@tiptap/extension-hard-break": "2.1.12",
"@tiptap/extension-heading": "2.1.12",
"@tiptap/extension-history": "2.1.12",
"@tiptap/extension-horizontal-rule": "2.1.12",
"@tiptap/extension-image": "2.0.3",
"@tiptap/extension-italic": "2.1.12",
"@tiptap/extension-link": "2.0.3",
"@tiptap/extension-list-item": "2.1.12",
"@tiptap/extension-ordered-list": "2.1.12",
"@tiptap/extension-paragraph": "2.1.12",
"@tiptap/extension-placeholder": "2.1.12",
"@tiptap/extension-strike": "2.1.12",
"@tiptap/extension-table": "2.0.3",
"@tiptap/extension-table-cell": "2.0.3",
"@tiptap/extension-table-header": "2.0.3",
"@tiptap/extension-table-row": "2.0.3",
"@tiptap/extension-task-item": "2.0.3",
"@tiptap/extension-task-list": "2.0.3",
"@tiptap/extension-text": "2.1.12",
"@tiptap/extension-typography": "2.0.3",
"@tiptap/extension-underline": "2.1.12",
"@tiptap/pm": "2.1.12",
"@tiptap/suggestion": "2.1.12",
"@tiptap/vue-3": "2.0.3",
"@types/is-touch-device": "1.0.0",
"@types/lodash.clonedeep": "4.5.7",
"@types/sortablejs": "1.15.1",
"@vueuse/core": "9.13.0",
"axios": "1.3.4",
"@types/lodash.clonedeep": "4.5.8",
"@types/sortablejs": "1.15.0",
"@vueuse/core": "10.5.0",
"@vueuse/router": "10.5.0",
"axios": "1.5.1",
"blurhash": "2.0.5",
"bulma-css-variables": "0.9.33",
"camel-case": "4.1.2",
"codemirror": "5.65.12",
"date-fns": "2.29.3",
"dayjs": "1.11.7",
"dompurify": "3.0.1",
"easymde": "2.18.0",
"date-fns": "2.30.0",
"dayjs": "1.11.10",
"dompurify": "3.0.6",
"fast-deep-equal": "3.1.3",
"flatpickr": "4.6.13",
"flexsearch": "0.7.31",
"floating-vue": "2.0.0-beta.20",
"focus-within": "3.0.2",
"highlight.js": "11.7.0",
"floating-vue": "2.0.0-beta.24",
"is-touch-device": "1.0.1",
"klona": "2.0.6",
"lodash.debounce": "4.0.8",
"marked": "4.2.12",
"pinia": "2.0.33",
"lowlight": "2.9.0",
"pinia": "2.1.7",
"register-service-worker": "1.7.2",
"snake-case": "3.0.4",
"sortablejs": "1.15.0",
"ufo": "1.1.1",
"vue": "3.2.47",
"tippy.js": "6.3.7",
"ufo": "1.3.1",
"vue": "3.3.6",
"vue-advanced-cropper": "2.8.8",
"vue-flatpickr-component": "11.0.3",
"vue-i18n": "9.2.2",
"vue-router": "4.1.6",
"workbox-precaching": "6.5.4",
"vue-i18n": "9.5.0",
"vue-router": "4.2.5",
"workbox-precaching": "7.0.0",
"zhyswan-vuedraggable": "4.1.3"
},
"devDependencies": {
"@4tw/cypress-drag-drop": "2.2.3",
"@cypress/vite-dev-server": "5.0.4",
"@cypress/vue": "5.0.4",
"@faker-js/faker": "7.6.0",
"@histoire/plugin-screenshot": "0.15.8",
"@histoire/plugin-vue": "0.15.8",
"@rushstack/eslint-patch": "1.2.0",
"@types/codemirror": "5.60.7",
"@types/dompurify": "3.0.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.15.3",
"@4tw/cypress-drag-drop": "2.2.5",
"@cypress/vite-dev-server": "5.0.6",
"@cypress/vue": "6.0.0",
"@faker-js/faker": "8.2.0",
"@histoire/plugin-screenshot": "0.17.0",
"@histoire/plugin-vue": "0.17.1",
"@rushstack/eslint-patch": "1.5.1",
"@tsconfig/node18": "18.2.2",
"@types/codemirror": "5.60.12",
"@types/dompurify": "3.0.4",
"@types/flexsearch": "0.7.5",
"@types/is-touch-device": "1.0.1",
"@types/lodash.debounce": "4.0.8",
"@types/marked": "5.0.2",
"@types/node": "18.18.6",
"@types/postcss-preset-env": "7.7.0",
"@typescript-eslint/eslint-plugin": "5.55.0",
"@typescript-eslint/parser": "5.55.0",
"@vitejs/plugin-legacy": "4.0.2",
"@vitejs/plugin-vue": "4.1.0",
"@vue/eslint-config-typescript": "11.0.2",
"@vue/test-utils": "2.3.1",
"@vue/tsconfig": "0.1.3",
"autoprefixer": "10.4.14",
"browserslist": "4.21.5",
"caniuse-lite": "1.0.30001465",
"csstype": "3.1.1",
"cypress": "12.8.1",
"esbuild": "0.17.11",
"eslint": "8.36.0",
"eslint-plugin-vue": "9.9.0",
"happy-dom": "8.9.0",
"histoire": "0.15.8",
"netlify-cli": "13.1.2",
"postcss": "8.4.21",
"@types/sortablejs": "1.15.4",
"@typescript-eslint/eslint-plugin": "6.8.0",
"@typescript-eslint/parser": "6.8.0",
"@vitejs/plugin-legacy": "4.1.1",
"@vitejs/plugin-vue": "4.4.0",
"@vue/eslint-config-typescript": "12.0.0",
"@vue/test-utils": "2.4.1",
"@vue/tsconfig": "0.4.0",
"autoprefixer": "10.4.16",
"browserslist": "4.22.1",
"caniuse-lite": "1.0.30001551",
"css-has-pseudo": "6.0.0",
"csstype": "3.1.2",
"cypress": "13.3.2",
"esbuild": "0.19.5",
"eslint": "8.52.0",
"eslint-plugin-vue": "9.17.0",
"happy-dom": "12.9.1",
"histoire": "0.17.2",
"postcss": "8.4.31",
"postcss-easing-gradients": "3.0.1",
"postcss-easings": "3.0.1",
"postcss-preset-env": "8.0.1",
"rollup": "3.19.1",
"rollup-plugin-visualizer": "5.9.0",
"sass": "1.59.3",
"start-server-and-test": "2.0.0",
"typescript": "4.9.5",
"vite": "4.2.0",
"vite-plugin-inject-preload": "1.3.1",
"vite-plugin-pwa": "0.14.4",
"postcss-easings": "4.0.0",
"postcss-focus-within": "8.0.0",
"postcss-preset-env": "9.2.0",
"rollup": "4.1.4",
"rollup-plugin-visualizer": "5.9.2",
"sass": "1.69.4",
"start-server-and-test": "2.0.1",
"typescript": "5.2.2",
"vite": "4.5.0",
"vite-plugin-inject-preload": "1.3.3",
"vite-plugin-pwa": "0.16.5",
"vite-plugin-sentry": "1.3.0",
"vite-svg-loader": "4.0.0",
"vitest": "0.29.3",
"vue-tsc": "1.2.0",
"vitest": "0.34.6",
"vue-tsc": "1.8.19",
"wait-on": "7.0.1",
"workbox-cli": "6.5.4"
"workbox-cli": "7.0.0"
},
"pnpm": {
"patchedDependencies": {

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
],
"packageRules": [
{
"matchPackageNames": ["netlify-cli", "happy-dom"],
"matchPackageNames": ["happy-dom"],
"extends": ["schedule:weekly"]
},
{
@ -27,11 +27,17 @@
"histoire"
]
},
{
"groupName": "tiptap",
"matchPackagePrefixes": [
"@tiptap/",
"tiptap"
]
},
{
"matchDepTypes": ["devDependencies"],
"automerge": true,
"automergeStrategy": "squash",
"automergeType": "pr"
"groupName": "dev-dependencies",
"extends": ["schedule:daily"]
}
]
}
}

View File

@ -33,9 +33,9 @@ const promiseExec = cmd => {
}
(async function () {
let stdout = await promiseExec(`./node_modules/.bin/netlify link --id ${siteId}`)
let stdout = await promiseExec(`/home/node/docker-netlify-cli/node_modules/.bin/netlify link --id ${siteId}`)
console.log(stdout)
stdout = await promiseExec(`./node_modules/.bin/netlify deploy --alias ${alias}`)
stdout = await promiseExec(`/home/node/docker-netlify-cli/node_modules/.bin/netlify deploy --alias ${alias}`)
console.log(stdout)
const data = await fetch(prIssueCommentsUrl).then(response => response.json())

View File

@ -1 +1 @@
57af69409e66bc87f4f2fc5822dd8d3c2eb47c601f81af1ac4a56f3e2d80837b1a2de06f4ff57695ec379b7c15b881e3 ./scripts/deploy-preview-netlify.mjs
4a7c1293c7b12e9ab476cdf35251a407c6a1cd005d22c06df994222cccfb25cde5f47d15866a098c9d739778fee4dc19 ./scripts/deploy-preview-netlify.mjs

View File

@ -12,8 +12,10 @@
<keyboard-shortcuts v-if="keyboardShortcutsActive"/>
<Teleport to="body">
<AddToHomeScreen/>
<UpdateNotification/>
<Notification/>
<DemoMode/>
</Teleport>
</ready>
</template>
@ -43,6 +45,8 @@ import {useBaseStore} from '@/stores/base'
import {useColorScheme} from '@/composables/useColorScheme'
import {useBodyClass} from '@/composables/useBodyClass'
import AddToHomeScreen from '@/components/home/AddToHomeScreen.vue'
import DemoMode from '@/components/home/DemoMode.vue'
const baseStore = useBaseStore()
const authStore = useAuthStore()
@ -92,7 +96,7 @@ watch(userEmailConfirm, (userEmailConfirm) => {
router.push({name: 'user.login'})
}, { immediate: true })
setLanguage()
setLanguage(authStore.settings.language)
useColorScheme()
</script>

Binary file not shown.

4
src/assets/checkbox.svg Normal file
View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18px" height="18px" viewBox="0 0 18 18" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z" stroke-dasharray="60"></path>
<polyline points="1 9 7 14 15 4" stroke-dasharray="22" stroke-dashoffset="66"></polyline>
</svg>

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 KiB

After

Width:  |  Height:  |  Size: 313 KiB

View File

@ -63,7 +63,7 @@ import {unrefElement} from '@vueuse/core'
import {ref, type HTMLAttributes} from 'vue'
import type {RouteLocationRaw} from 'vue-router'
export interface BaseButtonProps extends HTMLAttributes {
export interface BaseButtonProps extends /* @vue-ignore */ HTMLAttributes {
type?: BaseButtonTypes
disabled?: boolean
to?: RouteLocationRaw

View File

@ -0,0 +1,54 @@
<template>
<div class="base-checkbox" v-cy="'checkbox'">
<input
type="checkbox"
:id="checkboxId"
class="is-sr-only"
:checked="modelValue"
@change="(event) => emit('update:modelValue', (event.target as HTMLInputElement).checked)"
:disabled="disabled || undefined"
/>
<slot name="label" :checkboxId="checkboxId">
<label :for="checkboxId" class="base-checkbox__label">
<slot/>
</label>
</slot>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import {createRandomID} from '@/helpers/randomId'
defineProps({
modelValue: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
})
const emit = defineEmits<{
(event: 'update:modelValue', value: boolean): void
}>()
const checkboxId = ref(`fancycheckbox_${createRandomID()}`)
</script>
<style lang="scss" scoped>
.base-checkbox__label {
cursor: pointer;
user-select: none;
-webkit-tap-highlight-color: transparent;
display: inline-flex;
}
.base-checkbox:has(input:disabled) .base-checkbox__label {
cursor:not-allowed;
pointer-events: none;
}
</style>

View File

@ -32,7 +32,7 @@ import {computed, ref} from 'vue'
import {getInheritedBackgroundColor} from '@/helpers/getInheritedBackgroundColor'
const props = defineProps({
/** Wheather the Expandable is open or not */
/** Whether the Expandable is open or not */
open: {
type: Boolean,
default: false,

View File

@ -0,0 +1,11 @@
<script setup lang="ts">
import datemathHelp from './datemathHelp.vue'
</script>
<template>
<Story>
<Variant title="Default">
<datemathHelp />
</Variant>
</Story>
</template>

View File

@ -1,7 +1,8 @@
<template>
<card
class="has-no-shadow how-it-works-modal"
:title="$t('input.datemathHelp.title')">
:title="$t('input.datemathHelp.title')"
>
<p>
{{ $t('input.datemathHelp.intro') }}
</p>
@ -27,11 +28,11 @@
</p>
<p>{{ $t('misc.forExample') }}</p>
<ul>
<li><code>+1d</code>{{ $t('input.datemathHelp.add1Day') }}</li>
<li><code>-1d</code>{{ $t('input.datemathHelp.minus1Day') }}</li>
<li><code>/d</code>{{ $t('input.datemathHelp.roundDay') }}</li>
<li><code>+1d</code> {{ $t('input.datemathHelp.add1Day') }}</li>
<li><code>-1d</code> {{ $t('input.datemathHelp.minus1Day') }}</li>
<li><code>/d</code> {{ $t('input.datemathHelp.roundDay') }}</li>
</ul>
<p>{{ $t('input.datemathHelp.supportedUnits') }}</p>
<h3>{{ $t('input.datemathHelp.supportedUnits') }}</h3>
<table class="table">
<tbody>
<tr>
@ -69,7 +70,7 @@
</tbody>
</table>
<p>{{ $t('input.datemathHelp.someExamples') }}</p>
<h3>{{ $t('input.datemathHelp.someExamples') }}</h3>
<table class="table">
<tbody>
<tr>
@ -100,7 +101,7 @@
<td><code>{{ exampleDate }}||+1M/d</code></td>
<td>
<i18n-t keypath="input.datemathHelp.examples.datePlusMonth" scope="global">
<code>{{ exampleDate }}</code>
<strong>{{ exampleDate }}</strong>
</i18n-t>
</td>
</tr>
@ -110,13 +111,15 @@
</template>
<script lang="ts" setup>
import {formatDate} from '@/helpers/time/formatDate'
import {formatDateShort} from '@/helpers/time/formatDate'
import BaseButton from '@/components/base/BaseButton.vue'
const exampleDate = formatDate(new Date(), 'yyyy-MM-dd')
const exampleDate = formatDateShort(new Date())
</script>
<style scoped lang="scss">
// FIXME: Remove style overwrites
.how-it-works-modal {
font-size: 1rem;
}

View File

@ -0,0 +1,80 @@
<template>
<div
v-if="shouldShowMessage"
class="add-to-home-screen"
:class="{'has-update-available': hasUpdateAvailable}"
>
<icon icon="arrow-up-from-bracket" class="add-icon"/>
<p>
{{ $t('home.addToHomeScreen') }}
</p>
<BaseButton @click="() => hideMessage = true" class="hide-button">
<icon icon="x"/>
</BaseButton>
</div>
</template>
<script lang="ts" setup>
import BaseButton from '@/components/base/BaseButton.vue'
import {useLocalStorage} from '@vueuse/core'
import {computed} from 'vue'
import {useBaseStore} from '@/stores/base'
const baseStore = useBaseStore()
const hideMessage = useLocalStorage('hideAddToHomeScreenMessage', false)
const hasUpdateAvailable = computed(() => baseStore.updateAvailable)
const shouldShowMessage = computed(() => {
if (hideMessage.value) {
return false
}
if (typeof window !== 'undefined' && window.matchMedia('(display-mode: standalone)').matches) {
return false
}
return true
})
</script>
<style lang="scss" scoped>
.add-to-home-screen {
position: fixed;
// FIXME: We should prevent usage of z-index or
// at least define it centrally
// the highest z-index of a modal is .hint-modal with 4500
z-index: 5000;
bottom: 1rem;
inset-inline: 1rem;
max-width: max-content;
margin-inline: auto;
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: .5rem 1rem;
background: var(--grey-900);
border-radius: $radius;
font-size: .9rem;
color: var(--grey-200);
@media screen and (min-width: $tablet) {
display: none;
}
&.has-update-available {
bottom: 5rem;
}
}
.add-icon {
color: var(--primary-light);
}
.hide-button {
padding: .25rem .5rem;
cursor: pointer;
}
</style>

View File

@ -0,0 +1,49 @@
<script setup lang="ts">
import {computed, ref} from 'vue'
import {useConfigStore} from '@/stores/config'
import BaseButton from '@/components/base/BaseButton.vue'
const configStore = useConfigStore()
const hide = ref(false)
const enabled = computed(() => configStore.demoModeEnabled && !hide.value)
</script>
<template>
<div
v-if="enabled"
class="demo-mode-banner"
>
<p>
{{ $t('demo.title') }}
<strong class="is-uppercase">{{ $t('demo.everythingWillBeDeleted') }}</strong>
</p>
<BaseButton @click="() => hide = true" class="hide-button">
<icon icon="times"/>
</BaseButton>
</div>
</template>
<style scoped lang="scss">
.demo-mode-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: var(--danger);
z-index: 100;
padding: .5rem;
text-align: center;
&, strong {
color: hsl(220, 13%, 91%) !important; // --grey-200 in light mode, hardcoded because the color should not change
}
}
.hide-button {
padding: .25rem .5rem;
cursor: pointer;
position: absolute;
right: .5rem;
top: .25rem;
}
</style>

View File

@ -4,17 +4,26 @@ import { useNow } from '@vueuse/core'
import LogoFull from '@/assets/logo-full.svg?component'
import LogoFullPride from '@/assets/logo-full-pride.svg?component'
import {MILLISECONDS_A_HOUR} from '@/constants/date'
const now = useNow()
const Logo = computed(() => now.value.getMonth() === 5 ? LogoFullPride : LogoFull)
const now = useNow({
interval: MILLISECONDS_A_HOUR,
})
const Logo = computed(() => window.ALLOW_ICON_CHANGES && now.value.getMonth() === 6 ? LogoFullPride : LogoFull)
const CustomLogo = computed(() => window.CUSTOM_LOGO_URL)
</script>
<template>
<Logo alt="Vikunja" class="logo" />
<div>
<Logo v-if="!CustomLogo" alt="Vikunja" class="logo" />
<img v-show="CustomLogo" :src="CustomLogo" alt="Vikunja" class="logo" />
</div>
</template>
<style lang="scss" scoped>
.logo {
color: var(--logo-text-color);
max-width: 168px;
max-height: 48px;
}
</style>

View File

@ -3,7 +3,7 @@
class="menu-show-button"
@click="baseStore.toggleMenu()"
@shortkey="() => baseStore.toggleMenu()"
v-shortcut="'Control+e'"
v-shortcut="'Mod+e'"
:title="$t('keyboardShortcuts.toggleMenu')"
:aria-label="menuActive ? $t('misc.hideMenu') : $t('misc.showMenu')"
/>

View File

@ -0,0 +1,109 @@
<template>
<draggable
v-model="availableProjects"
animation="100"
ghostClass="ghost"
group="projects"
@start="() => drag = true"
@end="saveProjectPosition"
handle=".handle"
tag="menu"
item-key="id"
:disabled="!canEditOrder"
filter=".drag-disabled"
:component-data="{
type: 'transition-group',
name: !drag ? 'flip-list' : null,
class: [
'menu-list can-be-hidden',
{ 'dragging-disabled': !canEditOrder }
],
}"
>
<template #item="{element: project}">
<ProjectsNavigationItem
:class="{'drag-disabled': project.id < 0}"
:project="project"
:is-loading="projectUpdating[project.id]"
:can-collapse="canCollapse"
:level="level"
:data-project-id="project.id"
/>
</template>
</draggable>
</template>
<script lang="ts" setup>
import {ref, watch} from 'vue'
import draggable from 'zhyswan-vuedraggable'
import type {SortableEvent} from 'sortablejs'
import ProjectsNavigationItem from '@/components/home/ProjectsNavigationItem.vue'
import {calculateItemPosition} from '@/helpers/calculateItemPosition'
import type {IProject} from '@/modelTypes/IProject'
import {useProjectStore} from '@/stores/projects'
const props = defineProps<{
modelValue?: IProject[],
canEditOrder: boolean,
canCollapse?: boolean,
level?: number,
}>()
const emit = defineEmits<{
(e: 'update:modelValue', projects: IProject[]): void
}>()
const drag = ref(false)
const projectStore = useProjectStore()
// Vue draggable will modify the projects list as it changes their position which will not work on a prop.
// Hence, we'll clone the prop and work on the clone.
const availableProjects = ref<IProject[]>([])
watch(
() => props.modelValue,
projects => {
availableProjects.value = projects || []
},
{immediate: true},
)
const projectUpdating = ref<{ [id: IProject['id']]: boolean }>({})
async function saveProjectPosition(e: SortableEvent) {
if (!e.newIndex && e.newIndex !== 0) return
const projectsActive = availableProjects.value
// If the project was dragged to the last position, Safari will report e.newIndex as the size of the projectsActive
// array instead of using the position. Because the index is wrong in that case, dragging the project will fail.
// To work around that we're explicitly checking that case here and decrease the index.
const newIndex = e.newIndex === projectsActive.length ? e.newIndex - 1 : e.newIndex
const projectId = parseInt(e.item.dataset.projectId)
const project = projectStore.projects[projectId]
const parentProjectId = e.to.parentNode.dataset.projectId ? parseInt(e.to.parentNode.dataset.projectId) : 0
const projectBefore = projectsActive[newIndex - 1] ?? null
const projectAfter = projectsActive[newIndex + 1] ?? null
projectUpdating.value[project.id] = true
const position = calculateItemPosition(
projectBefore !== null ? projectBefore.position : null,
projectAfter !== null ? projectAfter.position : null,
)
try {
// create a copy of the project in order to not violate pinia manipulation
await projectStore.updateProject({
...project,
position,
parentProjectId,
})
emit('update:modelValue', availableProjects.value)
} finally {
projectUpdating.value[project.id] = false
}
}
</script>

View File

@ -0,0 +1,186 @@
<template>
<li
class="list-menu loader-container is-loading-small"
:class="{'is-loading': isLoading}"
>
<div>
<BaseButton
v-if="canCollapse && childProjects?.length > 0"
@click="childProjectsOpen = !childProjectsOpen"
class="collapse-project-button"
>
<icon icon="chevron-down" :class="{ 'project-is-collapsed': !childProjectsOpen }"/>
</BaseButton>
<BaseButton
:to="{ name: 'project.index', params: { projectId: project.id} }"
class="list-menu-link"
:class="{'router-link-exact-active': currentProject?.id === project.id}"
>
<span
v-if="!canCollapse || childProjects?.length === 0"
class="collapse-project-button-placeholder"
></span>
<div class="color-bubble-handle-wrapper" :class="{'is-draggable': project.id > 0}">
<ColorBubble
v-if="project.hexColor !== ''"
:color="project.hexColor"
/>
<span v-else-if="project.id < -1" class="saved-filter-icon icon menu-item-icon">
<icon icon="filter"/>
</span>
<span
v-if="project.id > 0"
class="icon menu-item-icon handle lines-handle"
:class="{'has-color-bubble': project.hexColor !== ''}"
>
<icon icon="grip-lines"/>
</span>
</div>
<span class="project-menu-title">{{ getProjectTitle(project) }}</span>
</BaseButton>
<BaseButton
v-if="project.id > 0"
class="favorite"
:class="{'is-favorite': project.isFavorite}"
@click="projectStore.toggleProjectFavorite(project)"
>
<icon :icon="project.isFavorite ? 'star' : ['far', 'star']"/>
</BaseButton>
<ProjectSettingsDropdown
v-if="project.id > 0"
class="menu-list-dropdown"
:project="project"
:level="level"
>
<template #trigger="{toggleOpen}">
<BaseButton class="menu-list-dropdown-trigger" @click="toggleOpen">
<icon icon="ellipsis-h" class="icon"/>
</BaseButton>
</template>
</ProjectSettingsDropdown>
<span class="list-setting-spacer" v-else></span>
</div>
<ProjectsNavigation
v-if="canNestDeeper && childProjectsOpen && canCollapse"
:model-value="childProjects"
:can-edit-order="true"
:can-collapse="canCollapse"
:level="level + 1"
/>
</li>
</template>
<script setup lang="ts">
import {computed, ref} from 'vue'
import {useProjectStore} from '@/stores/projects'
import {useBaseStore} from '@/stores/base'
import type {IProject} from '@/modelTypes/IProject'
import BaseButton from '@/components/base/BaseButton.vue'
import ProjectSettingsDropdown from '@/components/project/project-settings-dropdown.vue'
import {getProjectTitle} from '@/helpers/getProjectTitle'
import ColorBubble from '@/components/misc/colorBubble.vue'
import ProjectsNavigation from '@/components/home/ProjectsNavigation.vue'
import {canNestProjectDeeper} from '@/helpers/canNestProjectDeeper'
const {
project,
isLoading,
canCollapse,
level = 0,
} = defineProps<{
project: IProject,
isLoading?: boolean,
canCollapse?: boolean,
level?: number,
}>()
const projectStore = useProjectStore()
const baseStore = useBaseStore()
const currentProject = computed(() => baseStore.currentProject)
const childProjectsOpen = ref(true)
const childProjects = computed(() => {
if (!canNestDeeper.value) {
return []
}
return projectStore.getChildProjects(project.id)
.filter(p => !p.isArchived)
.sort((a, b) => a.position - b.position)
})
const canNestDeeper = computed(() => canNestProjectDeeper(level))
</script>
<style lang="scss" scoped>
.list-setting-spacer {
width: 5rem;
flex-shrink: 0;
}
.project-is-collapsed {
transform: rotate(-90deg);
}
.favorite {
transition: opacity $transition, color $transition;
opacity: 0;
&:hover,
&.is-favorite {
opacity: 1;
color: var(--warning);
}
}
.list-menu:hover > div > .favorite {
opacity: 1;
}
.list-menu:hover > div > a > .color-bubble-handle-wrapper.is-draggable > {
.saved-filter-icon,
.color-bubble {
opacity: 0;
}
}
.is-touch .color-bubble {
opacity: 1 !important;
}
.color-bubble-handle-wrapper {
position: relative;
width: 1rem;
height: 1rem;
display: flex;
align-items: center;
justify-content: flex-start;
margin-right: .25rem;
flex-shrink: 0;
.color-bubble, .icon {
transition: all $transition;
position: absolute;
width: 12px;
margin: 0 !important;
padding: 0 !important;
}
}
.project-menu-title {
overflow: hidden;
text-overflow: ellipsis;
}
.saved-filter-icon {
color: var(--grey-300) !important;
font-size: .75rem;
}
.is-touch .handle.has-color-bubble {
display: none !important;
}
</style>

View File

@ -7,8 +7,9 @@
<MenuButton class="menu-button" />
<div v-if="currentProject.id" class="project-title-wrapper">
<h1 class="project-title">{{ currentProject.title === '' ? $t('misc.loading') : getProjectTitle(currentProject) }}
<div v-if="currentProject?.id" class="project-title-wrapper">
<h1 class="project-title">
{{ currentProject.title === '' ? $t('misc.loading') : getProjectTitle(currentProject) }}
</h1>
<BaseButton :to="{ name: 'project.info', params: { projectId: currentProject.id } }" class="project-title-button">
@ -26,10 +27,7 @@
</div>
<div class="navbar-end">
<BaseButton @click="openQuickActions" class="trigger-button" v-shortcut="'Control+k'"
:title="$t('keyboardShortcuts.quickSearch')">
<icon icon="search" />
</BaseButton>
<OpenQuickActions/>
<Notifications />
<dropdown>
<template #trigger="{ toggleOpen, open }">
@ -79,6 +77,7 @@ import Notifications from '@/components/notifications/notifications.vue'
import Logo from '@/components/home/Logo.vue'
import BaseButton from '@/components/base/BaseButton.vue'
import MenuButton from '@/components/home/MenuButton.vue'
import OpenQuickActions from '@/components/misc/OpenQuickActions.vue'
import { getProjectTitle } from '@/helpers/getProjectTitle'
@ -89,7 +88,7 @@ import { useAuthStore } from '@/stores/auth'
const baseStore = useBaseStore()
const currentProject = computed(() => baseStore.currentProject)
const background = computed(() => baseStore.background)
const canWriteCurrentProject = computed(() => baseStore.currentProject.maxRight > Rights.READ)
const canWriteCurrentProject = computed(() => baseStore.currentProject?.maxRight > Rights.READ)
const menuActive = computed(() => baseStore.menuActive)
const authStore = useAuthStore()
@ -97,10 +96,6 @@ const authStore = useAuthStore()
const configStore = useConfigStore()
const imprintUrl = computed(() => configStore.legal.imprintUrl)
const privacyPolicyUrl = computed(() => configStore.legal.privacyPolicyUrl)
function openQuickActions() {
baseStore.setQuickActionsActive(true)
}
</script>
<style lang="scss" scoped>

View File

@ -12,9 +12,12 @@
</template>
<script lang="ts" setup>
import {ref} from 'vue'
import {computed, ref} from 'vue'
import {useBaseStore} from '@/stores/base'
const updateAvailable = ref(false)
const baseStore = useBaseStore()
const updateAvailable = computed(() => baseStore.updateAvailable)
const registration = ref(null)
const refreshing = ref(false)
@ -31,11 +34,11 @@ navigator?.serviceWorker?.addEventListener(
function showRefreshUI(e: Event) {
console.log('recieved refresh event', e)
registration.value = e.detail
updateAvailable.value = true
baseStore.setUpdateAvailable(true)
}
function refreshApp() {
updateAvailable.value = false
baseStore.setUpdateAvailable(false)
if (!registration.value || !registration.value.waiting) {
return
}
@ -60,12 +63,11 @@ function refreshApp() {
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: .5rem;
padding: .5rem .5rem .5rem 1rem;
background: $warning;
border-radius: $radius;
font-size: .9rem;
color: var(--grey-900);
color: hsl(220.9, 39.3%, 11%); // color copied to avoid it changing in dark mode
}
.update-notification__message {

View File

@ -69,6 +69,7 @@ import BaseButton from '@/components/base/BaseButton.vue'
import {useBaseStore} from '@/stores/base'
import {useLabelStore} from '@/stores/labels'
import {useProjectStore} from '@/stores/projects'
import {useRouteWithModal} from '@/composables/useRouteWithModal'
import {useRenewTokenOnFocus} from '@/composables/useRenewTokenOnFocus'
@ -94,14 +95,13 @@ watch(() => route.name as string, (routeName) => {
(
[
'home',
'namespace.edit',
'teams.index',
'teams.edit',
'tasks.range',
'labels.index',
'migrate.start',
'migrate.wunderlist',
'namespaces.index',
'projects.index',
].includes(routeName) ||
routeName.startsWith('user.settings')
)
@ -116,6 +116,9 @@ useRenewTokenOnFocus()
const labelStore = useLabelStore()
labelStore.loadAllLabels()
const projectStore = useProjectStore()
projectStore.loadProjects()
</script>
<style lang="scss" scoped>

View File

@ -9,9 +9,9 @@
<Logo class="logo" v-if="logoVisible"/>
<h1
:class="{'m-0': !logoVisible}"
:style="{ 'opacity': currentProject.title === '' ? '0': '1' }"
:style="{ 'opacity': currentProject?.title === '' ? '0': '1' }"
class="title">
{{ currentProject.title === '' ? $t('misc.loading') : currentProject.title }}
{{ currentProject?.title === '' ? $t('misc.loading') : currentProject?.title }}
</h1>
<div class="box has-text-left view">
<router-view/>

View File

@ -1,10 +1,10 @@
<template>
<aside :class="{'is-active': menuActive}" class="namespace-container">
<aside :class="{'is-active': baseStore.menuActive}" class="menu-container">
<nav class="menu top-menu">
<router-link :to="{name: 'home'}" class="logo">
<Logo width="164" height="48"/>
</router-link>
<ul class="menu-list">
<menu class="menu-list other-menu-items">
<li>
<router-link :to="{ name: 'home'}" v-shortcut="'g o'">
<span class="menu-item-icon icon">
@ -22,11 +22,11 @@
</router-link>
</li>
<li>
<router-link :to="{ name: 'namespaces.index'}" v-shortcut="'g n'">
<router-link :to="{ name: 'projects.index'}" v-shortcut="'g p'">
<span class="menu-item-icon icon">
<icon icon="layer-group"/>
</span>
{{ $t('namespace.title') }}
{{ $t('project.projects') }}
</router-link>
</li>
<li>
@ -45,238 +45,64 @@
{{ $t('team.title') }}
</router-link>
</li>
</ul>
</menu>
</nav>
<nav class="menu namespaces-lists loader-container is-loading-small" :class="{'is-loading': loading}">
<template v-for="(n, nk) in namespaces" :key="n.id">
<div class="namespace-title" :class="{'has-menu': n.id > 0}">
<BaseButton
@click="toggleProjects(n.id)"
class="menu-label"
v-tooltip="namespaceTitles[nk]"
>
<ColorBubble
v-if="n.hexColor !== ''"
:color="n.hexColor"
class="mr-1"
/>
<span class="name">{{ namespaceTitles[nk] }}</span>
<div
class="icon menu-item-icon is-small toggle-lists-icon pl-2"
:class="{'active': typeof projectsVisible[n.id] !== 'undefined' ? projectsVisible[n.id] : true}"
>
<icon icon="chevron-down"/>
</div>
<span class="count" :class="{'ml-2 mr-0': n.id > 0}">
({{ namespaceProjectsCount[nk] }})
</span>
</BaseButton>
<namespace-settings-dropdown class="menu-list-dropdown" :namespace="n" v-if="n.id > 0"/>
</div>
<!--
NOTE: a v-model / computed setter is not possible, since the updateActiveProjects function
triggered by the change needs to have access to the current namespace
-->
<draggable
v-if="projectsVisible[n.id] ?? true"
v-bind="dragOptions"
:modelValue="activeProjects[nk]"
@update:modelValue="(projects) => updateActiveProjects(n, projects)"
group="namespace-lists"
@start="() => drag = true"
@end="saveListPosition"
handle=".handle"
:disabled="n.id < 0 || undefined"
tag="ul"
item-key="id"
:data-namespace-id="n.id"
:data-namespace-index="nk"
:component-data="{
type: 'transition-group',
name: !drag ? 'flip-list' : null,
class: [
'menu-list can-be-hidden',
{ 'dragging-disabled': n.id < 0 }
]
}"
>
<template #item="{element: l}">
<li
class="list-menu loader-container is-loading-small"
:class="{'is-loading': projectUpdating[l.id]}"
>
<BaseButton
:to="{ name: 'project.index', params: { projectId: l.id} }"
class="list-menu-link"
:class="{'router-link-exact-active': currentProject.id === l.id}"
>
<span class="icon menu-item-icon handle">
<icon icon="grip-lines"/>
</span>
<ColorBubble
v-if="l.hexColor !== ''"
:color="l.hexColor"
class="mr-1"
/>
<span class="list-menu-title">{{ getProjectTitle(l) }}</span>
</BaseButton>
<BaseButton
v-if="l.id > 0"
class="favorite"
:class="{'is-favorite': l.isFavorite}"
@click="projectStore.toggleProjectFavorite(l)"
>
<icon :icon="l.isFavorite ? 'star' : ['far', 'star']"/>
</BaseButton>
<ProjectSettingsDropdown class="menu-list-dropdown" :project="l" v-if="l.id > 0">
<template #trigger="{toggleOpen}">
<BaseButton class="menu-list-dropdown-trigger" @click="toggleOpen">
<icon icon="ellipsis-h" class="icon"/>
</BaseButton>
</template>
</ProjectSettingsDropdown>
<span class="list-setting-spacer" v-else></span>
</li>
</template>
</draggable>
</template>
</nav>
<Loading
v-if="projectStore.isLoading"
variant="small"
/>
<template v-else>
<nav class="menu" v-if="favoriteProjects">
<ProjectsNavigation
:model-value="favoriteProjects"
:can-edit-order="false"
:can-collapse="false"
/>
</nav>
<nav class="menu" v-if="savedFilterProjects">
<ProjectsNavigation
:model-value="savedFilterProjects"
:can-edit-order="false"
:can-collapse="false"
/>
</nav>
<nav class="menu">
<ProjectsNavigation
:model-value="projects"
:can-edit-order="true"
:can-collapse="true"
:level="1"
/>
</nav>
</template>
<PoweredByLink/>
</aside>
</template>
<script setup lang="ts">
import {ref, computed, onBeforeMount} from 'vue'
import draggable from 'zhyswan-vuedraggable'
import type {SortableEvent} from 'sortablejs'
import {computed} from 'vue'
import BaseButton from '@/components/base/BaseButton.vue'
import ProjectSettingsDropdown from '@/components/project/project-settings-dropdown.vue'
import NamespaceSettingsDropdown from '@/components/namespace/namespace-settings-dropdown.vue'
import PoweredByLink from '@/components/home/PoweredByLink.vue'
import Logo from '@/components/home/Logo.vue'
import {calculateItemPosition} from '@/helpers/calculateItemPosition'
import {getNamespaceTitle} from '@/helpers/getNamespaceTitle'
import {getProjectTitle} from '@/helpers/getProjectTitle'
import type {IProject} from '@/modelTypes/IProject'
import type {INamespace} from '@/modelTypes/INamespace'
import ColorBubble from '@/components/misc/colorBubble.vue'
import Loading from '@/components/misc/loading.vue'
import {useBaseStore} from '@/stores/base'
import {useProjectStore} from '@/stores/projects'
import {useNamespaceStore} from '@/stores/namespaces'
const drag = ref(false)
const dragOptions = {
animation: 100,
ghostClass: 'ghost',
}
import ProjectsNavigation from '@/components/home/ProjectsNavigation.vue'
const baseStore = useBaseStore()
const namespaceStore = useNamespaceStore()
const currentProject = computed(() => baseStore.currentProject)
const menuActive = computed(() => baseStore.menuActive)
const loading = computed(() => namespaceStore.isLoading)
const namespaces = computed(() => {
return namespaceStore.namespaces.filter(n => !n.isArchived)
})
const activeProjects = computed(() => {
return namespaces.value.map(({projects}) => {
return projects?.filter(item => {
return typeof item !== 'undefined' && !item.isArchived
})
})
})
const namespaceTitles = computed(() => {
return namespaces.value.map((namespace) => getNamespaceTitle(namespace))
})
const namespaceProjectsCount = computed(() => {
return namespaces.value.map((_, index) => activeProjects.value[index]?.length ?? 0)
})
const projectStore = useProjectStore()
function toggleProjects(namespaceId: INamespace['id']) {
projectsVisible.value[namespaceId] = !projectsVisible.value[namespaceId]
}
const projectsVisible = ref<{ [id: INamespace['id']]: boolean }>({})
// FIXME: async action will be unfinished when component mounts
onBeforeMount(async () => {
const namespaces = await namespaceStore.loadNamespaces()
namespaces.forEach(n => {
if (typeof projectsVisible.value[n.id] === 'undefined') {
projectsVisible.value[n.id] = true
}
})
})
function updateActiveProjects(namespace: INamespace, activeProjects: IProject[]) {
// This is a bit hacky: since we do have to filter out the archived items from the list
// for vue draggable updating it is not as simple as replacing it.
// To work around this, we merge the active projects with the archived ones. Doing so breaks the order
// because now all archived projects are sorted after the active ones. This is fine because they are sorted
// later when showing them anyway, and it makes the merging happening here a lot easier.
const projects = [
...activeProjects,
...namespace.projects.filter(l => l.isArchived),
]
namespaceStore.setNamespaceById({
...namespace,
projects,
})
}
const projectUpdating = ref<{ [id: INamespace['id']]: boolean }>({})
async function saveListPosition(e: SortableEvent) {
if (!e.newIndex && e.newIndex !== 0) return
const namespaceId = parseInt(e.to.dataset.namespaceId as string)
const newNamespaceIndex = parseInt(e.to.dataset.namespaceIndex as string)
const projectsActive = activeProjects.value[newNamespaceIndex]
// If the project was dragged to the last position, Safari will report e.newIndex as the size of the projectsActive
// array instead of using the position. Because the index is wrong in that case, dragging the project will fail.
// To work around that we're explicitly checking that case here and decrease the index.
const newIndex = e.newIndex === projectsActive.length ? e.newIndex - 1 : e.newIndex
const project = projectsActive[newIndex]
const projectBefore = projectsActive[newIndex - 1] ?? null
const projectAfter = projectsActive[newIndex + 1] ?? null
projectUpdating.value[project.id] = true
const position = calculateItemPosition(
projectBefore !== null ? projectBefore.position : null,
projectAfter !== null ? projectAfter.position : null,
)
try {
// create a copy of the project in order to not violate pinia manipulation
await projectStore.updateProject({
...project,
position,
namespaceId,
})
} finally {
projectUpdating.value[project.id] = false
}
}
const projects = computed(() => projectStore.notArchivedRootProjects)
const favoriteProjects = computed(() => projectStore.favoriteProjects)
const savedFilterProjects = computed(() => projectStore.savedFilterProjects)
</script>
<style lang="scss" scoped>
$navbar-padding: 2rem;
$vikunja-nav-background: var(--site-background);
$vikunja-nav-color: var(--grey-700);
$vikunja-nav-selected-width: 0.4rem;
.logo {
display: block;
@ -289,10 +115,10 @@ $vikunja-nav-selected-width: 0.4rem;
}
}
.namespace-container {
background: $vikunja-nav-background;
.menu-container {
background: var(--site-background);
color: $vikunja-nav-color;
padding: 0 0 1rem;
padding: 1rem 0;
transition: transform $transition-duration ease-in;
position: fixed;
top: $navbar-height;
@ -314,252 +140,24 @@ $vikunja-nav-selected-width: 0.4rem;
}
}
// these are general menu styles
// should be in own components
.menu {
.menu-label,
.menu-list .list-menu-link,
.menu-list a {
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
.color-bubble {
height: 12px;
flex: 0 0 12px;
}
}
.menu-list {
li {
height: 44px;
display: flex;
align-items: center;
&:hover {
background: var(--white);
}
.menu-list-dropdown {
opacity: 1;
transition: $transition;
}
@media(hover: hover) and (pointer: fine) {
.menu-list-dropdown {
opacity: 0;
}
&:hover .menu-list-dropdown {
opacity: 1;
}
}
}
.menu-item-icon {
color: var(--grey-400);
}
.menu-list-dropdown-trigger {
display: flex;
padding: 0.5rem;
}
.flip-list-move {
transition: transform $transition-duration;
}
.ghost {
background: var(--grey-200);
* {
opacity: 0;
}
}
a:hover {
background: transparent;
}
.list-menu-link,
li > a {
color: $vikunja-nav-color;
padding: 0.75rem .5rem 0.75rem ($navbar-padding * 1.5 - 1.75rem);
transition: all 0.2s ease;
border-radius: 0;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
width: 100%;
border-left: $vikunja-nav-selected-width solid transparent;
&:hover {
border-left: $vikunja-nav-selected-width solid var(--primary);
}
&.router-link-exact-active {
color: var(--primary);
border-left: $vikunja-nav-selected-width solid var(--primary);
}
.icon {
height: 1rem;
vertical-align: middle;
padding-right: 0.5rem;
}
&.router-link-exact-active .icon:not(.handle) {
color: var(--primary);
}
.handle {
opacity: 0;
transition: opacity $transition;
margin-right: .25rem;
}
&:hover .handle {
opacity: 1;
}
}
&:not(.dragging-disabled) .handle {
cursor: grab;
}
}
}
.top-menu {
margin-top: math.div($navbar-padding, 2);
.menu-list {
li {
font-weight: 600;
font-family: $vikunja-font;
}
.list-menu-link,
li > a {
padding-left: 2rem;
display: inline-block;
.icon {
padding-bottom: .25rem;
}
}
}
}
.namespaces-lists {
padding-top: math.div($navbar-padding, 2);
.menu-label {
font-size: 1rem;
font-weight: 700;
font-weight: bold;
font-family: $vikunja-font;
color: $vikunja-nav-color;
.top-menu .menu-list {
li {
font-weight: 600;
min-height: 2.5rem;
padding-top: 0;
padding-left: $navbar-padding;
overflow: hidden;
margin-bottom: 0;
flex: 1 1 auto;
.name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: auto;
}
.count {
color: var(--grey-500);
margin-right: .5rem;
// align brackets with number
font-feature-settings: "case";
}
font-family: $vikunja-font;
}
.favorite {
margin-left: .25rem;
transition: opacity $transition, color $transition;
opacity: 1;
.list-menu-link,
li > a {
padding-left: 2rem;
display: inline-block;
&.is-favorite {
color: var(--warning);
opacity: 1;
.icon {
padding-bottom: .25rem;
}
}
@media(hover: hover) and (pointer: fine) {
.list-menu .favorite {
opacity: 0;
}
.list-menu:hover .favorite,
.favorite.is-favorite {
opacity: 1;
}
}
.list-menu-title {
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
}
.color-bubble {
width: 14px;
height: 14px;
flex-basis: auto;
}
.is-archived {
min-width: 85px;
}
}
.namespace-title {
display: flex;
align-items: center;
justify-content: space-between;
color: $vikunja-nav-color;
padding: 0 .25rem;
.toggle-lists-icon {
svg {
transition: all $transition;
transform: rotate(90deg);
opacity: 1;
}
&.active svg {
transform: rotate(0deg);
opacity: 0;
}
}
&:hover .toggle-lists-icon svg {
opacity: 1;
}
&:not(.has-menu) .toggle-lists-icon {
padding-right: 1rem;
}
}
.list-setting-spacer {
width: 2.5rem;
flex-shrink: 0;
}
.namespaces-list.loader-container.is-loading {
min-height: calc(100vh - #{$navbar-height + 1.5rem + 1rem + 1.5rem});
.menu + .menu {
padding-top: math.div($navbar-padding, 2);
}
</style>

View File

@ -1,3 +1,5 @@
import {createAsyncComponent} from '@/helpers/createAsyncComponent'
export default createAsyncComponent(() => import('@/components/input/editor.vue'))
const TipTap = createAsyncComponent(() => import('@/components/input/editor/TipTap.vue'))
export default TipTap

View File

@ -35,7 +35,7 @@
</template>
<script setup lang="ts">
import {computed, ref, toRef, watch} from 'vue'
import {computed, ref, watch} from 'vue'
import {createRandomID} from '@/helpers/randomId'
import XButton from '@/components/input/button.vue'
@ -53,22 +53,16 @@ const lastChangeTimeout = ref<ReturnType<typeof setTimeout> | null>(null)
const defaultColors = ref(DEFAULT_COLORS)
const colorListID = ref(createRandomID())
const props = defineProps({
modelValue: {
type: String,
required: true,
},
menuPosition: {
type: String,
default: 'top',
},
})
const {
modelValue,
} = defineProps<{
modelValue: string,
}>()
const emit = defineEmits(['update:modelValue'])
const modelValue = toRef(props, 'modelValue')
watch(
modelValue,
() => modelValue,
(newValue) => {
color.value = newValue
},

View File

@ -1,63 +0,0 @@
<template>
<multiselect
v-model="selectedNamespaces"
:search-results="foundNamespaces"
:loading="namespaceService.loading"
:multiple="true"
:placeholder="$t('namespace.search')"
label="namespace"
@search="findNamespaces"
/>
</template>
<script setup lang="ts">
import {computed, ref, shallowReactive, watchEffect, type PropType} from 'vue'
import Multiselect from '@/components/input/multiselect.vue'
import type {INamespace} from '@/modelTypes/INamespace'
import NamespaceService from '@/services/namespace'
import {includesById} from '@/helpers/utils'
const props = defineProps({
modelValue: {
type: Array as PropType<INamespace[]>,
default: () => [],
},
})
const emit = defineEmits<{
(e: 'update:modelValue', value: INamespace[]): void
}>()
const namespaces = ref<INamespace[]>([])
watchEffect(() => {
namespaces.value = props.modelValue
})
const selectedNamespaces = computed({
get() {
return namespaces.value
},
set: (value) => {
namespaces.value = value
emit('update:modelValue', value)
},
})
const namespaceService = shallowReactive(new NamespaceService())
const foundNamespaces = ref<INamespace[]>([])
async function findNamespaces(query: string) {
if (query === '') {
foundNamespaces.value = []
return
}
const response = await namespaceService.getAll({}, {s: query}) as INamespace[]
// Filter selected items from the results
foundNamespaces.value = response.filter(({id}) => !includesById(namespaces.value, id))
}
</script>

View File

@ -0,0 +1,26 @@
<template>
<BaseButton class="simple-button">
<slot/>
</BaseButton>
</template>
<script lang="ts" setup>
import BaseButton from '@/components/base/BaseButton.vue'
</script>
<style lang="scss" scoped>
.simple-button {
color: var(--text);
padding: .25rem .5rem;
transition: background-color $transition;
border-radius: $radius;
display: block;
margin: .1rem 0;
width: 100%;
text-align: left;
&:hover {
background: var(--white);
}
}
</style>

View File

@ -47,7 +47,7 @@ import BaseButton, {type BaseButtonProps} from '@/components/base/BaseButton.vue
import type { IconProp } from '@fortawesome/fontawesome-svg-core'
// extending the props of the BaseButton
export interface ButtonProps extends BaseButtonProps {
export interface ButtonProps extends /* @vue-ignore */ BaseButtonProps {
variant?: ButtonTypes
icon?: IconProp
iconColor?: string

View File

@ -1,78 +1,15 @@
<template>
<div class="datepicker">
<BaseButton @click.stop="toggleDatePopup" class="show" :disabled="disabled || undefined">
<SimpleButton @click.stop="toggleDatePopup" class="show" :disabled="disabled || undefined">
{{ date === null ? chooseDateLabel : formatDateShort(date) }}
</BaseButton>
</SimpleButton>
<CustomTransition name="fade">
<div v-if="show" class="datepicker-popup" ref="datepickerPopup">
<BaseButton
v-if="(new Date()).getHours() < 21"
class="datepicker__quick-select-date"
@click.stop="setDate('today')"
>
<span class="icon"><icon :icon="['far', 'calendar-alt']"/></span>
<span class="text">
<span>{{ $t('input.datepicker.today') }}</span>
<span class="weekday">{{ getWeekdayFromStringInterval('today') }}</span>
</span>
</BaseButton>
<BaseButton
class="datepicker__quick-select-date"
@click.stop="setDate('tomorrow')"
>
<span class="icon"><icon :icon="['far', 'sun']"/></span>
<span class="text">
<span>{{ $t('input.datepicker.tomorrow') }}</span>
<span class="weekday">{{ getWeekdayFromStringInterval('tomorrow') }}</span>
</span>
</BaseButton>
<BaseButton
class="datepicker__quick-select-date"
@click.stop="setDate('nextMonday')"
>
<span class="icon"><icon icon="coffee"/></span>
<span class="text">
<span>{{ $t('input.datepicker.nextMonday') }}</span>
<span class="weekday">{{ getWeekdayFromStringInterval('nextMonday') }}</span>
</span>
</BaseButton>
<BaseButton
class="datepicker__quick-select-date"
@click.stop="setDate('thisWeekend')"
>
<span class="icon"><icon icon="cocktail"/></span>
<span class="text">
<span>{{ $t('input.datepicker.thisWeekend') }}</span>
<span class="weekday">{{ getWeekdayFromStringInterval('thisWeekend') }}</span>
</span>
</BaseButton>
<BaseButton
class="datepicker__quick-select-date"
@click.stop="setDate('laterThisWeek')"
>
<span class="icon"><icon icon="chess-knight"/></span>
<span class="text">
<span>{{ $t('input.datepicker.laterThisWeek') }}</span>
<span class="weekday">{{ getWeekdayFromStringInterval('laterThisWeek') }}</span>
</span>
</BaseButton>
<BaseButton
class="datepicker__quick-select-date"
@click.stop="setDate('nextWeek')"
>
<span class="icon"><icon icon="forward"/></span>
<span class="text">
<span>{{ $t('input.datepicker.nextWeek') }}</span>
<span class="weekday">{{ getWeekdayFromStringInterval('nextWeek') }}</span>
</span>
</BaseButton>
<flat-pickr
:config="flatPickerConfig"
class="input"
v-model="flatPickrDate"
<DatepickerInline
v-model="date"
@update:model-value="updateData"
/>
<x-button
@ -89,19 +26,15 @@
</template>
<script setup lang="ts">
import {ref, onMounted, onBeforeUnmount, toRef, watch, computed, type PropType} from 'vue'
import flatPickr from 'vue-flatpickr-component'
import 'flatpickr/dist/flatpickr.css'
import {ref, onMounted, onBeforeUnmount, toRef, watch, type PropType} from 'vue'
import BaseButton from '@/components/base/BaseButton.vue'
import CustomTransition from '@/components/misc/CustomTransition.vue'
import DatepickerInline from '@/components/input/datepickerInline.vue'
import SimpleButton from '@/components/input/SimpleButton.vue'
import {formatDate, formatDateShort} from '@/helpers/time/formatDate'
import {calculateDayInterval} from '@/helpers/time/calculateDayInterval'
import {calculateNearestHours} from '@/helpers/time/calculateNearestHours'
import {formatDateShort} from '@/helpers/time/formatDate'
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import {createDateFromString} from '@/helpers/time/createDateFromString'
import {useAuthStore} from '@/stores/auth'
import {useI18n} from 'vue-i18n'
const props = defineProps({
@ -125,8 +58,6 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue', 'close', 'close-on-change'])
const {t} = useI18n({useScope: 'global'})
const date = ref<Date | null>()
const show = ref(false)
const changed = ref(false)
@ -141,37 +72,6 @@ watch(
{immediate: true},
)
const authStore = useAuthStore()
const weekStart = computed(() => authStore.settings.weekStart)
const flatPickerConfig = computed(() => ({
altFormat: t('date.altFormatLong'),
altInput: true,
dateFormat: 'Y-m-d H:i',
enableTime: true,
time_24hr: true,
inline: true,
locale: {
firstDayOfWeek: weekStart.value,
},
}))
// Since flatpickr dates are strings, we need to convert them to native date objects.
// To make that work, we need a separate variable since flatpickr does not have a change event.
const flatPickrDate = computed({
set(newValue: string | Date) {
date.value = createDateFromString(newValue)
updateData()
},
get() {
if (!date.value) {
return ''
}
return formatDate(date.value, 'yyy-LL-dd H:mm')
},
})
function setDateValue(dateString: string | Date | null) {
if (dateString === null) {
date.value = null
@ -212,29 +112,6 @@ function close() {
}
}, 200)
}
function setDate(dateString: string) {
if (date.value === null) {
date.value = new Date()
}
const interval = calculateDayInterval(dateString)
const newDate = new Date()
newDate.setDate(newDate.getDate() + interval)
newDate.setHours(calculateNearestHours(newDate))
newDate.setMinutes(0)
newDate.setSeconds(0)
date.value = newDate
flatPickrDate.value = newDate
updateData()
}
function getWeekdayFromStringInterval(dateString: string) {
const interval = calculateDayInterval(dateString)
const newDate = new Date()
newDate.setDate(newDate.getDate() + interval)
return formatDate(newDate, 'E')
}
</script>
<style lang="scss" scoped>
@ -257,42 +134,6 @@ function getWeekdayFromStringInterval(dateString: string) {
}
}
.datepicker__quick-select-date {
display: flex;
align-items: center;
padding: 0 .5rem;
width: 100%;
height: 2.25rem;
color: var(--text);
transition: all $transition;
&:first-child {
border-radius: $radius $radius 0 0;
}
&:hover {
background: var(--grey-100);
}
.text {
width: 100%;
font-size: .85rem;
display: flex;
justify-content: space-between;
padding-right: .25rem;
.weekday {
color: var(--text-light);
text-transform: capitalize;
}
}
.icon {
width: 2rem;
text-align: center;
}
}
.datepicker__close-button {
margin: 1rem;
width: calc(100% - 2rem);

View File

@ -0,0 +1,228 @@
<template>
<BaseButton
v-if="(new Date()).getHours() < 21"
class="datepicker__quick-select-date"
@click.stop="setDate('today')"
>
<span class="icon"><icon :icon="['far', 'calendar-alt']"/></span>
<span class="text">
<span>{{ $t('input.datepicker.today') }}</span>
<span class="weekday">{{ getWeekdayFromStringInterval('today') }}</span>
</span>
</BaseButton>
<BaseButton
class="datepicker__quick-select-date"
@click.stop="setDate('tomorrow')"
>
<span class="icon"><icon :icon="['far', 'sun']"/></span>
<span class="text">
<span>{{ $t('input.datepicker.tomorrow') }}</span>
<span class="weekday">{{ getWeekdayFromStringInterval('tomorrow') }}</span>
</span>
</BaseButton>
<BaseButton
class="datepicker__quick-select-date"
@click.stop="setDate('nextMonday')"
>
<span class="icon"><icon icon="coffee"/></span>
<span class="text">
<span>{{ $t('input.datepicker.nextMonday') }}</span>
<span class="weekday">{{ getWeekdayFromStringInterval('nextMonday') }}</span>
</span>
</BaseButton>
<BaseButton
class="datepicker__quick-select-date"
@click.stop="setDate('thisWeekend')"
>
<span class="icon"><icon icon="cocktail"/></span>
<span class="text">
<span>{{ $t('input.datepicker.thisWeekend') }}</span>
<span class="weekday">{{ getWeekdayFromStringInterval('thisWeekend') }}</span>
</span>
</BaseButton>
<BaseButton
class="datepicker__quick-select-date"
@click.stop="setDate('laterThisWeek')"
>
<span class="icon"><icon icon="chess-knight"/></span>
<span class="text">
<span>{{ $t('input.datepicker.laterThisWeek') }}</span>
<span class="weekday">{{ getWeekdayFromStringInterval('laterThisWeek') }}</span>
</span>
</BaseButton>
<BaseButton
class="datepicker__quick-select-date"
@click.stop="setDate('nextWeek')"
>
<span class="icon"><icon icon="forward"/></span>
<span class="text">
<span>{{ $t('input.datepicker.nextWeek') }}</span>
<span class="weekday">{{ getWeekdayFromStringInterval('nextWeek') }}</span>
</span>
</BaseButton>
<div class="flatpickr-container">
<flat-pickr
:config="flatPickerConfig"
v-model="flatPickrDate"
/>
</div>
</template>
<script lang="ts" setup>
import {ref, toRef, watch, computed, type PropType} from 'vue'
import flatPickr from 'vue-flatpickr-component'
import 'flatpickr/dist/flatpickr.css'
import BaseButton from '@/components/base/BaseButton.vue'
import {formatDate} from '@/helpers/time/formatDate'
import {calculateDayInterval} from '@/helpers/time/calculateDayInterval'
import {calculateNearestHours} from '@/helpers/time/calculateNearestHours'
import {createDateFromString} from '@/helpers/time/createDateFromString'
import {useAuthStore} from '@/stores/auth'
import {useI18n} from 'vue-i18n'
const props = defineProps({
modelValue: {
type: [Date, null, String] as PropType<Date | null | string>,
validator: prop => prop instanceof Date || prop === null || typeof prop === 'string',
default: null,
},
})
const emit = defineEmits(['update:modelValue', 'close-on-change'])
const {t} = useI18n({useScope: 'global'})
const date = ref<Date | null>()
const changed = ref(false)
const modelValue = toRef(props, 'modelValue')
watch(
modelValue,
setDateValue,
{immediate: true},
)
const authStore = useAuthStore()
const weekStart = computed(() => authStore.settings.weekStart)
const flatPickerConfig = computed(() => ({
altFormat: t('date.altFormatLong'),
altInput: true,
dateFormat: 'Y-m-d H:i',
enableTime: true,
time_24hr: true,
inline: true,
locale: {
firstDayOfWeek: weekStart.value,
},
}))
// Since flatpickr dates are strings, we need to convert them to native date objects.
// To make that work, we need a separate variable since flatpickr does not have a change event.
const flatPickrDate = computed({
set(newValue: string | Date | null) {
if (newValue === null) {
date.value = null
return
}
date.value = createDateFromString(newValue)
updateData()
},
get() {
if (!date.value) {
return ''
}
return formatDate(date.value, 'yyy-LL-dd H:mm')
},
})
function setDateValue(dateString: string | Date | null) {
if (dateString === null) {
date.value = null
return
}
date.value = createDateFromString(dateString)
}
function updateData() {
changed.value = true
emit('update:modelValue', date.value)
}
function setDate(dateString: string) {
if (date.value === null) {
date.value = new Date()
}
const interval = calculateDayInterval(dateString)
const newDate = new Date()
newDate.setDate(newDate.getDate() + interval)
newDate.setHours(calculateNearestHours(newDate))
newDate.setMinutes(0)
newDate.setSeconds(0)
date.value = newDate
flatPickrDate.value = newDate
updateData()
}
function getWeekdayFromStringInterval(dateString: string) {
const interval = calculateDayInterval(dateString)
const newDate = new Date()
newDate.setDate(newDate.getDate() + interval)
return formatDate(newDate, 'E')
}
</script>
<style lang="scss" scoped>
.datepicker__quick-select-date {
display: flex;
align-items: center;
padding: 0 .5rem;
width: 100%;
height: 2.25rem;
color: var(--text);
transition: all $transition;
&:first-child {
border-radius: $radius $radius 0 0;
}
&:hover {
background: var(--grey-100);
}
.text {
width: 100%;
font-size: .85rem;
display: flex;
justify-content: space-between;
padding-right: .25rem;
.weekday {
color: var(--text-light);
text-transform: capitalize;
}
}
.icon {
width: 2rem;
text-align: center;
}
}
.flatpickr-container {
:deep(.flatpickr-calendar) {
margin: 0 auto 8px;
box-shadow: none;
}
:deep(.input) {
border: none;
}
}
</style>

View File

@ -1,446 +0,0 @@
<template>
<div class="editor">
<div class="clear"></div>
<vue-easymde
:configs="config"
@change="() => bubble()"
@update:modelValue="handleInput"
class="content"
v-if="isEditActive"
v-model="text"/>
<div class="preview content" v-html="preview" v-if="isPreviewActive && text !== ''">
</div>
<p class="has-text-centered has-text-grey is-italic my-5" v-if="showPreviewText">
{{ emptyText }}
<template v-if="isEditEnabled">
<ButtonLink
@click="toggleEdit"
v-shortcut="editShortcut"
class="d-print-none">
{{ $t('input.editor.edit') }}
</ButtonLink>.
</template>
</p>
<ul class="actions d-print-none" v-if="bottomActions.length > 0">
<li v-if="isEditEnabled && !showPreviewText && showSave">
<BaseButton
v-if="showEditButton"
@click="toggleEdit"
v-shortcut="editShortcut">
{{ $t('input.editor.edit') }}
</BaseButton>
<BaseButton
v-else-if="isEditActive"
@click="toggleEdit"
class="done-edit">
{{ $t('misc.save') }}
</BaseButton>
</li>
<li v-for="(action, k) in bottomActions" :key="k">
<BaseButton @click="action.action">{{ action.title }}</BaseButton>
</li>
</ul>
<template v-else-if="isEditEnabled && showSave">
<ul v-if="showEditButton" class="actions d-print-none">
<li>
<BaseButton
@click="toggleEdit"
v-shortcut="editShortcut">
{{ $t('input.editor.edit') }}
</BaseButton>
</li>
</ul>
<x-button
v-else-if="isEditActive"
@click="toggleEdit"
variant="secondary"
:shadow="false"
v-cy="'saveEditor'">
{{ $t('misc.save') }}
</x-button>
</template>
</div>
</template>
<script setup lang="ts">
import {computed, nextTick, onMounted, ref, toRefs, watch} from 'vue'
import VueEasymde from './vue-easymde.vue'
import {marked} from 'marked'
import DOMPurify from 'dompurify'
import {createEasyMDEConfig} from './editorConfig'
import AttachmentModel from '@/models/attachment'
import AttachmentService from '@/services/attachment'
import {setupMarkdownRenderer} from '@/helpers/markdownRenderer'
import {findCheckboxesInText} from '@/helpers/checklistFromText'
import {createRandomID} from '@/helpers/randomId'
import BaseButton from '@/components/base/BaseButton.vue'
import ButtonLink from '@/components/misc/ButtonLink.vue'
import type { IAttachment } from '@/modelTypes/IAttachment'
import type { ITask } from '@/modelTypes/ITask'
const props = defineProps({
modelValue: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '',
},
uploadEnabled: {
type: Boolean,
default: false,
},
uploadCallback: {
type: Function,
},
hasPreview: {
type: Boolean,
default: true,
},
previewIsDefault: {
type: Boolean,
default: true,
},
isEditEnabled: {
default: true,
},
bottomActions: {
type: Array,
default: () => [],
},
emptyText: {
type: String,
default: '',
},
showSave: {
type: Boolean,
default: false,
},
// If a key is passed the editor will go in "edit" mode when the key is pressed.
// Disabled if an empty string is passed.
editShortcut: {
type: String,
default: '',
},
})
const emit = defineEmits(['update:modelValue'])
const text = ref('')
const changeTimeout = ref<ReturnType<typeof setTimeout> | null>(null)
const isEditActive = ref(false)
const isPreviewActive = ref(true)
const showPreviewText = computed(() => isPreviewActive.value && text.value === '' && props.emptyText !== '')
const showEditButton = computed(() => !isEditActive.value && text.value !== '')
const preview = ref('')
const attachmentService = new AttachmentService()
type CacheKey = `${ITask['id']}-${IAttachment['id']}`
const loadedAttachments = ref<{[key: CacheKey]: string}>({})
const config = ref(createEasyMDEConfig({
placeholder: props.placeholder,
uploadImage: props.uploadEnabled,
imageUploadFunction: props.uploadCallback,
}))
const checkboxId = ref(createRandomID())
const {modelValue} = toRefs(props)
watch(
modelValue,
async (value) => {
text.value = value
await nextTick()
renderPreview()
},
)
watch(
text,
(newVal, oldVal) => {
// Only bubble the new value if it actually changed, but not if the component just got mounted and the text changed from the outside.
if (oldVal === '' && text.value === modelValue.value) {
return
}
bubble()
},
)
onMounted(() => {
if (modelValue.value !== '') {
text.value = modelValue.value
}
if (props.previewIsDefault && props.hasPreview) {
nextTick(() => renderPreview())
return
}
isPreviewActive.value = false
isEditActive.value = true
})
// This gets triggered when only pasting content into the editor.
// A change event would not get generated by that, an input event does.
// Therefore, we're using this handler to catch paste events.
// But because this also gets triggered when typing into the editor, we give
// it a higher timeout to make the timouts cancel each other in that case so
// that in the end, only one change event is triggered to the outside per change.
function handleInput(val: string) {
// Don't bubble if the text is up to date
if (val === text.value) {
return
}
text.value = val
bubble(1000)
}
function bubble(timeout = 500) {
if (changeTimeout.value !== null) {
clearTimeout(changeTimeout.value)
}
changeTimeout.value = setTimeout(() => {
emit('update:modelValue', text.value)
}, timeout)
}
function replaceAt(str: string, index: number, replacement: string) {
return str.slice(0, index) + replacement + str.slice(index + replacement.length)
}
function findNthIndex(str: string, n: number) {
const checkboxes = findCheckboxesInText(str)
return checkboxes[n]
}
function renderPreview() {
setupMarkdownRenderer(checkboxId.value)
preview.value = DOMPurify.sanitize(marked(text.value), {ADD_ATTR: ['target']})
// Since the render function is synchronous, we can't do async http requests in it.
// Therefore, we can't resolve the blob url at (markdown) compile time.
// To work around this, we modify the url after rendering it in the vue component.
// We're doing the whole thing in the next tick to ensure the image elements are available in the
// dom tree. If we're calling this right after setting this.preview it could be the images were
// not already made available.
// Some docs at https://stackoverflow.com/q/62865160/10924593
nextTick().then(async () => {
const attachmentImage = document.querySelectorAll<HTMLImageElement>('.attachment-image')
if (attachmentImage) {
Array.from(attachmentImage).forEach(async (img) => {
// The url is something like /tasks/<id>/attachments/<id>
const parts = img.dataset.src?.slice(window.API_URL.length + 1).split('/')
const taskId = Number(parts[1])
const attachmentId = Number(parts[3])
const cacheKey: CacheKey = `${taskId}-${attachmentId}`
if (typeof loadedAttachments.value[cacheKey] !== 'undefined') {
img.src = loadedAttachments.value[cacheKey]
return
}
const attachment = new AttachmentModel({taskId: taskId, id: attachmentId})
const url = await attachmentService.getBlobUrl(attachment)
img.src = url
loadedAttachments.value[cacheKey] = url
})
}
const textCheckbox = document.querySelectorAll<HTMLInputElement>(`.text-checkbox-${checkboxId.value}`)
if (textCheckbox) {
Array.from(textCheckbox).forEach(check => {
check.removeEventListener('change', handleCheckboxClick)
check.addEventListener('change', handleCheckboxClick)
check.parentElement?.classList.add('has-checkbox')
})
}
})
}
function handleCheckboxClick(e: Event) {
// Find the original markdown checkbox this is targeting
const checked = (e.target as HTMLInputElement).checked
const numMarkdownCheck = Number((e.target as HTMLInputElement).dataset.checkboxNum)
const index = findNthIndex(text.value, numMarkdownCheck)
if (index < 0 || typeof index === 'undefined') {
console.debug('no index found')
return
}
const projectPrefix = text.value.substring(index, index + 1)
console.debug({index, projectPrefix, checked, text: text.value})
text.value = replaceAt(text.value, index, `${projectPrefix} ${checked ? '[x]' : '[ ]'} `)
bubble()
renderPreview()
}
function toggleEdit() {
if (isEditActive.value) {
isPreviewActive.value = true
isEditActive.value = false
renderPreview()
bubble(0) // save instantly
} else {
isPreviewActive.value = false
isEditActive.value = true
}
}
</script>
<style lang="scss">
@import 'codemirror/lib/codemirror.css';
@import 'highlight.js/scss/base16/equilibrium-gray-light';
.editor {
.clear {
clear: both;
}
.preview.content {
margin-bottom: .5rem;
overflow-wrap: anywhere; // Safari does not understand "break-word" so we put that first to make sure it at least is able to show it somewhat properly there.
overflow-wrap: break-word;
ul li {
input[type="checkbox"] {
margin-right: .5rem;
}
&.has-checkbox {
margin-left: -1.25rem;
list-style: none;
}
}
}
}
.CodeMirror {
padding: .5rem;
border: 1px solid var(--grey-200) !important;
background: var(--white);
&-lines pre {
margin: 0 !important;
}
&-placeholder {
color: var(--grey-400) !important;
font-style: italic;
}
&-cursor {
border-color: var(--grey-700);
}
}
.editor-preview {
padding: 0;
&-side {
padding: .5rem;
}
}
.editor-toolbar {
background: var(--grey-50);
border: 1px solid var(--grey-200);
border-bottom: none;
button {
color: var(--grey-700);
&.active {
background: var(--grey-200);
}
svg {
vertical-align: middle;
&, rect {
width: 20px;
height: 20px;
}
}
&::after {
position: absolute;
top: 24px;
margin-left: -3px;
}
&:hover {
background: var(--grey-200);
border-color: var(--grey-300);
}
}
i.separator {
border-color: var(--grey-200) !important;
}
}
pre.CodeMirror-line {
margin-bottom: 0 !important;
color: var(--grey-700) !important;
}
.cm-header {
font-family: $vikunja-font;
font-weight: 400;
}
ul.actions {
font-size: .8rem;
margin: 0;
li {
display: inline-block;
&::after {
content: '·';
padding: 0 .25rem;
}
&:last-child:after {
content: '';
}
}
&, a {
color: var(--grey-500);
&.done-edit {
color: var(--primary);
}
}
a:hover {
text-decoration: underline;
}
}
.vue-easymde.content {
margin-bottom: 0 !important;
}
</style>

View File

@ -0,0 +1,144 @@
<template>
<div class="items">
<template v-if="items.length">
<button
class="item"
:class="{ 'is-selected': index === selectedIndex }"
v-for="(item, index) in items"
:key="index"
@click="selectItem(index)"
>
<icon :icon="item.icon"/>
<div class="description">
<p>{{ item.title }}</p>
<p>{{ item.description }}</p>
</div>
</button>
</template>
<div class="item" v-else>
No result
</div>
</div>
</template>
<script>
export default {
props: {
items: {
type: Array,
required: true,
},
command: {
type: Function,
required: true,
},
},
data() {
return {
selectedIndex: 0,
}
},
watch: {
items() {
this.selectedIndex = 0
},
},
methods: {
onKeyDown({event}) {
if (event.key === 'ArrowUp') {
this.upHandler()
return true
}
if (event.key === 'ArrowDown') {
this.downHandler()
return true
}
if (event.key === 'Enter') {
this.enterHandler()
return true
}
return false
},
upHandler() {
this.selectedIndex = ((this.selectedIndex + this.items.length) - 1) % this.items.length
},
downHandler() {
this.selectedIndex = (this.selectedIndex + 1) % this.items.length
},
enterHandler() {
this.selectItem(this.selectedIndex)
},
selectItem(index) {
const item = this.items[index]
if (item) {
this.command(item)
}
},
},
}
</script>
<style lang="scss" scoped>
.items {
padding: 0.2rem;
position: relative;
border-radius: 0.5rem;
background: #fff;
color: rgba(0, 0, 0, 0.8);
overflow: hidden;
font-size: 0.9rem;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0px 10px 20px rgba(0, 0, 0, 0.1);
}
.item {
display: flex;
align-items: center;
margin: 0;
width: 100%;
text-align: left;
background: transparent;
border-radius: $radius;
border: 0;
padding: 0.2rem 0.4rem;
transition: background-color $transition;
&.is-selected, &:hover {
background: var(--grey-100);
cursor: pointer;
}
> svg {
box-sizing: border-box;
width: 2rem;
height: 2rem;
border: 1px solid var(--grey-300);
padding: .5rem;
margin-right: .5rem;
border-radius: $radius;
color: var(--grey-700);
}
}
.description {
display: flex;
flex-direction: column;
font-size: .9rem;
p:last-child {
font-size: .75rem;
color: var(--grey-500);
}
}
</style>

View File

@ -0,0 +1,439 @@
<template>
<div class="editor-toolbar">
<div class="editor-toolbar__segment">
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
v-tooltip="$t('input.editor.heading1')"
>
<span class="icon">
<icon :icon="['fa', 'fa-header']"/>
<span class="icon__lower-text">1</span>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
v-tooltip="$t('input.editor.heading2')"
>
<span class="icon">
<icon :icon="['fa', 'fa-header']"/>
<span class="icon__lower-text">2</span>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
:class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
v-tooltip="$t('input.editor.heading3')"
>
<span class="icon">
<icon :icon="['fa', 'fa-header']"/>
<span class="icon__lower-text">3</span>
</span>
</BaseButton>
</div>
<div class="editor-toolbar__segment">
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleBold().run()"
:class="{ 'is-active': editor.isActive('bold') }"
v-tooltip="$t('input.editor.bold')"
>
<span class="icon">
<icon :icon="['fa', 'fa-bold']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleItalic().run()"
:class="{ 'is-active': editor.isActive('italic') }"
v-tooltip="$t('input.editor.italic')"
>
<span class="icon">
<icon :icon="['fa', 'fa-italic']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleUnderline().run()"
:class="{ 'is-active': editor.isActive('underline') }"
v-tooltip="$t('input.editor.underline')"
>
<span class="icon">
<icon :icon="['fa', 'fa-underline']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleStrike().run()"
:class="{ 'is-active': editor.isActive('strike') }"
v-tooltip="$t('input.editor.strikethrough')"
>
<span class="icon">
<icon :icon="['fa', 'fa-strikethrough']"/>
</span>
</BaseButton>
</div>
<div class="editor-toolbar__segment">
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleCodeBlock().run()"
:class="{ 'is-active': editor.isActive('codeBlock') }"
v-tooltip="$t('input.editor.code')"
>
<span class="icon">
<icon :icon="['fa', 'fa-code']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleBlockquote().run()"
:class="{ 'is-active': editor.isActive('blockquote') }"
v-tooltip="$t('input.editor.quote')"
>
<span class="icon">
<icon :icon="['fa', 'fa-quote-right']"/>
</span>
</BaseButton>
</div>
<div class="editor-toolbar__segment">
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleBulletList().run()"
:class="{ 'is-active': editor.isActive('bulletList') }"
v-tooltip="$t('input.editor.bulletList')"
>
<span class="icon">
<icon :icon="['fa', 'fa-list-ol']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleOrderedList().run()"
:class="{ 'is-active': editor.isActive('orderedList') }"
v-tooltip="$t('input.editor.orderedList')"
>
<span class="icon">
<icon :icon="['fa', 'fa-list-ul']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleTaskList().run()"
:class="{ 'is-active': editor.isActive('taskList') }"
v-tooltip="$t('input.editor.taskList')"
>
<span class="icon">
<icon icon="fa-list-check"/>
</span>
</BaseButton>
</div>
<div class="editor-toolbar__segment">
<BaseButton
class="editor-toolbar__button"
@click="openImagePicker"
v-tooltip="$t('input.editor.image')"
>
<span class="icon">
<icon icon="fa-image"/>
</span>
</BaseButton>
</div>
<div class="editor-toolbar__segment">
<BaseButton
class="editor-toolbar__button"
@click="setLink"
:class="{ 'is-active': editor.isActive('link') }"
title="set link"
v-tooltip="$t('input.editor.link')"
>
<span class="icon">
<icon :icon="['fa', 'fa-link']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().setParagraph().run()"
:class="{ 'is-active': editor.isActive('paragraph') }"
title="paragraph"
v-tooltip="$t('input.editor.text')"
>
<span class="icon">
<icon :icon="['fa', 'fa-paragraph']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().setHorizontalRule().run()"
v-tooltip="$t('input.editor.horizontalRule')"
>
<span class="icon">
<icon :icon="['fa', 'fa-ruler-horizontal']"/>
</span>
</BaseButton>
</div>
<div class="editor-toolbar__segment">
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().undo().run()"
v-tooltip="$t('input.editor.undo')"
>
<span class="icon">
<icon :icon="['fa', 'fa-undo']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().redo().run()"
v-tooltip="$t('input.editor.redo')"
>
<span class="icon">
<icon :icon="['fa', 'fa-redo']"/>
</span>
</BaseButton>
</div>
<div class="editor-toolbar__segment">
<!-- table -->
<BaseButton
class="editor-toolbar__button"
@click="toggleTableMode"
:class="{ 'is-active': editor.isActive('table') }"
v-tooltip="$t('input.editor.table.title')"
>
<span class="icon">
<icon :icon="['fa', 'fa-table']"/>
</span>
</BaseButton>
<div v-if="tableMode" class="editor-toolbar__table-buttons">
<BaseButton
class="editor-toolbar__button"
@click="
editor
.chain()
.focus()
.insertTable({ rows: 3, cols: 3, withHeaderRow: true })
.run()
"
>
{{ $t('input.editor.table.insert') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().addColumnBefore().run()"
:disabled="!editor.can().addColumnBefore"
>
{{ $t('input.editor.table.addColumnBefore') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().addColumnAfter().run()"
:disabled="!editor.can().addColumnAfter"
>
{{ $t('input.editor.table.addColumnAfter') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().deleteColumn().run()"
:disabled="!editor.can().deleteColumn"
>
{{ $t('input.editor.table.deleteColumn') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().addRowBefore().run()"
:disabled="!editor.can().addRowBefore"
>
{{ $t('input.editor.table.addRowBefore') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().addRowAfter().run()"
:disabled="!editor.can().addRowAfter"
>
{{ $t('input.editor.table.addRowAfter') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().deleteRow().run()"
:disabled="!editor.can().deleteRow"
>
{{ $t('input.editor.table.deleteRow') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().deleteTable().run()"
:disabled="!editor.can().deleteTable"
>
{{ $t('input.editor.table.deleteTable') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().mergeCells().run()"
:disabled="!editor.can().mergeCells"
>
{{ $t('input.editor.table.mergeCells') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().splitCell().run()"
:disabled="!editor.can().splitCell"
>
{{ $t('input.editor.table.splitCell') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleHeaderColumn().run()"
:disabled="!editor.can().toggleHeaderColumn"
>
{{ $t('input.editor.table.toggleHeaderColumn') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleHeaderRow().run()"
:disabled="!editor.can().toggleHeaderRow"
>
{{ $t('input.editor.table.toggleHeaderRow') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleHeaderCell().run()"
:disabled="!editor.can().toggleHeaderCell"
>
{{ $t('input.editor.table.toggleHeaderCell') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().mergeOrSplit().run()"
:disabled="!editor.can().mergeOrSplit"
>
{{ $t('input.editor.table.mergeOrSplit') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().fixTables().run()"
:disabled="!editor.can().fixTables"
>
{{ $t('input.editor.table.fixTables') }}
</BaseButton>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import {Editor} from '@tiptap/vue-3'
import BaseButton from '@/components/base/BaseButton.vue'
const {
editor = null,
} = defineProps<{
editor: Editor,
}>()
const tableMode = ref(false)
function toggleTableMode() {
tableMode.value = !tableMode.value
}
function openImagePicker() {
document.getElementById('tiptap__image-upload').click()
}
function setLink() {
const previousUrl = editor.getAttributes('link').href
const url = window.prompt('URL', previousUrl)
// cancelled
if (url === null) {
return
}
// empty
if (url === '') {
editor.chain().focus().extendMarkRange('link').unsetLink().run()
return
}
// update link
editor
.chain()
.focus()
.extendMarkRange('link')
.setLink({href: url, target: '_blank'})
.run()
}
</script>
<style lang="scss" scoped>
.editor-toolbar {
background: var(--white);
border: 1px solid var(--grey-200);
user-select: none;
padding: .5rem;
border-radius: $radius;
display: flex;
flex-wrap: wrap;
> * + * {
border-left: 1px solid var(--grey-200);
margin-left: 6px;
padding-left: 6px;
}
}
.editor-toolbar__button {
min-width: 2rem;
height: 2rem;
border-radius: $radius;
border: 1px solid transparent;
color: var(--grey-700);
transition: all $transition;
background: transparent;
margin-right: .25rem;
&:hover {
background: var(--grey-100);
border-color: var(--grey-200);
}
.icon {
position: relative;
.icon__lower-text {
font-size: .75rem;
position: absolute;
bottom: -3px;
right: -2px;
font-weight: bold;
}
}
}
.editor-toolbar__table-buttons {
margin-top: .5rem;
> .editor-toolbar__button {
margin-right: .5rem;
margin-bottom: .5rem;
padding: 0 .25rem;
border: 1px solid var(--grey-400);
font-size: .75rem;
height: 1.5rem;
}
}
</style>

View File

@ -0,0 +1,854 @@
<template>
<div class="tiptap">
<EditorToolbar
v-if="editor && isEditing"
:editor="editor"
:upload-callback="uploadCallback"
/>
<BubbleMenu
v-if="editor && isEditing"
:editor="editor"
class="editor-bubble__wrapper"
>
<BaseButton
class="editor-bubble__button"
@click="editor.chain().focus().toggleBold().run()"
:class="{ 'is-active': editor.isActive('bold') }"
v-tooltip="$t('input.editor.bold')"
>
<icon :icon="['fa', 'fa-bold']"/>
</BaseButton>
<BaseButton
class="editor-bubble__button"
@click="editor.chain().focus().toggleItalic().run()"
:class="{ 'is-active': editor.isActive('italic') }"
v-tooltip="$t('input.editor.italic')"
>
<icon :icon="['fa', 'fa-italic']"/>
</BaseButton>
<BaseButton
class="editor-bubble__button"
@click="editor.chain().focus().toggleUnderline().run()"
:class="{ 'is-active': editor.isActive('underline') }"
v-tooltip="$t('input.editor.underline')"
>
<icon :icon="['fa', 'fa-underline']"/>
</BaseButton>
<BaseButton
class="editor-bubble__button"
@click="editor.chain().focus().toggleStrike().run()"
:class="{ 'is-active': editor.isActive('strike') }"
v-tooltip="$t('input.editor.strikethrough')"
>
<icon :icon="['fa', 'fa-strikethrough']"/>
</BaseButton>
<BaseButton
class="editor-bubble__button"
@click="editor.chain().focus().toggleCode().run()"
:class="{ 'is-active': editor.isActive('code') }"
v-tooltip="$t('input.editor.code')"
>
<icon :icon="['fa', 'fa-code']"/>
</BaseButton>
<BaseButton
class="editor-bubble__button"
@click="setLink"
:class="{ 'is-active': editor.isActive('link') }"
v-tooltip="$t('input.editor.link')"
>
<icon :icon="['fa', 'fa-link']"/>
</BaseButton>
</BubbleMenu>
<editor-content
class="tiptap__editor"
:class="{'tiptap__editor-is-empty': isEmpty, 'tiptap__editor-is-edit-enabled': isEditing}"
:editor="editor"
/>
<input
v-if="isEditing"
type="file"
id="tiptap__image-upload"
class="is-hidden"
ref="uploadInputRef"
@change="addImage"
/>
<ul class="tiptap__editor-actions d-print-none" v-if="bottomActions.length === 0 && !isEditing">
<li>
<BaseButton
@click="setEdit"
class="done-edit">
{{ $t('input.editor.edit') }}
</BaseButton>
</li>
</ul>
<ul class="tiptap__editor-actions d-print-none" v-if="bottomActions.length > 0">
<li v-if="isEditing && showSave">
<BaseButton
@click="bubbleSave"
class="done-edit">
{{ $t('misc.save') }}
</BaseButton>
</li>
<li v-if="!isEditing">
<BaseButton
@click="setEdit"
class="done-edit">
{{ $t('input.editor.edit') }}
</BaseButton>
</li>
<li v-for="(action, k) in bottomActions" :key="k">
<BaseButton @click="action.action">{{ action.title }}</BaseButton>
</li>
</ul>
<x-button
v-else-if="isEditing && showSave"
class="mt-4"
@click="bubbleSave"
variant="secondary"
:shadow="false"
v-cy="'saveEditor'"
>
{{ $t('misc.save') }}
</x-button>
</div>
</template>
<script setup lang="ts">
import {ref, watch, onBeforeUnmount, nextTick, onMounted, computed} from 'vue'
import {refDebounced} from '@vueuse/core'
import EditorToolbar from './EditorToolbar.vue'
import Link from '@tiptap/extension-link'
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
import Table from '@tiptap/extension-table'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'
import TableRow from '@tiptap/extension-table-row'
import Typography from '@tiptap/extension-typography'
import Image from '@tiptap/extension-image'
import Underline from '@tiptap/extension-underline'
import TaskItem from '@tiptap/extension-task-item'
import TaskList from '@tiptap/extension-task-list'
import {Blockquote} from '@tiptap/extension-blockquote'
import {Bold} from '@tiptap/extension-bold'
import {BulletList} from '@tiptap/extension-bullet-list'
import {Code} from '@tiptap/extension-code'
import {Document} from '@tiptap/extension-document'
import {Dropcursor} from '@tiptap/extension-dropcursor'
import {Gapcursor} from '@tiptap/extension-gapcursor'
import {HardBreak} from '@tiptap/extension-hard-break'
import {Heading} from '@tiptap/extension-heading'
import {History} from '@tiptap/extension-history'
import {HorizontalRule} from '@tiptap/extension-horizontal-rule'
import {Italic} from '@tiptap/extension-italic'
import {ListItem} from '@tiptap/extension-list-item'
import {OrderedList} from '@tiptap/extension-ordered-list'
import {Paragraph} from '@tiptap/extension-paragraph'
import {Strike} from '@tiptap/extension-strike'
import {Text} from '@tiptap/extension-text'
import {BubbleMenu, EditorContent, useEditor} from '@tiptap/vue-3'
import {Node} from '@tiptap/pm/model'
import Commands from './commands'
import suggestionSetup from './suggestion'
import {lowlight} from 'lowlight'
import type {BottomAction, UploadCallback} from './types'
import type {ITask} from '@/modelTypes/ITask'
import type {IAttachment} from '@/modelTypes/IAttachment'
import AttachmentModel from '@/models/attachment'
import AttachmentService from '@/services/attachment'
import {useI18n} from 'vue-i18n'
import BaseButton from '@/components/base/BaseButton.vue'
import XButton from '@/components/input/button.vue'
import {Placeholder} from '@tiptap/extension-placeholder'
import {eventToHotkeyString} from '@github/hotkey'
import {mergeAttributes} from '@tiptap/core'
import {createRandomID} from '@/helpers/randomId'
const {t} = useI18n()
const CustomTableCell = TableCell.extend({
addAttributes() {
return {
// extend the existing attributes
...this.parent?.(),
// and add a new one
backgroundColor: {
default: null,
parseHTML: (element: HTMLElement) => element.getAttribute('data-background-color'),
renderHTML: (attributes) => {
return {
'data-background-color': attributes.backgroundColor,
style: `background-color: ${attributes.backgroundColor}`,
}
},
},
}
},
})
type CacheKey = `${ITask['id']}-${IAttachment['id']}`
const loadedAttachments = ref<{ [key: CacheKey]: string }>({})
const CustomImage = Image.extend({
renderHTML({HTMLAttributes}) {
if (HTMLAttributes.src?.startsWith(window.API_URL)) {
const id = 'tiptap-image-' + createRandomID()
nextTick(async () => {
const img = document.getElementById(id)
if (!img) return
// The url is something like /tasks/<id>/attachments/<id>
const parts = img.dataset?.src.slice(window.API_URL.length + 1).split('/')
const taskId = Number(parts[1])
const attachmentId = Number(parts[3])
const cacheKey: CacheKey = `${taskId}-${attachmentId}`
if (typeof loadedAttachments.value[cacheKey] === 'undefined') {
const attachment = new AttachmentModel({taskId: taskId, id: attachmentId})
const attachmentService = new AttachmentService()
const url = await attachmentService.getBlobUrl(attachment)
loadedAttachments.value[cacheKey] = url
}
img.src = loadedAttachments.value[cacheKey]
})
return ['img', mergeAttributes(this.options.HTMLAttributes, {
'data-src': HTMLAttributes.src,
src: '#',
alt: HTMLAttributes.alt,
title: HTMLAttributes.title,
id,
})]
}
return ['img', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]
},
})
type Mode = 'edit' | 'preview'
const {
modelValue,
uploadCallback,
isEditEnabled = true,
bottomActions = [],
showSave = false,
placeholder = '',
editShortcut = '',
initialMode = 'edit',
} = defineProps<{
modelValue: string,
uploadCallback?: UploadCallback,
isEditEnabled?: boolean,
bottomActions?: BottomAction[],
showSave?: boolean,
placeholder?: string,
editShortcut?: string,
initialMode?: Mode,
}>()
const emit = defineEmits(['update:modelValue', 'save'])
const inputHTML = ref('')
watch(
() => modelValue,
() => {
inputHTML.value = modelValue
},
{immediate: true},
)
const isEmpty = computed(() => inputHTML.value === '')
const internalMode = ref<Mode>(initialMode)
const isEditing = computed(() => internalMode.value === 'edit' && isEditEnabled)
function setEdit() {
internalMode.value = 'edit'
editor.value?.commands.focus()
}
const debouncedInputHTML = refDebounced(inputHTML, 1000)
watch(debouncedInputHTML, () => bubbleNow())
function bubbleNow() {
emit('update:modelValue', inputHTML.value)
}
function bubbleSave() {
bubbleNow()
emit('save', inputHTML.value)
if (initialMode === 'preview' && isEditing.value) {
internalMode.value = 'preview'
}
}
const editor = useEditor({
content: inputHTML.value,
editable: isEditing.value,
extensions: [
// Starterkit:
Blockquote,
Bold,
BulletList,
Code,
CodeBlockLowlight.configure({
lowlight,
}),
Document,
Dropcursor,
Gapcursor,
HardBreak.extend({
addKeyboardShortcuts() {
return {
'Mod-Enter': () => {
bubbleSave()
},
}
},
}),
Heading,
History,
HorizontalRule,
Italic,
ListItem,
OrderedList,
Paragraph,
Strike,
Text,
Placeholder.configure({
placeholder: ({editor}) => {
if (!isEditing.value) {
return ''
}
if (editor.getText() !== '' && !editor.isFocused) {
return ''
}
return placeholder !== ''
? placeholder
: t('input.editor.placeholder')
},
}),
Typography,
Underline,
Link.configure({
openOnClick: true,
validate: (href: string) => /^https?:\/\//.test(href),
}),
Table.configure({
resizable: true,
}),
TableRow,
TableHeader,
// Custom TableCell with backgroundColor attribute
CustomTableCell,
CustomImage,
TaskList,
TaskItem.configure({
nested: true,
onReadOnlyChecked: (node: Node, checked: boolean): boolean => {
if (isEditEnabled) {
node.attrs.checked = checked
inputHTML.value = editor.value?.getHTML()
bubbleSave()
return true
}
return false
},
}),
Commands.configure({
suggestion: suggestionSetup(t),
}),
BubbleMenu,
],
onUpdate: () => {
// HTML
inputHTML.value = editor.value!.getHTML()
// JSON
// this.$emit('update:modelValue', this.editor.getJSON())
},
})
watch(
() => isEditing.value,
() => {
editor.value?.setEditable(isEditing.value)
},
)
watch(inputHTML, (value) => {
if (!editor.value) return
// HTML
const isSame = editor.value.getHTML() === value
// JSON
// const isSame = JSON.stringify(editor.value.getJSON()) === JSON.stringify(value)
if (isSame) {
return
}
editor.value.commands.setContent(value, false)
})
onBeforeUnmount(() => editor.value?.destroy())
const uploadInputRef = ref<HTMLInputElement | null>(null)
function uploadAndInsertFiles(files: File[] | FileList) {
uploadCallback(files).then(urls => {
urls?.forEach(url => {
editor.value
?.chain()
.focus()
.setImage({src: url})
.run()
})
bubbleSave()
})
}
function addImage() {
if (typeof uploadCallback !== 'undefined') {
const files = uploadInputRef.value?.files
if (!files || files.length === 0) {
return
}
uploadAndInsertFiles(files)
return
}
const url = window.prompt('URL')
if (url) {
editor.value?.chain().focus().setImage({src: url}).run()
bubbleSave()
}
}
function setLink() {
const previousUrl = editor.value?.getAttributes('link').href
const url = window.prompt('URL', previousUrl)
// cancelled
if (url === null) {
return
}
// empty
if (url === '') {
editor.value
?.chain()
.focus()
.extendMarkRange('link')
.unsetLink()
.run()
return
}
// update link
editor.value
?.chain()
.focus()
.extendMarkRange('link')
.setLink({href: url, target: '_blank'})
.run()
}
onMounted(() => {
internalMode.value = initialMode
document.addEventListener('paste', handleImagePaste)
if (editShortcut !== '') {
document.addEventListener('keydown', setFocusToEditor)
}
})
onBeforeUnmount(() => {
document.removeEventListener('paste', handleImagePaste)
if (editShortcut !== '') {
document.removeEventListener('keydown', setFocusToEditor)
}
})
function handleImagePaste(event) {
event.preventDefault()
event?.clipboardData?.items?.forEach(i => {
if (i.kind === 'file' && i.type.startsWith('image/')) {
uploadAndInsertFiles([i.getAsFile()])
}
})
}
// See https://github.com/github/hotkey/discussions/85#discussioncomment-5214660
function setFocusToEditor(event) {
const hotkeyString = eventToHotkeyString(event)
if (!hotkeyString) return
if (hotkeyString !== editShortcut ||
event.target.tagName.toLowerCase() === 'input' ||
event.target.tagName.toLowerCase() === 'textarea' ||
event.target.contentEditable === 'true') {
return
}
event.preventDefault()
if (initialMode === 'preview' && isEditEnabled && !isEditing.value) {
internalMode.value = 'edit'
}
editor.value?.commands.focus()
}
</script>
<style lang="scss">
.tiptap__editor {
&.tiptap__editor-is-edit-enabled {
min-height: 10rem;
}
transition: box-shadow $transition;
border-radius: $radius;
&:focus-within, &:focus {
box-shadow: 0 0 0 2px hsla(var(--primary-hsl), 0.5);
}
}
.tiptap p.is-empty::before {
content: attr(data-placeholder);
color: var(--grey-400);
pointer-events: none;
height: 0;
float: left;
}
// Basic editor styles
.ProseMirror {
padding: .5rem;
&:focus-within, &:focus {
box-shadow: none;
}
> * + * {
margin-top: 0.75em;
}
ul,
ol {
padding: 0 1rem;
}
h1,
h2,
h3,
h4,
h5,
h6 {
line-height: 1.1;
}
a {
color: #68cef8;
}
code {
background-color: rgba(#616161, 0.1);
color: #616161;
}
pre {
background: #0d0d0d;
color: #fff;
font-family: 'JetBrainsMono', monospace;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
code {
color: inherit;
padding: 0;
background: none;
font-size: 0.8rem;
}
}
pre {
background: #0d0d0d;
color: #fff;
font-family: 'JetBrainsMono', monospace;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
code {
color: inherit;
padding: 0;
background: none;
font-size: 0.8rem;
}
.hljs-comment,
.hljs-quote {
color: #616161;
}
.hljs-variable,
.hljs-template-variable,
.hljs-attribute,
.hljs-tag,
.hljs-name,
.hljs-regexp,
.hljs-link,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #f98181;
}
.hljs-number,
.hljs-meta,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params {
color: #fbbc88;
}
.hljs-string,
.hljs-symbol,
.hljs-bullet {
color: #b9f18d;
}
.hljs-title,
.hljs-section {
color: #faf594;
}
.hljs-keyword,
.hljs-selector-tag {
color: #70cff8;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: 700;
}
}
img {
max-width: 100%;
height: auto;
&.ProseMirror-selectednode {
outline: 3px solid #68cef8;
}
}
blockquote {
padding-left: 1rem;
border-left: 2px solid rgba(#0d0d0d, 0.1);
}
hr {
border: none;
border-top: 2px solid rgba(#0d0d0d, 0.1);
margin: 2rem 0;
}
}
.ProseMirror {
/* Table-specific styling */
table {
border-collapse: collapse;
table-layout: fixed;
width: 100%;
margin: 0;
overflow: hidden;
td,
th {
min-width: 1em;
border: 2px solid #ced4da;
padding: 3px 5px;
vertical-align: top;
box-sizing: border-box;
position: relative;
> * {
margin-bottom: 0;
}
}
th {
font-weight: bold;
text-align: left;
background-color: #f1f3f5;
}
.selectedCell:after {
z-index: 2;
position: absolute;
content: '';
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(200, 200, 255, 0.4);
pointer-events: none;
}
.column-resize-handle {
position: absolute;
right: -2px;
top: 0;
bottom: -2px;
width: 4px;
background-color: #adf;
pointer-events: none;
}
p {
margin: 0;
}
}
// Lists
ul {
margin-left: .5rem;
margin-top: 0 !important;
li {
margin-top: 0;
}
p {
margin-bottom: 0 !important;
}
}
}
.tableWrapper {
overflow-x: auto;
}
.resize-cursor {
cursor: ew-resize;
cursor: col-resize;
}
// tasklist
ul[data-type='taskList'] {
list-style: none;
padding: 0;
margin-left: 0;
li {
display: flex;
> label {
flex: 0 0 auto;
margin-right: 0.5rem;
user-select: none;
}
> div {
flex: 1 1 auto;
}
}
input[type='checkbox'] {
cursor: pointer;
}
}
.editor-bubble__wrapper {
background: var(--white);
border-radius: $radius;
border: 1px solid var(--grey-200);
box-shadow: var(--shadow-md);
display: flex;
overflow: hidden;
}
.editor-bubble__button {
color: var(--grey-700);
transition: all $transition;
background: transparent;
svg {
box-sizing: border-box;
display: block;
width: 1rem;
height: 1rem;
padding: .5rem;
margin: 0;
}
&:hover {
background: var(--grey-200);
}
}
ul.tiptap__editor-actions {
font-size: .8rem;
margin: 0;
li {
display: inline-block;
&::after {
content: '·';
padding: 0 .25rem;
}
&:last-child:after {
content: '';
}
}
&, a {
color: var(--grey-500);
&.done-edit {
color: var(--primary);
}
}
a:hover {
text-decoration: underline;
}
}
</style>

View File

@ -0,0 +1,28 @@
import {Extension} from '@tiptap/core'
import Suggestion from '@tiptap/suggestion'
// Copied and adjusted from https://github.com/ueberdosis/tiptap/tree/252acb32d27a0f9af14813eeed83d8a50059a43a/demos/src/Experiments/Commands/Vue
export default Extension.create({
name: 'slash-menu-commands',
addOptions() {
return {
suggestion: {
char: '/',
command: ({editor, range, props}) => {
props.command({editor, range})
},
},
}
},
addProseMirrorPlugins() {
return [
Suggestion({
editor: this.editor,
...this.options.suggestion,
}),
]
},
})

View File

@ -0,0 +1,214 @@
import {VueRenderer} from '@tiptap/vue-3'
import tippy from 'tippy.js'
import CommandsList from './CommandsList.vue'
export default function suggestionSetup(t) {
return {
items: ({query}: { query: string }) => {
return [
{
title: t('input.editor.text'),
description: t('input.editor.textTooltip'),
icon: 'fa-font',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.setNode('paragraph', {level: 1})
.run()
},
},
{
title: t('input.editor.heading1'),
description: t('input.editor.heading1Tooltip'),
icon: 'fa-header',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.setNode('heading', {level: 1})
.run()
},
},
{
title: t('input.editor.heading2'),
description: t('input.editor.heading2Tooltip'),
icon: 'fa-header',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.setNode('heading', {level: 2})
.run()
},
},
{
title: t('input.editor.heading3'),
description: t('input.editor.heading3Tooltip'),
icon: 'fa-header',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.setNode('heading', {level: 2})
.run()
},
},
{
title: t('input.editor.bulletList'),
description: t('input.editor.bulletListTooltip'),
icon: 'fa-list-ul',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.toggleBulletList()
.run()
},
},
{
title: t('input.editor.orderedList'),
description: t('input.editor.orderedListTooltip'),
icon: 'fa-list-ol',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.toggleOrderedList()
.run()
},
},
{
title: t('input.editor.taskList'),
description: t('input.editor.taskListTooltip'),
icon: 'fa-list-check',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.toggleTaskList()
.run()
},
},
{
title: t('input.editor.quote'),
description: t('input.editor.quoteTooltip'),
icon: 'fa-quote-right',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.toggleBlockquote()
.run()
},
},
{
title: t('input.editor.code'),
description: t('input.editor.codeTooltip'),
icon: 'fa-code',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.toggleCodeBlock()
.run()
},
},
{
title: t('input.editor.image'),
description: t('input.editor.imageTooltip'),
icon: 'fa-image',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.run()
document.getElementById('tiptap__image-upload').click()
},
},
{
title: t('input.editor.horizontalRule'),
description: t('input.editor.horizontalRuleTooltip'),
icon: 'fa-ruler-horizontal',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.setHorizontalRule()
.run()
},
},
].filter(item => item.title.toLowerCase().startsWith(query.toLowerCase()))
},
render: () => {
let component: VueRenderer
let popup
return {
onStart: props => {
component = new VueRenderer(CommandsList, {
// using vue 2:
// parent: this,
// propsData: props,
props,
editor: props.editor,
})
if (!props.clientRect) {
return
}
popup = tippy('body', {
getReferenceClientRect: props.clientRect,
appendTo: () => document.body,
content: component.element,
showOnCreate: true,
interactive: true,
trigger: 'manual',
placement: 'bottom-start',
})
},
onUpdate(props) {
component.updateProps(props)
if (!props.clientRect) {
return
}
popup[0].setProps({
getReferenceClientRect: props.clientRect,
})
},
onKeyDown(props) {
if (props.event.key === 'Escape') {
popup[0].hide()
return true
}
return component.ref?.onKeyDown(props)
},
onExit() {
popup[0].destroy()
component.destroy()
},
}
},
}
}

View File

@ -0,0 +1,6 @@
export type UploadCallback = (files: File[] | FileList) => Promise<string[]>
export interface BottomAction {
title: string
action: () => void,
}

View File

@ -1,135 +0,0 @@
import EasyMDE from 'easymde'
import {i18n} from '@/i18n'
export function createEasyMDEConfig({ placeholder, uploadImage, imageUploadFunction }) {
return {
autoDownloadFontAwesome: false,
spellChecker: false,
placeholder,
uploadImage,
imageUploadFunction,
minHeight: '150px',
sideBySideFullscreen: false,
toolbar: [
{
name: 'heading-1',
action: EasyMDE.toggleHeading1,
title: i18n.global.t('input.editor.heading1'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M18.2773 19.25L12.5773 4.34995C12.5773 4.34995 12.5773 4.24995 12.4773 4.24995C12.4773 4.24995 12.4773 4.14995 12.3773 4.14995C12.3773 4.14995 12.2773 4.14995 12.2773 4.04995L12.1773 3.94995H12.0773H11.9773C11.8773 3.94995 11.8773 3.94995 11.8773 3.94995H11.7773C11.6773 4.04995 11.6773 4.14995 11.5773 4.14995C11.5773 4.14995 11.5773 4.14995 11.4773 4.14995C11.4773 4.14995 11.4773 4.24995 11.3773 4.24995L11.2773 4.34995L5.67733 19.25C5.57733 19.55 5.67733 19.95 5.97733 20.05C6.07733 20.05 6.07733 20.05 6.17733 20.05C6.37733 20.05 6.67733 19.95 6.77733 19.65L7.87733 16.85H16.1773L17.2773 19.65C17.3773 19.85 17.5773 20.05 17.8773 20.05C17.9773 20.05 17.9773 20.05 18.0773 20.05C18.2773 19.85 18.4773 19.55 18.2773 19.25ZM8.27733 15.65L11.9773 6.24995L15.6773 15.65H8.27733Z"/></svg>',
},
{
name: 'heading-2',
action: EasyMDE.toggleHeading2,
title: i18n.global.t('input.editor.heading2'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M18.2773 19.25L12.5773 4.34995C12.5773 4.34995 12.5773 4.24995 12.4773 4.24995C12.4773 4.24995 12.4773 4.14995 12.3773 4.14995C12.3773 4.14995 12.2773 4.14995 12.2773 4.04995L12.1773 3.94995H12.0773H11.9773C11.8773 3.94995 11.8773 3.94995 11.8773 3.94995H11.7773C11.6773 4.04995 11.6773 4.14995 11.5773 4.14995C11.5773 4.14995 11.5773 4.14995 11.4773 4.14995C11.4773 4.14995 11.4773 4.24995 11.3773 4.24995L11.2773 4.34995L5.67733 19.25C5.57733 19.55 5.67733 19.95 5.97733 20.05C6.07733 20.05 6.07733 20.05 6.17733 20.05C6.37733 20.05 6.67733 19.95 6.77733 19.65L7.87733 16.85H16.1773L17.2773 19.65C17.3773 19.85 17.5773 20.05 17.8773 20.05C17.9773 20.05 17.9773 20.05 18.0773 20.05C18.2773 19.85 18.4773 19.55 18.2773 19.25ZM8.27733 15.65L11.9773 6.24995L15.6773 15.65H8.27733Z"/></svg>',
},
{
name: 'heading-3',
action: EasyMDE.toggleHeading3,
title: i18n.global.t('input.editor.heading3'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M18.2773 19.25L12.5773 4.34995C12.5773 4.34995 12.5773 4.24995 12.4773 4.24995C12.4773 4.24995 12.4773 4.14995 12.3773 4.14995C12.3773 4.14995 12.2773 4.14995 12.2773 4.04995L12.1773 3.94995H12.0773H11.9773C11.8773 3.94995 11.8773 3.94995 11.8773 3.94995H11.7773C11.6773 4.04995 11.6773 4.14995 11.5773 4.14995C11.5773 4.14995 11.5773 4.14995 11.4773 4.14995C11.4773 4.14995 11.4773 4.24995 11.3773 4.24995L11.2773 4.34995L5.67733 19.25C5.57733 19.55 5.67733 19.95 5.97733 20.05C6.07733 20.05 6.07733 20.05 6.17733 20.05C6.37733 20.05 6.67733 19.95 6.77733 19.65L7.87733 16.85H16.1773L17.2773 19.65C17.3773 19.85 17.5773 20.05 17.8773 20.05C17.9773 20.05 17.9773 20.05 18.0773 20.05C18.2773 19.85 18.4773 19.55 18.2773 19.25ZM8.27733 15.65L11.9773 6.24995L15.6773 15.65H8.27733Z"/></svg>',
},
{
name: 'heading-smaller',
action: EasyMDE.toggleHeadingSmaller,
title: i18n.global.t('input.editor.headingSmaller'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M18.2773 19.25L12.5773 4.34995C12.5773 4.34995 12.5773 4.24995 12.4773 4.24995C12.4773 4.24995 12.4773 4.14995 12.3773 4.14995C12.3773 4.14995 12.2773 4.14995 12.2773 4.04995L12.1773 3.94995H12.0773H11.9773C11.8773 3.94995 11.8773 3.94995 11.8773 3.94995H11.7773C11.6773 4.04995 11.6773 4.14995 11.5773 4.14995C11.5773 4.14995 11.5773 4.14995 11.4773 4.14995C11.4773 4.14995 11.4773 4.24995 11.3773 4.24995L11.2773 4.34995L5.67733 19.25C5.57733 19.55 5.67733 19.95 5.97733 20.05C6.07733 20.05 6.07733 20.05 6.17733 20.05C6.37733 20.05 6.67733 19.95 6.77733 19.65L7.87733 16.85H16.1773L17.2773 19.65C17.3773 19.85 17.5773 20.05 17.8773 20.05C17.9773 20.05 17.9773 20.05 18.0773 20.05C18.2773 19.85 18.4773 19.55 18.2773 19.25ZM8.27733 15.65L11.9773 6.24995L15.6773 15.65H8.27733Z"/></svg>',
},
{
name: 'heading-bigger',
action: EasyMDE.toggleHeadingBigger,
title: i18n.global.t('input.editor.headingBigger'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M18.2773 19.25L12.5773 4.34995C12.5773 4.34995 12.5773 4.24995 12.4773 4.24995C12.4773 4.24995 12.4773 4.14995 12.3773 4.14995C12.3773 4.14995 12.2773 4.14995 12.2773 4.04995L12.1773 3.94995H12.0773H11.9773C11.8773 3.94995 11.8773 3.94995 11.8773 3.94995H11.7773C11.6773 4.04995 11.6773 4.14995 11.5773 4.14995C11.5773 4.14995 11.5773 4.14995 11.4773 4.14995C11.4773 4.14995 11.4773 4.24995 11.3773 4.24995L11.2773 4.34995L5.67733 19.25C5.57733 19.55 5.67733 19.95 5.97733 20.05C6.07733 20.05 6.07733 20.05 6.17733 20.05C6.37733 20.05 6.67733 19.95 6.77733 19.65L7.87733 16.85H16.1773L17.2773 19.65C17.3773 19.85 17.5773 20.05 17.8773 20.05C17.9773 20.05 17.9773 20.05 18.0773 20.05C18.2773 19.85 18.4773 19.55 18.2773 19.25ZM8.27733 15.65L11.9773 6.24995L15.6773 15.65H8.27733Z"/></svg>',
},
'|',
{
name: 'bold',
action: EasyMDE.toggleBold,
title: i18n.global.t('input.editor.bold'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M3.5 3H6.5H15.25C18.15 3 20.5 5.36 20.5 8.25C20.5 9.8 19.81 11.19 18.73 12.15C20.37 13.04 21.5 14.76 21.5 16.75C21.5 19.64 19.15 22 16.25 22H6.5H3.5C2.95 22 2.5 21.55 2.5 21C2.5 20.45 2.95 20 3.5 20H5.5V5H3.5C2.95 5 2.5 4.55 2.5 4C2.5 3.45 2.95 3 3.5 3ZM7.5 20H16.25C18.04 20 19.5 18.54 19.5 16.75C19.5 14.96 18.04 13.5 16.25 13.5H7.5V20ZM7.5 11.5H15.25C17.04 11.5 18.5 10.04 18.5 8.25C18.5 6.46 17.04 5 15.25 5H7.5V11.5Z"/></svg>',
},
{
name: 'italic',
action: EasyMDE.toggleItalic,
title: i18n.global.t('input.editor.italic'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M14.0967 4.2H17.0001C17.3301 4.2 17.6001 3.93 17.6001 3.6C17.6001 3.27 17.3301 3 17.0001 3H10.2001C9.8701 3 9.6001 3.27 9.6001 3.6C9.6001 3.93 9.8701 4.2 10.2001 4.2H12.8748L9.90335 19.8H6.9999C6.6699 19.8 6.3999 20.07 6.3999 20.4C6.3999 20.73 6.6699 21 6.9999 21H13.7999C14.1299 21 14.3999 20.73 14.3999 20.4C14.3999 20.07 14.1299 19.8 13.7999 19.8H11.1253L14.0967 4.2Z"/></svg>',
},
{
name: 'strikethrough',
action: EasyMDE.toggleStrikethrough,
title: i18n.global.t('input.editor.strikethrough'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M18.25 7.17005C18.25 7.50005 17.98 7.77005 17.65 7.77005C17.32 7.77005 17.05 7.50005 17.05 7.17005V5.96005C15.97 5.12005 14.17 4.56005 12.79 4.31005C11.1 4.00005 9.51 4.30005 8.41 5.12005C7.2 6.03005 6.67 7.67005 7.19 8.88005C7.56 9.73005 8.37 10.31 8.98 10.64C9.57215 10.9644 10.1961 11.2013 10.8465 11.3999H20.4C20.73 11.3999 21 11.6699 21 11.9999C21 12.3299 20.73 12.5999 20.4 12.5999H15.3012C16.6583 13.0929 17.5255 13.7765 17.95 14.69C18.73 16.36 17.74 18.33 16.36 19.41C15.05 20.4401 13.35 21 11.54 21H11.16C9.78 20.9401 8.34 20.5301 6.95 19.85V20.3601C6.95 20.6901 6.68 20.96 6.35 20.96C6.02 20.96 5.75 20.6901 5.75 20.3601V17.36C5.75 17.03 6.02 16.76 6.35 16.76C6.68 16.76 6.95 17.03 6.95 17.36V18.5C8.35 19.2801 9.81 19.74 11.21 19.8C12.86 19.89 14.46 19.39 15.62 18.48C16.6 17.71 17.37 16.3 16.86 15.21C16.55 14.54 15.8 14.0201 14.58 13.63C13.9711 13.4331 13.3222 13.2762 12.6906 13.1235C12.6168 13.1056 12.5432 13.0878 12.47 13.07C12.4313 13.0607 12.3925 13.0514 12.3537 13.0421C11.7861 12.9055 11.2108 12.767 10.6413 12.5999H3.6C3.27 12.5999 3 12.3299 3 11.9999C3 11.6699 3.27 11.3999 3.6 11.3999H7.90288C7.04984 10.8343 6.42752 10.1363 6.09 9.36005C5.34 7.63005 6.03 5.40005 7.69 4.16005C9.05 3.15005 10.99 2.77005 13 3.13005C13.64 3.25005 15.53 3.66005 17.05 4.53005V4.17005C17.05 3.84005 17.32 3.57005 17.65 3.57005C17.98 3.57005 18.25 3.84005 18.25 4.17005V7.17005Z"/></svg>',
},
{
name: 'code',
action: EasyMDE.toggleCodeBlock,
title: i18n.global.t('input.editor.code'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M8.57 20.9601C8.64 20.9901 8.71 21.0001 8.78 21.0001C9.02 21.0001 9.24 20.8501 9.34 20.6101L15.79 3.81005C15.9 3.50005 15.75 3.15005 15.44 3.03005C15.14 2.92005 14.79 3.07005 14.67 3.38005L8.22 20.1801C8.11 20.4901 8.26 20.8401 8.57 20.9601ZM7.00007 18.0001C6.85007 18.0001 6.69007 17.9401 6.58007 17.8201L1.18007 12.4201C0.950068 12.1901 0.950068 11.8101 1.18007 11.5701L6.58007 6.17006C6.81007 5.94006 7.19007 5.94006 7.43007 6.17006C7.66007 6.40006 7.66007 6.78006 7.43007 7.02006L2.45007 12.0001L7.43007 16.9801C7.66007 17.2101 7.66007 17.5901 7.43007 17.8301C7.31007 17.9401 7.15007 18.0001 7.00007 18.0001ZM17 18.0001C16.85 18.0001 16.69 17.9401 16.58 17.8201C16.35 17.5901 16.35 17.2101 16.58 16.9701L21.55 12.0001L16.57 7.02006C16.34 6.79006 16.34 6.41006 16.57 6.17006C16.81 5.94006 17.19 5.94006 17.42 6.17006L22.82 11.5701C23.05 11.8001 23.05 12.1801 22.82 12.4201L17.42 17.8201C17.31 17.9401 17.15 18.0001 17 18.0001Z"/></svg>',
},
{
name: 'quote',
action: EasyMDE.toggleBlockquote,
title: i18n.global.t('input.editor.quote'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M19.373 5.16357H5.62695C4.79102 5.16357 4.11133 5.84326 4.11133 6.6792V16.2095C4.11133 17.0464 4.79102 17.7261 5.62695 17.7261H6.8877V21.1245C6.8877 21.3667 7.0332 21.5854 7.25684 21.6782C7.33203 21.7095 7.41016 21.7241 7.4873 21.7241C7.64258 21.7241 7.7959 21.6636 7.91113 21.5493L11.748 17.7261H19.373C20.209 17.7261 20.8887 17.0464 20.8887 16.2095V6.6792C20.8887 5.84326 20.209 5.16357 19.373 5.16357ZM19.6895 16.2095C19.6895 16.3843 19.5469 16.5269 19.373 16.5269H11.5C11.3408 16.5269 11.1895 16.5894 11.0762 16.7017L8.08691 19.6802V17.1265C8.08691 16.7954 7.81836 16.5269 7.4873 16.5269H5.62695C5.45312 16.5269 5.31055 16.3843 5.31055 16.2095V6.6792C5.31055 6.50537 5.45312 6.36279 5.62695 6.36279H19.373C19.5469 6.36279 19.6895 6.50537 19.6895 6.6792V16.2095ZM10.3431 8.45264C9.46326 8.45264 8.75 9.16589 8.75 10.0458C8.75 10.9257 9.46326 11.639 10.3431 11.639C10.4775 11.639 10.6058 11.6173 10.7305 11.5861V11.6195C10.7305 12.1322 10.3135 12.5492 9.75586 12.5492C9.4248 12.5492 9.17871 12.8177 9.17871 13.1488C9.17871 13.4799 9.46973 13.7484 9.80078 13.7484C10.9746 13.7484 11.9297 12.7933 11.9297 11.6195V10.1176L11.9294 10.1165L11.9292 10.1155C11.9297 10.1049 11.9312 10.0946 11.9326 10.0843L11.9326 10.0843C11.9345 10.0716 11.9363 10.059 11.9363 10.0458C11.9363 9.16589 11.223 8.45264 10.3431 8.45264ZM13.0637 10.0458C13.0637 9.16589 13.7771 8.45264 14.657 8.45264C15.5369 8.45264 16.2501 9.16589 16.2501 10.0458C16.2501 10.0584 16.2484 10.0706 16.2466 10.0828C16.2452 10.0929 16.2437 10.103 16.2433 10.1134C16.2433 10.1149 16.2441 10.1161 16.2441 10.1176V11.6195C16.2441 12.7933 15.2891 13.7484 14.1152 13.7484C13.7842 13.7484 13.4922 13.4799 13.4922 13.1488C13.4922 12.8177 13.7383 12.5492 14.0693 12.5492C14.6279 12.5492 15.0449 12.1322 15.0449 11.6195V11.5858C14.9202 11.6173 14.7915 11.639 14.657 11.639C13.7771 11.639 13.0637 10.9257 13.0637 10.0458Z"/></svg>',
},
{
name: 'unordered-list',
action: EasyMDE.toggleUnorderedList,
title: i18n.global.t('input.editor.unorderedList'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M6.5281 3.7002H3.5281C3.1981 3.7002 2.9281 3.9702 2.9281 4.3002V7.3002C2.9281 7.6302 3.1981 7.9002 3.5281 7.9002H6.5281C6.8581 7.9002 7.1281 7.6302 7.1281 7.3002V4.3002C7.1281 3.9702 6.8581 3.7002 6.5281 3.7002ZM5.9281 6.7002H4.1281V4.9002H5.9281V6.7002ZM3.5281 9.90015H6.5281C6.8581 9.90015 7.1281 10.1701 7.1281 10.5001V13.5001C7.1281 13.8301 6.8581 14.1001 6.5281 14.1001H3.5281C3.1981 14.1001 2.9281 13.8301 2.9281 13.5001V10.5001C2.9281 10.1701 3.1981 9.90015 3.5281 9.90015ZM4.1281 12.9001H5.9281V11.1001H4.1281V12.9001ZM3.5281 16.1001H6.5281C6.8581 16.1001 7.1281 16.3701 7.1281 16.7001V19.7001C7.1281 20.0301 6.8581 20.3001 6.5281 20.3001H3.5281C3.1981 20.3001 2.9281 20.0301 2.9281 19.7001V16.7001C2.9281 16.3701 3.1981 16.1001 3.5281 16.1001ZM4.1281 19.1001H5.9281V17.3001H4.1281V19.1001ZM9.72817 6.4002H20.7282C21.0582 6.4002 21.3282 6.1302 21.3282 5.8002C21.3282 5.4702 21.0582 5.2002 20.7282 5.2002H9.72817C9.39817 5.2002 9.12817 5.4702 9.12817 5.8002C9.12817 6.1302 9.39817 6.4002 9.72817 6.4002ZM9.72817 11.4001H20.7282C21.0582 11.4001 21.3282 11.6701 21.3282 12.0001C21.3282 12.3301 21.0582 12.6001 20.7282 12.6001H9.72817C9.39817 12.6001 9.12817 12.3301 9.12817 12.0001C9.12817 11.6701 9.39817 11.4001 9.72817 11.4001ZM9.72817 17.6001H20.7282C21.0582 17.6001 21.3282 17.8701 21.3282 18.2001C21.3282 18.5301 21.0582 18.8001 20.7282 18.8001H9.72817C9.39817 18.8001 9.12817 18.5301 9.12817 18.2001C9.12817 17.8701 9.39817 17.6001 9.72817 17.6001Z"/></svg>',
},
{
name: 'ordered-list',
action: EasyMDE.toggleOrderedList,
title: i18n.global.t('input.editor.orderedList'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M4.19494 8.29994H5.99494C6.26494 8.29994 6.49494 8.07995 6.49494 7.79994C6.49494 7.51995 6.27494 7.29994 5.99494 7.29994H5.59494V3.79994C5.59494 3.62994 5.50494 3.46994 5.36494 3.37994C5.22494 3.28994 5.04494 3.26994 4.89494 3.33994L3.89494 3.76994C3.64494 3.87994 3.52494 4.17994 3.63494 4.42994C3.74494 4.67994 4.03494 4.79994 4.29494 4.68994L4.59494 4.55994V7.29994H4.19494C3.91494 7.29994 3.69494 7.51995 3.69494 7.79994C3.69494 8.07995 3.91494 8.29994 4.19494 8.29994ZM20.195 6.39995H9.19497C8.86497 6.39995 8.59497 6.12995 8.59497 5.79995C8.59497 5.46995 8.86497 5.19995 9.19497 5.19995H20.195C20.525 5.19995 20.795 5.46995 20.795 5.79995C20.795 6.12995 20.525 6.39995 20.195 6.39995ZM3.78486 14.36H6.37486C6.65486 14.36 6.87486 14.14 6.87486 13.86C6.87486 13.58 6.65486 13.36 6.37486 13.36H4.88486C5.00486 13.23 5.12486 13.09 5.23486 12.95C5.26626 12.9151 5.29645 12.8802 5.32626 12.8458L5.32629 12.8457C5.38192 12.7814 5.43627 12.7186 5.49486 12.66C5.73486 12.4 5.98486 12.12 6.17486 11.79C6.47486 11.25 6.41486 10.63 6.01486 10.17C5.57486 9.66 4.86486 9.5 4.24486 9.74C3.74486 9.95 3.39486 10.35 3.22486 10.91C3.14486 11.18 3.29486 11.46 3.56486 11.54C3.82486 11.61 4.10486 11.46 4.18486 11.2C4.29486 10.85 4.48486 10.73 4.62486 10.67C4.88486 10.57 5.13486 10.68 5.26486 10.82C5.38486 10.96 5.40486 11.12 5.30486 11.29C5.17595 11.5202 4.99618 11.7165 4.80458 11.9257L4.75486 11.98C4.67298 12.0801 4.58283 12.1801 4.49946 12.2727L4.49945 12.2727L4.47486 12.3C4.12486 12.72 3.76486 13.13 3.40486 13.53C3.27486 13.68 3.23486 13.9 3.32486 14.07C3.41486 14.24 3.58486 14.36 3.78486 14.36ZM3.68486 20.3699C4.04486 20.5899 4.46486 20.6999 4.87486 20.6999C5.13486 20.6999 5.38486 20.6499 5.61486 20.5499C6.31486 20.2799 6.73486 19.5599 6.60486 18.8799C6.53486 18.5499 6.35486 18.2899 6.12486 18.0899C6.32486 17.8999 6.45486 17.6499 6.50486 17.3799C6.57486 17.0099 6.49486 16.6299 6.27486 16.3099C5.85486 15.6899 5.07486 15.5199 4.10486 15.8299C3.83486 15.9199 3.69486 16.1999 3.77486 16.4599C3.86486 16.7299 4.14486 16.8699 4.40486 16.7899C4.70486 16.6999 5.24486 16.5799 5.45486 16.8899C5.51486 16.9899 5.54486 17.0999 5.52486 17.1999C5.51486 17.2699 5.47486 17.3599 5.36486 17.4299C5.26486 17.4999 5.12486 17.5399 4.95486 17.5799L4.77486 17.6299C4.54486 17.6999 4.40486 17.9099 4.41486 18.1499C4.42486 18.3899 4.61486 18.5799 4.84486 18.6099C5.20486 18.6599 5.58486 18.8299 5.63486 19.0799C5.67486 19.2999 5.46486 19.5499 5.25486 19.6299C4.94486 19.7599 4.52486 19.7099 4.21486 19.5199C3.97486 19.3699 3.67486 19.4399 3.52486 19.6799C3.37486 19.9199 3.44486 20.2299 3.68486 20.3699ZM20.195 18.7999H9.19497C8.86497 18.7999 8.59497 18.5299 8.59497 18.1999C8.59497 17.8699 8.86497 17.5999 9.19497 17.5999H20.195C20.525 17.5999 20.795 17.8699 20.795 18.1999C20.795 18.5299 20.525 18.7999 20.195 18.7999ZM9.19497 12.5999H20.195C20.525 12.5999 20.795 12.3299 20.795 11.9999C20.795 11.6699 20.525 11.3999 20.195 11.3999H9.19497C8.86497 11.3999 8.59497 11.6699 8.59497 11.9999C8.59497 12.3299 8.86497 12.5999 9.19497 12.5999Z"/></svg>',
},
'|',
{
name: 'clean-block',
action: EasyMDE.cleanBlock,
title: i18n.global.t('input.editor.cleanBlock'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M9.25989 6.18384H20.4513C20.7823 6.18384 21.0509 6.45239 21.0509 6.78345V17.9749C21.0509 18.3059 20.7823 18.5745 20.4513 18.5745H9.25989C9.0929 18.5745 8.93469 18.5061 8.82043 18.384L3.6095 12.7883C3.39563 12.5579 3.39563 12.2004 3.6095 11.97L8.82043 6.37427C8.93469 6.2522 9.0929 6.18384 9.25989 6.18384ZM9.52063 17.3752H19.8517V7.38306H9.52063L4.86926 12.3792L9.52063 17.3752ZM12.7755 15.0686C12.6222 15.0686 12.4679 15.01 12.3517 14.8928C12.1173 14.6584 12.1173 14.2786 12.3517 14.0452L14.0503 12.3469L12.3517 10.6487C12.1173 10.4153 12.1173 10.0354 12.3517 9.80103C12.5841 9.56665 12.965 9.56665 13.1993 9.80103L14.8981 11.4994L16.5968 9.80103C16.8312 9.56665 17.212 9.56665 17.4445 9.80103C17.6788 10.0354 17.6788 10.4153 17.4445 10.6487L15.7458 12.3469L17.4445 14.0452C17.6788 14.2786 17.6788 14.6584 17.4445 14.8928C17.3282 15.01 17.174 15.0686 17.0206 15.0686C16.8673 15.0686 16.714 15.01 16.5968 14.8928L14.8981 13.1945L13.1993 14.8928C13.0822 15.01 12.9288 15.0686 12.7755 15.0686Z"/></svg>',
},
{
name: 'link',
action: EasyMDE.drawLink,
title: i18n.global.t('input.editor.link'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M11.4399 15.3452C11.4999 15.3652 11.5699 15.3752 11.6299 15.3752C11.8799 15.3752 12.1199 15.2152 12.1999 14.9652C12.2999 14.6452 12.1299 14.3052 11.8199 14.2052C11.3499 14.0452 10.9299 13.7852 10.5699 13.4152C10.1999 13.0552 9.9399 12.6452 9.7799 12.1552C9.6599 11.8252 9.5999 11.4652 9.5999 11.0952C9.5999 10.2152 9.9399 9.38518 10.5699 8.75518L15.1599 4.15518C16.4499 2.87518 18.5399 2.87518 19.8299 4.15518C20.4499 4.78518 20.7899 5.61518 20.7899 6.49518C20.7899 7.37518 20.4499 8.20518 19.8299 8.82518L16.7399 11.9052C16.5099 12.1452 16.5099 12.5252 16.7399 12.7552C16.9799 12.9852 17.3599 12.9852 17.5899 12.7552L20.6799 9.67518C21.5299 8.83518 21.9999 7.69518 21.9999 6.49518C21.9999 5.29518 21.5299 4.16518 20.6899 3.30518C18.9299 1.55518 16.0799 1.55518 14.3199 3.30518L9.7299 7.90518C8.8699 8.75518 8.3999 9.88518 8.3999 11.0952C8.3999 11.6152 8.4899 12.1152 8.6499 12.5552C8.8599 13.1952 9.2399 13.7952 9.7199 14.2652C10.1999 14.7552 10.7999 15.1352 11.4399 15.3452ZM3.32 20.6851C4.2 21.5551 5.35 21.9951 6.5 21.9951C7.65 21.9951 8.81 21.5551 9.69 20.7051L14.28 16.1051C15.14 15.2551 15.61 14.1251 15.61 12.9151C15.61 12.4551 15.54 11.9951 15.4 11.5551C15.17 10.8651 14.8 10.2551 14.28 9.73509C13.76 9.21509 13.15 8.84509 12.46 8.61509C12.14 8.51509 11.8 8.68509 11.7 8.99509C11.6 9.30509 11.77 9.64509 12.1 9.75509C12.61 9.91509 13.06 10.1951 13.44 10.5751C13.82 10.9551 14.09 11.4051 14.26 11.9151C14.36 12.2351 14.41 12.5651 14.41 12.9051C14.41 13.7951 14.06 14.6251 13.43 15.2451L8.84 19.8451C7.55 21.1251 5.46 21.1251 4.17 19.8451C3.55 19.2151 3.21 18.3951 3.21 17.5051C3.21 16.6151 3.55 15.7851 4.17 15.1651L7.35 11.9851C7.58 11.7451 7.59 11.3651 7.35 11.1351C7.11 10.9051 6.73 10.9051 6.5 11.1351L3.32 14.3151C2.47 15.1551 2 16.2851 2 17.4951C2 18.7051 2.47 19.8351 3.32 20.6851Z"/></svg>',
},
{
name: 'image',
action: EasyMDE.drawImage,
title: i18n.global.t('input.editor.image'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M4 4C2.89543 4 2 4.89543 2 6V16V17.5152V18C2 19.1046 2.89543 20 4 20H20C21.0528 20 21.9156 19.1866 21.9942 18.1539L22 18.1632V18V16V6C22 4.89543 21.1046 4 20 4H4ZM3.2 17.7V16.5642L6.78192 13.7254C6.8616 13.6622 6.97597 13.6689 7.04776 13.7409L10.3126 17.0146C10.7026 17.4056 11.3357 17.4065 11.7268 17.0165C11.7606 16.9827 11.792 16.9465 11.8207 16.9083L16.736 10.352C16.8023 10.2636 16.9277 10.2457 17.016 10.312C17.0355 10.3265 17.0521 10.3445 17.0651 10.365L20.8 16.2669V17.7C20.8 18.3075 20.3075 18.8 19.7 18.8H4.3C3.69249 18.8 3.2 18.3075 3.2 17.7ZM17.3865 8.61836L20.8 14.08V6.3C20.8 5.69249 20.3075 5.2 19.7 5.2H4.3C3.69249 5.2 3.2 5.69249 3.2 6.3V15.04L6.65054 12.2796C6.84949 12.1204 7.13629 12.1363 7.31645 12.3164L10.8369 15.8369C10.915 15.915 11.0417 15.915 11.1198 15.8369C11.1265 15.8302 16.5625 8.58336 16.5625 8.58336C16.7282 8.36245 17.0416 8.31768 17.2625 8.48336C17.3118 8.52034 17.3538 8.56611 17.3865 8.61836ZM8 8.5C8 9.32843 7.32843 10 6.5 10C5.67157 10 5 9.32843 5 8.5C5 7.67157 5.67157 7 6.5 7C7.32843 7 8 7.67157 8 8.5Z"/></svg>',
},
{
name: 'table',
action: EasyMDE.drawTable,
title: i18n.global.t('input.editor.table'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M6.18524 3.08496H19.4152C20.6752 3.08496 21.7152 4.11496 21.7152 5.38496V18.615C21.7152 19.885 20.6852 20.915 19.4152 20.915H6.18524C4.91524 20.915 3.88525 19.885 3.88525 18.615V5.38496C3.88525 4.11496 4.91524 3.08496 6.18524 3.08496ZM19.4052 19.705C20.0152 19.705 20.5052 19.215 20.5052 18.605H20.5153V5.38496C20.5153 4.77496 20.0252 4.28496 19.4152 4.28496H6.18524C5.57524 4.28496 5.08521 4.77496 5.08521 5.38496V18.605C5.08521 19.215 5.57524 19.705 6.18524 19.705H19.4052ZM17.4453 9.15503H8.15527C7.82527 9.15503 7.5553 9.42503 7.5553 9.75503C7.5553 10.085 7.82527 10.355 8.15527 10.355H17.4453C17.7753 10.355 18.0453 10.085 18.0453 9.75503C18.0453 9.42503 17.7753 9.15503 17.4453 9.15503ZM17.4453 13.635H8.15527C7.82527 13.635 7.5553 13.905 7.5553 14.235C7.5553 14.565 7.82527 14.835 8.15527 14.835H17.4453C17.7753 14.835 18.0453 14.565 18.0453 14.235C18.0453 13.905 17.7753 13.635 17.4453 13.635Z"/></svg>',
},
{
name: 'horizontal-rule',
action: EasyMDE.drawHorizontalRule,
title: i18n.global.t('input.editor.horizontalRule'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M21 13H3C2.45 13 2 12.55 2 12C2 11.45 2.45 11 3 11H21C21.55 11 22 11.45 22 12C22 12.55 21.55 13 21 13Z"/></svg>',
},
'|',
{
name: 'side-by-side',
action: EasyMDE.toggleSideBySide,
title: i18n.global.t('input.editor.sideBySide'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M18.4787 14.58C18.3587 14.69 18.2987 14.85 18.2987 15C18.2987 15.15 18.3587 15.31 18.4787 15.42C18.7187 15.65 19.0987 15.65 19.3287 15.42L22.3287 12.42C22.5587 12.18 22.5587 11.8 22.3287 11.57L19.3287 8.56996C19.0887 8.33996 18.7087 8.33996 18.4787 8.56996C18.2487 8.80996 18.2487 9.18996 18.4787 9.41996L20.451 11.3999L14.4487 11.3999L14.4487 4.6C14.4487 4.27 14.1787 4 13.8487 4C13.5187 4 13.2487 4.27 13.2487 4.6L13.2487 19.4C13.2487 19.73 13.5187 20 13.8487 20C14.1787 20 14.4487 19.73 14.4487 19.4L14.4487 12.5999L20.4511 12.5999L18.4787 14.58ZM9.54878 19.4L9.54878 12.5999L3.5486 12.5999L5.52867 14.58C5.75867 14.81 5.75867 15.19 5.52867 15.43C5.29867 15.66 4.91867 15.66 4.67867 15.43L1.67867 12.43C1.63274 12.384 1.5956 12.3323 1.56725 12.2774C1.53058 12.2077 1.50724 12.1299 1.50068 12.0477C1.49934 12.0317 1.49867 12.0158 1.49867 12C1.49867 11.9841 1.49933 11.9682 1.50067 11.9522C1.51454 11.778 1.60365 11.6242 1.73526 11.5234L4.67867 8.57997C4.90867 8.34997 5.28867 8.34997 5.52867 8.57997C5.75867 8.80997 5.75867 9.18997 5.52867 9.42997L3.55107 11.3999L9.54878 11.3999L9.54878 4.6C9.54878 4.27 9.81878 4 10.1488 4C10.4788 4 10.7488 4.27 10.7488 4.6L10.7488 11.9999L10.7488 19.4C10.7488 19.73 10.4788 20 10.1488 20C9.81878 20 9.54878 19.73 9.54878 19.4Z"/></svg>',
},
{
name: 'guide',
action() {
window.open('https://www.markdownguide.org/basic-syntax/', '_blank')
},
title: i18n.global.t('input.editor.guide'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M19.4999 2.3999H6.4999C5.0699 2.3999 3.8999 3.5699 3.8999 4.9999V18.9999C3.8999 20.4299 5.0699 21.5999 6.4999 21.5999H19.4999C19.8299 21.5999 20.0999 21.3299 20.0999 20.9999V16.9999V2.9999C20.0999 2.6699 19.8299 2.3999 19.4999 2.3999ZM5.0999 4.9999V16.8118C5.50468 16.5513 5.98546 16.3999 6.4999 16.3999H18.8999V3.5999H6.4999C5.7299 3.5999 5.0999 4.2299 5.0999 4.9999ZM6.4999 17.5999H18.8999V20.3999H6.4999C5.7299 20.3999 5.0999 19.7699 5.0999 18.9999C5.0999 18.2299 5.7299 17.5999 6.4999 17.5999ZM8.4999 8.5999H15.4999C15.8299 8.5999 16.0999 8.3299 16.0999 7.9999C16.0999 7.6699 15.8299 7.3999 15.4999 7.3999H8.4999C8.1699 7.3999 7.8999 7.6699 7.8999 7.9999C7.8999 8.3299 8.1699 8.5999 8.4999 8.5999ZM15.4999 11.3999H8.4999C8.1699 11.3999 7.8999 11.6699 7.8999 11.9999C7.8999 12.3299 8.1699 12.5999 8.4999 12.5999H15.4999C15.8299 12.5999 16.0999 12.3299 16.0999 11.9999C16.0999 11.6699 15.8299 11.3999 15.4999 11.3999Z"/></svg>',
},
],
}
}

View File

@ -0,0 +1,71 @@
<script lang="ts" setup>
import {ref} from 'vue'
import {logEvent} from 'histoire/client'
import FancyCheckbox from './fancycheckbox.vue'
const isDisabled = ref<boolean | undefined>()
const isChecked = ref(false)
const isCheckedInitiallyEnabled = ref(true)
const isCheckedDisabled = ref(false)
const withoutInitialState = ref<boolean | undefined>()
</script>
<template>
<Story :layout="{ type: 'grid', width: '200px' }">
<Variant title="Default">
<FancyCheckbox
v-model="isChecked"
:disabled="isDisabled"
>
This is probably not important
</FancyCheckbox>
Visualisation
<input type="checkbox" v-model="isChecked">
{{ isChecked }}
</Variant>
<Variant title="Enabled Initially">
<FancyCheckbox
:disabled="isDisabled"
v-model="isCheckedInitiallyEnabled"
>
We want you to use this option
</FancyCheckbox>
Visualisation
<input type="checkbox" v-model="isCheckedInitiallyEnabled">
{{ isCheckedInitiallyEnabled }}
</Variant>
<Variant title="Disabled">
<FancyCheckbox
disabled
:modelValue="isCheckedDisabled"
@update:model-value="logEvent('Setting disabled: This should never happen', $event)"
>
You can't change this
</FancyCheckbox>
Visualisation
<input type="checkbox" v-model="isCheckedDisabled" disabled>
{{ isCheckedDisabled }}
</Variant>
<Variant title="Undefined initial State">
<FancyCheckbox
v-model="withoutInitialState"
:disabled="isDisabled"
>
Not sure what the value should be
</FancyCheckbox>
Visualisation
<input type="checkbox" v-model="withoutInitialState" disabled>
{{ withoutInitialState }}
</Variant>
</Story>
</template>

View File

@ -1,66 +1,42 @@
<template>
<div :class="{'is-disabled': disabled}" class="fancycheckbox">
<input
:checked="checked"
:disabled="disabled || undefined"
:id="checkBoxId"
@change="(event: Event) => updateData((event.target as HTMLInputElement).checked)"
type="checkbox"
/>
<label :for="checkBoxId" class="check" @click.prevent="check">
<svg height="18px" viewBox="0 0 18 18" width="18px">
<path
d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path>
<polyline points="1 9 7 14 15 4"></polyline>
</svg>
<span>
<slot></slot>
</span>
</label>
</div>
<BaseCheckbox
class="fancycheckbox"
:class="{
'is-disabled': disabled,
'is-block': isBlock,
}"
:disabled="disabled"
:model-value="modelValue"
@update:model-value="value => emit('update:modelValue', value)"
>
<CheckboxIcon class="fancycheckbox__icon" />
<span v-if="$slots.default" class="fancycheckbox__content">
<slot/>
</span>
</BaseCheckbox>
</template>
<script setup lang="ts">
import {ref, toRef, watch} from 'vue'
import CheckboxIcon from '@/assets/checkbox.svg?component'
import {createRandomID} from '@/helpers/randomId'
import BaseCheckbox from '@/components/base/BaseCheckbox.vue'
const checked = ref(false)
const checkBoxId = `fancycheckbox_${createRandomID()}`
const props = defineProps({
defineProps({
modelValue: {
type: Boolean,
required: false,
},
disabled: {
type: Boolean,
required: false,
},
isBlock: {
type: Boolean,
default: false,
},
})
const emit = defineEmits(['update:modelValue', 'change'])
const modelValue = toRef(props, 'modelValue')
watch(
modelValue,
newValue => {
checked.value = newValue
},
{immediate: true},
)
function updateData(newChecked: boolean) {
checked.value = newChecked
emit('update:modelValue', newChecked)
emit('change', newChecked)
}
function check() {
checked.value = !checked.value
updateData(checked.value)
}
const emit = defineEmits<{
(event: 'update:modelValue', value: boolean): void
}>()
</script>
@ -70,75 +46,54 @@ function check() {
padding-right: 5px;
padding-top: 3px;
// FIXME: should be a prop
&.is-block {
display: block;
margin: .5rem .2rem;
}
}
input[type=checkbox] {
display: none;
}
.check {
cursor: pointer;
position: relative;
margin: auto;
width: 18px;
height: 18px;
-webkit-tap-highlight-color: transparent;
transform: translate3d(0, 0, 0);
}
span {
.fancycheckbox__content {
font-size: 0.8rem;
vertical-align: top;
padding-left: .5rem;
}
svg {
.fancycheckbox__icon:deep() {
position: relative;
z-index: 1;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
stroke: #c8ccd4;
stroke-width: 1.5;
stroke: var(--stroke-color, #c8ccd4);
transform: translate3d(0, 0, 0);
transition: all 0.2s ease;
}
.check:hover svg {
stroke: var(--primary);
}
.is-disabled .check:hover svg {
stroke: #c8ccd4;
}
path {
stroke-dasharray: 60;
stroke-dashoffset: 0;
}
polyline {
stroke-dasharray: 22;
stroke-dashoffset: 66;
}
input[type=checkbox]:checked + .check {
svg {
stroke: var(--primary);
path,
polyline {
transition: all 0.2s linear, color 0.2s ease;
}
}
.fancycheckbox:not(:has(input:disabled)):hover .fancycheckbox__icon,
.fancycheckbox:has(input:checked) .fancycheckbox__icon {
--stroke-color: var(--primary);
}
</style>
<style lang="scss">
// Since css-has-pseudo doesn't work with deep classes,
// the following rules can't be scoped
.fancycheckbox:has(:not(input:checked)) .fancycheckbox__icon {
path {
transition-delay: 0.05s;
}
}
.fancycheckbox:has(input:checked) .fancycheckbox__icon {
path {
stroke-dashoffset: 60;
transition: all 0.3s linear;
}
polyline {
stroke-dashoffset: 42;
transition: all 0.2s linear;
transition-delay: 0.15s;
}
}

View File

@ -9,19 +9,24 @@
<div class="control" :class="{'is-loading': loading || localLoading}">
<div
class="input-wrapper input"
:class="{'has-multiple': hasMultiple}"
:class="{'has-multiple': hasMultiple, 'has-removal-button': removalAvailable}"
>
<template v-if="Array.isArray(internalValue)">
<slot
v-if="Array.isArray(internalValue)"
name="items"
:items="internalValue"
:remove="remove"
>
<template v-for="(item, key) in internalValue">
<slot name="tag" :item="item">
<span :key="`item${key}`" class="tag ml-2 mt-2">
{{ label !== '' ? item[label] : item }}
<BaseButton @click="() => remove(item)" class="delete is-small"></BaseButton>
</span>
<span :key="`item${key}`" class="tag ml-2 mt-2">
{{ label !== '' ? item[label] : item }}
<BaseButton @click="() => remove(item)" class="delete is-small"></BaseButton>
</span>
</slot>
</template>
</template>
</slot>
<input
type="text"
class="input"
@ -32,7 +37,16 @@
@keydown.down.exact.prevent="() => preSelect(0)"
ref="searchInput"
@focus="handleFocus"
:autocomplete="autocompleteEnabled ? undefined : 'off'"
:spellcheck="autocompleteEnabled ? undefined : 'false'"
/>
<BaseButton
v-if="removalAvailable"
class="removal-button"
@click="resetSelectedValue"
>
<icon icon="times"/>
</BaseButton>
</div>
</div>
@ -83,7 +97,9 @@
</template>
<script setup lang="ts">
import {computed, onBeforeUnmount, onMounted, ref, toRefs, watch, type ComponentPublicInstance, type PropType} from 'vue'
import {
computed, onBeforeUnmount, onMounted, ref, toRefs, watch, type ComponentPublicInstance, type PropType,
} from 'vue'
import {useI18n} from 'vue-i18n'
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
@ -91,6 +107,7 @@ import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import BaseButton from '@/components/base/BaseButton.vue'
import CustomTransition from '@/components/misc/CustomTransition.vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function elementInResults(elem: string | any, label: string, query: string): boolean {
// Don't make create available if we have an exact match in our search results.
if (label !== '') {
@ -119,7 +136,8 @@ const props = defineProps({
* The search results where the @search listener needs to put the results into
*/
searchResults: {
type: Array as PropType<{[id: string]: any}>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type: Array as PropType<{ [id: string]: any }>,
default: () => [],
},
/**
@ -134,7 +152,8 @@ const props = defineProps({
* The object with the value, updated every time an entry is selected.
*/
modelValue: {
type: [Object] as PropType<{[key: string]: any}>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type: [Object] as PropType<{ [key: string]: any }>,
default: null,
},
/**
@ -150,7 +169,7 @@ const props = defineProps({
createPlaceholder: {
type: String,
default() {
const {t} = useI18n({useScope: 'global'})
const {t} = useI18n({useScope: 'global'})
return t('input.multiselect.createPlaceholder')
},
},
@ -160,7 +179,7 @@ const props = defineProps({
selectPlaceholder: {
type: String,
default() {
const {t} = useI18n({useScope: 'global'})
const {t} = useI18n({useScope: 'global'})
return t('input.multiselect.selectPlaceholder')
},
},
@ -196,33 +215,43 @@ const props = defineProps({
type: Boolean,
default: true,
},
/**
* If false, the search input will get the autocomplete="off" attributes attached to it.
*/
autocompleteEnabled: {
type: Boolean,
default: true,
},
})
const emit = defineEmits<{
(e: 'update:modelValue', value: null): void
(e: 'update:modelValue', value: null): void
/**
* Triggered every time the search query input changes
*/
(e: 'search', query: string): void
(e: 'search', query: string): void
/**
* Triggered every time an option from the search results is selected. Also triggers a change in v-model.
*/
(e: 'select', value: {[key: string]: any}): void
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(e: 'select', value: { [key: string]: any }): void
/**
* If nothing or no exact match was found and `creatable` is true, this event is triggered with the current value of the search query.
*/
(e: 'create', query: string): void
(e: 'create', query: string): void
/**
* If `multiple` is enabled, this will be fired every time an item is removed from the array of selected items.
*/
(e: 'remove', value: null): void
(e: 'remove', value: null): void
}>()
const query = ref<string | {[key: string]: any}>('')
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const query = ref<string | { [key: string]: any }>('')
const searchTimeout = ref<ReturnType<typeof setTimeout> | null>(null)
const localLoading = ref(false)
const showSearchResults = ref(false)
const internalValue = ref<string | {[key: string]: any} | any[] | null>(null)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const internalValue = ref<string | { [key: string]: any } | any[] | null>(null)
onMounted(() => document.addEventListener('click', hideSearchResultsHandler))
onBeforeUnmount(() => document.removeEventListener('click', hideSearchResultsHandler))
@ -250,17 +279,19 @@ const searchResultsVisible = computed(() => {
})
const creatableAvailable = computed(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const hasResult = filteredSearchResults.value.some((elem: any) => elementInResults(elem, props.label, query.value))
const hasQueryAlreadyAdded = Array.isArray(internalValue.value) && internalValue.value.some(elem => elementInResults(elem, props.label, query.value))
return props.creatable
&& query.value !== ''
return props.creatable
&& query.value !== ''
&& !(hasResult || hasQueryAlreadyAdded)
})
const filteredSearchResults = computed(() => {
const currentInternal = internalValue.value
if (props.multiple && currentInternal !== null && Array.isArray(currentInternal)) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return searchResults.value.filter((item: any) => !currentInternal.some(e => e === item))
}
@ -271,7 +302,13 @@ const hasMultiple = computed(() => {
return props.multiple && Array.isArray(internalValue.value) && internalValue.value.length > 0
})
const removalAvailable = computed(() => !props.multiple && internalValue.value !== null && query.value !== '')
function resetSelectedValue() {
select(null)
}
const searchInput = ref<HTMLInputElement | null>(null)
// Searching will be triggered with a 200ms delay to avoid searching on every keyup event.
function search() {
@ -296,6 +333,7 @@ function search() {
}
const multiselectRoot = ref<HTMLElement | null>(null)
function hideSearchResultsHandler(e: MouseEvent) {
closeWhenClickedOutside(e, multiselectRoot.value, closeSearchResults)
}
@ -312,12 +350,14 @@ function handleFocus() {
}, 10)
}
function select(object: {[key: string]: any}) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function select(object: { [key: string]: any } | null) {
if (props.multiple) {
if (internalValue.value === null) {
internalValue.value = []
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(internalValue.value as any[]).push(object)
} else {
internalValue.value = object
@ -331,7 +371,8 @@ function select(object: {[key: string]: any}) {
}
}
function setSelectedObject(object: string | {[id: string]: any} | null, resetOnly = false) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function setSelectedObject(object: string | { [id: string]: any } | null, resetOnly = false) {
internalValue.value = object
// We assume we're getting an array when multiple is enabled and can therefore leave the query
@ -354,6 +395,7 @@ function setSelectedObject(object: string | {[id: string]: any} | null, resetOnl
}
const results = ref<(Element | ComponentPublicInstance)[]>([])
function setResult(el: Element | ComponentPublicInstance | null, index: number) {
if (el === null) {
delete results.value[index]
@ -399,8 +441,9 @@ function createOrSelectOnEnter() {
if (!creatableAvailable.value) {
// Check if there's an exact match for our search term
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const exactMatch = filteredSearchResults.value.find((elem: any) => elementInResults(elem, props.label, query.value))
if(exactMatch) {
if (exactMatch) {
select(exactMatch)
}
@ -410,6 +453,7 @@ function createOrSelectOnEnter() {
create()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function remove(item: any) {
for (const ind in internalValue.value) {
if (internalValue.value[ind] === item) {
@ -556,4 +600,14 @@ function focus() {
transition: color $transition;
padding-left: .5rem;
}
.has-removal-button {
position: relative;
}
.removal-button {
position: absolute;
right: .5rem;
color: var(--danger);
}
</style>

View File

@ -1,522 +0,0 @@
<template>
<div class="vue-easymde" ref="easymdeRef">
<textarea
class="vue-simplemde-textarea"
:name="name"
:value="modelValue"
@input="handleInput(($event.target as HTMLTextAreaElement).value)"
/>
</div>
</template>
<script setup lang="ts">
import {ref, watch, onMounted, onDeactivated, onBeforeUnmount, nextTick, shallowReactive, type ShallowReactive, type PropType} from 'vue'
import EasyMDE, {toggleFullScreen} from 'easymde'
import {marked} from 'marked'
import type CodeMirror from 'codemirror'
const props = defineProps({
modelValue: {
type: String,
default: '',
},
name: {
type: String,
},
previewClass: {
type: String,
},
autoinit: {
type: Boolean,
default: true,
},
highlight: {
type: Boolean,
default: false,
},
sanitize: {
type: Boolean,
default: false,
},
configs: {
type: Object,
default: () => ({}),
},
previewRender: {
type: Function as PropType<EasyMDE.Options['previewRender']>,
},
})
const emit = defineEmits(['update:modelValue', 'blur', 'initialized'])
const isValueUpdateFromInner = ref(false)
let easymde: ShallowReactive<EasyMDE> | undefined
onMounted(() => {
if (props.autoinit) initialize()
})
onDeactivated(() => {
if (easymde === undefined) return
if (easymde.isFullscreenActive()) toggleFullScreen(easymde)
easymde.toTextArea
})
onBeforeUnmount(() => {
if (easymde) {
easymde.toTextArea()
easymde.cleanup()
easymde = undefined
}
})
const easymdeRef = ref<HTMLElement | null>(null)
function initialize() {
const configs: EasyMDE.Options = Object.assign({
element: easymdeRef.value?.firstElementChild as HTMLElement,
initialValue: props.modelValue,
previewRender: props.previewRender,
renderingConfig: {},
}, props.configs)
// Synchronize the values of value and initialValue
if (configs.initialValue) {
emit('update:modelValue', configs.initialValue)
}
// Determine whether to enable code highlighting
if (props.highlight) {
configs.renderingConfig!.codeSyntaxHighlighting = true
}
// Set whether to render the input html
marked.setOptions({ sanitize: props.sanitize })
// Instantiated editor
easymde = shallowReactive(new EasyMDE(configs))
// Add a custom previewClass
const className = props.previewClass || ''
addPreviewClass(easymde, className)
// Binding event
easymde.codemirror.on('change', handleCodemirrorInput)
easymde.codemirror.on('blur', handleCodemirrorBlur)
nextTick(() => emit('initialized', easymde))
}
function addPreviewClass(easymde: EasyMDE, className: string) {
const wrapper = easymde.codemirror.getWrapperElement()
const preview = document.createElement('div')
wrapper.nextSibling.className += ` ${className}`
preview.className = `editor-preview ${className}`
wrapper.appendChild(preview)
}
function handleInput(val: string) {
isValueUpdateFromInner.value = true
emit('update:modelValue', val)
}
function handleCodemirrorInput(instance: CodeMirror.Editor, changeObj: CodeMirror.EditorChange) {
if (changeObj.origin === 'setValue' || easymde === undefined) {
return
}
handleInput(easymde.value())
}
function handleCodemirrorBlur() {
if (easymde === undefined) {
return
}
isValueUpdateFromInner.value = true
emit('blur', easymde.value())
}
watch(
() => props.modelValue,
(val) => {
if (isValueUpdateFromInner.value) {
isValueUpdateFromInner.value = false
} else {
easymde?.value(val)
}
},
)
</script>
<style lang="scss">
.EasyMDEContainer {
display: block;
}
.EasyMDEContainer.sided--no-fullscreen {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.EasyMDEContainer .CodeMirror {
box-sizing: border-box;
height: auto;
border: 1px solid #ddd;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
padding: 10px;
font: inherit;
z-index: 0;
word-wrap: break-word;
}
.EasyMDEContainer .CodeMirror-scroll {
cursor: text;
}
.EasyMDEContainer .CodeMirror-fullscreen {
background: #fff;
position: fixed !important;
top: 50px;
left: 0;
right: 0;
bottom: 0;
height: auto;
z-index: 8;
border-right: none !important;
border-bottom-right-radius: 0 !important;
}
.EasyMDEContainer .CodeMirror-sided {
width: 50% !important;
}
.EasyMDEContainer.sided--no-fullscreen .CodeMirror-sided {
border-right: none!important;
border-bottom-right-radius: 0px;
position: relative;
flex: 1 1 auto;
}
.EasyMDEContainer .CodeMirror-placeholder {
opacity: .5;
}
.EasyMDEContainer .CodeMirror-focused .CodeMirror-selected {
background: #d9d9d9;
}
.editor-toolbar {
position: relative;
user-select: none;
padding: 9px 10px;
border-top: 1px solid #bbb;
border-left: 1px solid #bbb;
border-right: 1px solid #bbb;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.editor-toolbar.fullscreen {
width: 100%;
height: 50px;
padding-top: 10px;
padding-bottom: 10px;
box-sizing: border-box;
background: #fff;
border: 0;
position: fixed;
top: 0;
left: 0;
opacity: 1;
z-index: 9;
}
.editor-toolbar.fullscreen::before {
width: 20px;
height: 50px;
background: linear-gradient(to right, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
position: fixed;
top: 0;
left: 0;
margin: 0;
padding: 0;
}
.editor-toolbar.fullscreen::after {
width: 20px;
height: 50px;
background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%);
position: fixed;
top: 0;
right: 0;
margin: 0;
padding: 0;
}
.EasyMDEContainer.sided--no-fullscreen .editor-toolbar {
width: 100%;
}
.editor-toolbar button, .editor-toolbar .easymde-dropdown {
background: transparent;
display: inline-block;
text-align: center;
text-decoration: none !important;
height: 30px;
margin: 0;
padding: 0;
border: 1px solid transparent;
border-radius: 3px;
cursor: pointer;
}
.editor-toolbar button {
width: 30px;
}
.editor-toolbar button.active,
.editor-toolbar button:hover {
background: #fcfcfc;
border-color: #95a5a6;
}
.editor-toolbar i.separator {
display: inline-block;
width: 0;
border-left: 1px solid #d9d9d9;
border-right: 1px solid #fff;
color: transparent;
text-indent: -10px;
margin: 0 6px;
}
.editor-toolbar button:after {
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
font-size: 65%;
vertical-align: text-bottom;
position: relative;
top: 2px;
}
.editor-toolbar button.heading-1:after {
content: "1";
}
.editor-toolbar button.heading-2:after {
content: "2";
}
.editor-toolbar button.heading-3:after {
content: "3";
}
.editor-toolbar button.heading-bigger:after {
content: "▲";
}
.editor-toolbar button.heading-smaller:after {
content: "▼";
}
.editor-toolbar.disabled-for-preview button:not(.no-disable) {
opacity: .6;
pointer-events: none;
}
@media only screen and (max-width: 700px) {
.editor-toolbar i.no-mobile {
display: none;
}
}
.editor-statusbar {
padding: 8px 10px;
font-size: 12px;
color: #959694;
text-align: right;
}
.EasyMDEContainer.sided--no-fullscreen .editor-statusbar {
width: 100%;
}
.editor-statusbar span {
display: inline-block;
min-width: 4em;
margin-left: 1em;
}
.editor-statusbar .lines:before {
content: 'lines: '
}
.editor-statusbar .words:before {
content: 'words: '
}
.editor-statusbar .characters:before {
content: 'characters: '
}
.editor-preview-full {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 7;
overflow: auto;
display: none;
box-sizing: border-box;
}
.editor-preview-side {
position: fixed;
bottom: 0;
width: 50%;
top: 50px;
right: 0;
z-index: 9;
overflow: auto;
display: none;
box-sizing: border-box;
border: 1px solid #ddd;
word-wrap: break-word;
}
.editor-preview-active-side {
display: block
}
.EasyMDEContainer.sided--no-fullscreen .editor-preview-active-side {
flex: 1 1 auto;
height: auto;
position: static;
}
.editor-preview-active {
display: block
}
.editor-preview {
padding: 10px;
background: #fafafa;
}
.editor-preview > p {
margin-top: 0
}
.editor-preview pre {
background: #eee;
margin-bottom: 10px;
}
.editor-preview table td,
.editor-preview table th {
border: 1px solid #ddd;
padding: 5px;
}
.cm-s-easymde .cm-tag {
color: #63a35c;
}
.cm-s-easymde .cm-attribute {
color: #795da3;
}
.cm-s-easymde .cm-string {
color: #183691;
}
.cm-s-easymde .cm-header-1 {
font-size: 200%;
line-height: 200%;
}
.cm-s-easymde .cm-header-2 {
font-size: 160%;
line-height: 160%;
}
.cm-s-easymde .cm-header-3 {
font-size: 125%;
line-height: 125%;
}
.cm-s-easymde .cm-header-4 {
font-size: 110%;
line-height: 110%;
}
.cm-s-easymde .cm-comment {
background: rgba(0, 0, 0, .05);
border-radius: 2px;
}
.cm-s-easymde .cm-link {
color: #7f8c8d;
}
.cm-s-easymde .cm-url {
color: #aab2b3;
}
.cm-s-easymde .cm-quote {
color: #7f8c8d;
font-style: italic;
}
.editor-toolbar .easymde-dropdown {
position: relative;
background: linear-gradient(to bottom right, #fff 0%, #fff 84%, #333 50%, #333 100%);
border-radius: 0;
border: 1px solid #fff;
}
.editor-toolbar .easymde-dropdown:hover {
background: linear-gradient(to bottom right, #fff 0%, #fff 84%, #333 50%, #333 100%);
}
.easymde-dropdown-content {
display: block;
visibility: hidden;
position: absolute;
background-color: #f9f9f9;
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
padding: 8px;
z-index: 2;
top: 30px;
}
.easymde-dropdown:active .easymde-dropdown-content,
.easymde-dropdown:focus .easymde-dropdown-content {
visibility: visible;
}
span[data-img-src]::after{
content: '';
background-image: var(--bg-image);
display: block;
max-height: 100%;
max-width: 100%;
background-size: contain;
height: 0;
padding-top: var(--height);
width: var(--width);
background-repeat: no-repeat;
}
</style>
<style lang="scss" scoped>
.vue-easymde .markdown-body {
padding: 0.5em
}
.vue-easymde .editor-preview-active,
.vue-easymde .editor-preview-active-side {
display: block;
}
</style>

View File

@ -2,10 +2,17 @@ import {library} from '@fortawesome/fontawesome-svg-core'
import {
faAlignLeft,
faAngleRight,
faAnglesUp,
faArchive,
faArrowLeft,
faArrowUpFromBracket,
faBold,
faItalic,
faStrikethrough,
faCode,
faBars,
faBell,
faBolt,
faCalendar,
faCheck,
faCheckDouble,
@ -26,6 +33,7 @@ import {
faFilter,
faForward,
faGripLines,
faHeader,
faHistory,
faImage,
faKeyboard,
@ -57,21 +65,49 @@ import {
faTrashAlt,
faUser,
faUsers,
faQuoteRight,
faListUl,
faLink,
faUndo,
faRedo,
faUnlink,
faParagraph,
faTable,
faX, faArrowTurnDown, faListCheck, faXmark, faXmarksLines, faFont, faRulerHorizontal, faUnderline,
} from '@fortawesome/free-solid-svg-icons'
import {
faBellSlash,
faCalendarAlt,
faCheckSquare,
faClock,
faComments,
faFileImage,
faSave,
faSquareCheck,
faStar,
faSun,
faTimesCircle,
faCircleQuestion,
} from '@fortawesome/free-regular-svg-icons'
import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome'
import type { FontAwesomeIcon as FontAwesomeIconFixedTypes } from '@/types/vue-fontawesome'
import type {FontAwesomeIcon as FontAwesomeIconFixedTypes} from '@/types/vue-fontawesome'
library.add(faBold)
library.add(faUndo)
library.add(faRedo)
library.add(faItalic)
library.add(faLink)
library.add(faUnlink)
library.add(faParagraph)
library.add(faSquareCheck)
library.add(faTable)
library.add(faFileImage)
library.add(faCheckSquare)
library.add(faStrikethrough)
library.add(faCode)
library.add(faQuoteRight)
library.add(faListUl)
library.add(faAlignLeft)
library.add(faAngleRight)
library.add(faArchive)
@ -86,6 +122,7 @@ library.add(faCheckDouble)
library.add(faChessKnight)
library.add(faChevronDown)
library.add(faCircleInfo)
library.add(faCircleQuestion)
library.add(faClock)
library.add(faCloudDownloadAlt)
library.add(faCloudUploadAlt)
@ -102,6 +139,7 @@ library.add(faFillDrip)
library.add(faFilter)
library.add(faForward)
library.add(faGripLines)
library.add(faHeader)
library.add(faHistory)
library.add(faImage)
library.add(faKeyboard)
@ -137,6 +175,17 @@ library.add(faTimesCircle)
library.add(faTrashAlt)
library.add(faUser)
library.add(faUsers)
library.add(faArrowUpFromBracket)
library.add(faX)
library.add(faAnglesUp)
library.add(faBolt)
library.add(faArrowTurnDown)
library.add(faListCheck)
library.add(faXmark)
library.add(faXmarksLines)
library.add(faFont)
library.add(faRulerHorizontal)
library.add(faUnderline)
// overwriting the wrong types
export default FontAwesomeIcon as unknown as FontAwesomeIconFixedTypes

View File

@ -0,0 +1,40 @@
<script setup lang="ts">
import BaseButton from '@/components/base/BaseButton.vue'
import {useBaseStore} from '@/stores/base'
import {onBeforeUnmount, onMounted} from 'vue'
import {eventToHotkeyString} from '@github/hotkey'
const baseStore = useBaseStore()
// See https://github.com/github/hotkey/discussions/85#discussioncomment-5214660
function openQuickActionsViaHotkey(event) {
const hotkeyString = eventToHotkeyString(event)
if (!hotkeyString) return
if (hotkeyString !== 'Control+k' && hotkeyString !== 'Meta+k') return
event.preventDefault()
openQuickActions()
}
onMounted(() => {
document.addEventListener('keydown', openQuickActionsViaHotkey)
})
onBeforeUnmount(() => {
document.removeEventListener('keydown', openQuickActionsViaHotkey)
})
function openQuickActions() {
baseStore.setQuickActionsActive(true)
}
</script>
<template>
<BaseButton
@click="openQuickActions"
class="trigger-button"
:title="$t('keyboardShortcuts.quickSearch')"
>
<icon icon="search"/>
</BaseButton>
</template>

View File

@ -24,12 +24,12 @@
}"
>
<div :class="{'content': hasContent}">
<slot />
<slot/>
</div>
</div>
<footer v-if="$slots.footer" class="card-footer">
<slot name="footer" />
<slot name="footer"/>
</footer>
</div>
</template>
@ -76,22 +76,27 @@ defineEmits(['close'])
<style lang="scss" scoped>
.card {
background-color: var(--white);
border-radius: $radius;
margin-bottom: 1rem;
border: 1px solid var(--card-border-color);
box-shadow: var(--shadow-sm);
background-color: var(--white);
border-radius: $radius;
margin-bottom: 1rem;
border: 1px solid var(--card-border-color);
box-shadow: var(--shadow-sm);
@media print {
box-shadow: none;
border: none;
}
}
.card-header {
box-shadow: none;
border-bottom: 1px solid var(--card-border-color);
border-radius: $radius $radius 0 0;
box-shadow: none;
border-bottom: 1px solid var(--card-border-color);
border-radius: $radius $radius 0 0;
}
.card-footer {
background-color: var(--grey-50);
border-top: 0;
background-color: var(--grey-50);
border-top: 0;
padding: var(--modal-card-head-padding);
display: flex;
justify-content: flex-end;

View File

@ -1,21 +1,26 @@
<template>
<BaseButton class="dropdown-item">
<span class="icon" v-if="icon">
<span
v-if="icon"
class="icon is-small"
:class="iconClass"
>
<Icon :icon="icon"/>
</span>
<span>
<slot />
<slot/>
</span>
</BaseButton>
</template>
<script lang="ts" setup>
import BaseButton, { type BaseButtonProps } from '@/components/base//BaseButton.vue'
import BaseButton, {type BaseButtonProps} from '@/components/base//BaseButton.vue'
import Icon from '@/components/misc/Icon'
import type { IconProp } from '@fortawesome/fontawesome-svg-core'
import type {IconProp} from '@fortawesome/fontawesome-svg-core'
export interface DropDownItemProps extends BaseButtonProps {
export interface DropDownItemProps extends /* @vue-ignore */ BaseButtonProps {
icon?: IconProp,
iconClass?: object | string,
}
defineProps<DropDownItemProps>()
@ -24,7 +29,6 @@ defineProps<DropDownItemProps>()
<style scoped lang="scss">
.dropdown-item {
color: var(--text);
display: block;
font-size: 0.875rem;
line-height: 1.5;
padding: $item-padding;
@ -52,10 +56,7 @@ defineProps<DropDownItemProps>()
.icon {
padding-right: .5rem;
&:not(.has-text-success) {
color: var(--grey-300) !important;
}
color: var(--grey-300);
}
.has-text-danger .icon {

View File

@ -1,10 +1,10 @@
<template>
<input
type="text"
data-input
:disabled="disabled"
data-input
:disabled="disabled"
v-bind="attrs"
ref="root"
ref="root"
/>
</template>
@ -20,39 +20,39 @@ type Options = flatpickr.Options.Options
type DateOption = flatpickr.Options.DateOption
function camelToKebab(string: string) {
return string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
return string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
}
function arrayify<T = unknown>(obj: T) {
return obj instanceof Array
return obj instanceof Array
? obj
: [obj]
}
function nullify<T = unknown>(value: T) {
return (value && (value as unknown[]).length)
return (value && (value as unknown[]).length)
? value
: null
}
// Events to emit, copied from flatpickr source
const includedEvents = [
'onChange',
'onClose',
'onDestroy',
'onMonthChange',
'onOpen',
'onYearChange',
'onChange',
'onClose',
'onDestroy',
'onMonthChange',
'onOpen',
'onYearChange',
] as HookKey[]
// Let's not emit these events by default
const excludedEvents = [
'onValueUpdate',
'onDayCreate',
'onParseConfig',
'onReady',
'onPreCalendarPosition',
'onKeyDown',
'onValueUpdate',
'onDayCreate',
'onParseConfig',
'onReady',
'onPreCalendarPosition',
'onKeyDown',
] as HookKey[]
// Keep a copy of all events for later use
@ -100,19 +100,19 @@ const attrs = useAttrs()
const root = ref<HTMLInputElement | null>(null)
const fp = ref<flatpickr.Instance | null>(null)
const safeConfig = ref<Options>({ ...props.config })
const safeConfig = ref<Options>({...props.config})
function prepareConfig() {
// Don't mutate original object on parent component
const newConfig: Options = { ...props.config }
const newConfig: Options = {...props.config}
props.events.forEach((hook) => {
// Respect global callbacks registered via setDefault() method
const globalCallbacks = flatpickr.defaultConfig[hook] || []
// Inject our own method along with user callback
const localCallback: Hook = (...args) => emit(camelToKebab(hook), ...args)
// Overwrite with merged array
newConfig[hook] = arrayify(newConfig[hook] || []).concat(
globalCallbacks,
@ -147,9 +147,9 @@ onMounted(() => {
prepareConfig()
/**
* Get the HTML node where flatpickr to be attached
* Bind on parent element if wrap is true
*/
* Get the HTML node where flatpickr to be attached
* Bind on parent element if wrap is true
*/
const element = props.config.wrap
? root.value.parentNode
: root.value
@ -179,7 +179,7 @@ watch(config, () => {
fp.value.set(name, safeConfig.value[name])
}
})
}, {deep:true})
}, {deep: true})
const fpInput = computed(() => {
if (!fp.value) return
@ -198,8 +198,8 @@ watchEffect(() => fpInput.value?.addEventListener('blur', onBlur))
onBeforeUnmount(() => fpInput.value?.removeEventListener('blur', onBlur))
/**
* Watch for the disabled property and sets the value to the real input.
*/
* Watch for the disabled property and sets the value to the real input.
*/
watchEffect(() => {
if (disabled.value) {
fpInput.value?.setAttribute('disabled', '')

View File

@ -44,8 +44,8 @@ export const KEYBOARD_SHORTCUTS : ShortcutGroup[] = [
combination: 'then',
},
{
title: 'keyboardShortcuts.navigation.namespaces',
keys: ['g', 'n'],
title: 'keyboardShortcuts.navigation.projects',
keys: ['g', 'p'],
combination: 'then',
},
{
@ -140,6 +140,22 @@ export const KEYBOARD_SHORTCUTS : ShortcutGroup[] = [
title: 'keyboardShortcuts.task.description',
keys: ['e'],
},
{
title: 'keyboardShortcuts.task.priority',
keys: ['p'],
},
{
title: 'keyboardShortcuts.task.delete',
keys: ['shift', 'delete'],
},
{
title: 'keyboardShortcuts.task.favorite',
keys: ['s'],
},
{
title: 'keyboardShortcuts.task.save',
keys: [ctrl, 's'],
},
],
},
]
]

View File

@ -1,13 +1,21 @@
<template>
<div class="loader-container is-loading"></div>
<div class="loader-container is-loading" :class="{'is-small': variant === 'small'}"></div>
</template>
<script lang="ts">
export default {
inheritAttrs: false,
inheritAttrs: true,
}
</script>
<script lang="ts" setup>
const {
variant = 'default',
} = defineProps<{
variant?: 'default' | 'small'
}>()
</script>
<style scoped lang="scss">
.loader-container {
height: 100%;
@ -20,5 +28,18 @@ export default {
min-height: 50px;
min-width: 100px;
}
&.is-small {
min-width: 100%;
height: 150px;
&.is-loading::after {
width: 3rem;
height: 3rem;
top: calc(50% - 1.5rem);
left: calc(50% - 1.5rem);
border-width: 3px;
}
}
}
</style>

View File

@ -14,7 +14,7 @@
>
<div
class="modal-container"
@click.self.prevent.stop="$emit('close')"
@mousedown.self.prevent.stop="$emit('close')"
v-shortcut="'Escape'"
>
<div
@ -75,17 +75,19 @@ import BaseButton from '@/components/base/BaseButton.vue'
import {ref, useAttrs, watchEffect} from 'vue'
import {useScrollLock} from '@vueuse/core'
const props = withDefaults(defineProps<{
const {
enabled = true,
overflow,
wide,
transitionName = 'modal',
variant = 'default',
} = defineProps<{
enabled?: boolean,
overflow?: boolean,
wide?: boolean,
transitionName?: 'modal' | 'fade',
variant?: 'default' | 'hint-modal' | 'scrolling',
}>(), {
enabled: true,
transitionName: 'modal',
variant: 'default',
})
}>()
defineEmits(['close', 'submit'])
@ -95,7 +97,7 @@ const modal = ref<HTMLElement | null>(null)
const scrollLock = useScrollLock(modal)
watchEffect(() => {
scrollLock.value = props.enabled
scrollLock.value = enabled
})
</script>
@ -195,7 +197,7 @@ $modal-width: 1024px;
.close {
$close-button-padding: 26px;
position: fixed;
top: 5px;
top: .5rem;
right: $close-button-padding;
color: var(--grey-900);
font-size: 2rem;
@ -210,6 +212,10 @@ $modal-width: 1024px;
@media screen and (min-width: calc(#{$desktop } + #{$close-button-min-space})) {
color: var(--white);
}
@media screen and (min-width: $tablet) and (max-width: #{$desktop + $close-button-min-space}) {
top: .75rem;
}
}
</style>

View File

@ -13,7 +13,7 @@
<section class="content">
<div>
<h2 class="title" v-if="title">{{ title }}</h2>
<api-config/>
<api-config v-if="showApiConfig"/>
<Message v-if="motd !== ''" class="is-hidden-tablet mb-4">
{{ motd }}
</Message>
@ -45,6 +45,12 @@ const route = useRoute()
const {t} = useI18n({useScope: 'global'})
const title = computed(() => t(route.meta?.title as string || ''))
useTitle(() => title.value)
const {
showApiConfig = true,
} = defineProps<{
showApiConfig?: boolean
}>()
</script>
<style lang="scss" scoped>

View File

@ -8,7 +8,7 @@
}"
ref="popup"
>
<slot name="content" :isOpen="open"/>
<slot name="content" :isOpen="open" :toggle="toggle"/>
</div>
</template>
@ -23,11 +23,14 @@ const props = defineProps({
},
})
const emit = defineEmits(['close'])
const open = ref(false)
const popup = ref<HTMLElement | null>(null)
function close() {
open.value = false
emit('close')
}
function toggle() {

View File

@ -11,22 +11,20 @@
<slot/>
</template>
<section v-else-if="error !== ''">
<no-auth-wrapper>
<card>
<p v-if="error === ERROR_NO_API_URL">
{{ $t('ready.noApiUrlConfigured') }}
<no-auth-wrapper :show-api-config="false">
<p v-if="error === ERROR_NO_API_URL">
{{ $t('ready.noApiUrlConfigured') }}
</p>
<message variant="danger" v-else class="mb-4">
<p>
{{ $t('ready.errorOccured') }}<br/>
{{ error }}
</p>
<message variant="danger" v-else>
<p>
{{ $t('ready.errorOccured') }}<br/>
{{ error }}
</p>
<p>
{{ $t('ready.checkApiUrl') }}
</p>
</message>
<api-config :configure-open="true" @found-api="load"/>
</card>
<p>
{{ $t('ready.checkApiUrl') }}
</p>
</message>
<api-config :configure-open="true" @found-api="load"/>
</no-auth-wrapper>
</section>
<CustomTransition name="fade">
@ -50,17 +48,20 @@ import Message from '@/components/misc/message.vue'
import CustomTransition from '@/components/misc/CustomTransition.vue'
import NoAuthWrapper from '@/components/misc/no-auth-wrapper.vue'
import {ERROR_NO_API_URL} from '@/helpers/checkAndSetApiUrl'
import {ERROR_NO_API_URL, InvalidApiUrlProvidedError, NoApiUrlProvidedError} from '@/helpers/checkAndSetApiUrl'
import {useOnline} from '@/composables/useOnline'
import {getAuthForRoute} from '@/router'
import {useBaseStore} from '@/stores/base'
import {useAuthStore} from '@/stores/auth'
import {useI18n} from 'vue-i18n'
const router = useRouter()
const route = useRoute()
const baseStore = useBaseStore()
const authStore = useAuthStore()
const ready = computed(() => baseStore.ready)
const online = useOnline()
@ -68,16 +69,26 @@ const online = useOnline()
const error = ref('')
const showLoading = computed(() => !ready.value && error.value === '')
const {t} = useI18n()
async function load() {
try {
await baseStore.loadApp()
baseStore.setReady(true)
const redirectTo = await getAuthForRoute(route)
const redirectTo = await getAuthForRoute(route, authStore)
if (typeof redirectTo !== 'undefined') {
await router.push(redirectTo)
}
} catch (e: unknown) {
error.value = String(e)
if (e instanceof NoApiUrlProvidedError) {
error.value = ERROR_NO_API_URL
return
}
if (e instanceof InvalidApiUrlProvidedError) {
error.value = t('apiConfig.error')
return
}
error.value = String(e.message)
}
}

View File

@ -47,7 +47,7 @@ import {success} from '@/message'
import type { IconProp } from '@fortawesome/fontawesome-svg-core'
const props = defineProps({
entity: String,
entity: String as ISubscription['entity'],
entityId: Number,
isButton: {
type: Boolean,
@ -73,12 +73,6 @@ const {t} = useI18n({useScope: 'global'})
const tooltipText = computed(() => {
if (disabled.value) {
if (props.entity === 'project' && subscriptionEntity.value === 'namespace') {
return t('task.subscription.subscribedProjectThroughParentNamespace')
}
if (props.entity === 'task' && subscriptionEntity.value === 'namespace') {
return t('task.subscription.subscribedTaskThroughParentNamespace')
}
if (props.entity === 'task' && subscriptionEntity.value === 'project') {
return t('task.subscription.subscribedTaskThroughParentProject')
}
@ -87,10 +81,6 @@ const tooltipText = computed(() => {
}
switch (props.entity) {
case 'namespace':
return props.modelValue !== null ?
t('task.subscription.subscribedNamespace') :
t('task.subscription.notSubscribedNamespace')
case 'project':
return props.modelValue !== null ?
t('task.subscription.subscribedProject') :
@ -130,9 +120,6 @@ async function subscribe() {
let message = ''
switch (props.entity) {
case 'namespace':
message = t('task.subscription.subscribeSuccessNamespace')
break
case 'project':
message = t('task.subscription.subscribeSuccessProject')
break
@ -153,9 +140,6 @@ async function unsubscribe() {
let message = ''
switch (props.entity) {
case 'namespace':
message = t('task.subscription.unsubscribeSuccessNamespace')
break
case 'project':
message = t('task.subscription.unsubscribeSuccessProject')
break

View File

@ -48,10 +48,11 @@ const displayName = computed(() => getDisplayName(props.user))
<style lang="scss" scoped>
.user {
margin: .5rem;
display: flex;
justify-items: center;
&.is-inline {
display: inline;
display: inline-flex;
}
}

View File

@ -1,103 +0,0 @@
<template>
<dropdown>
<template #trigger="triggerProps">
<slot name="trigger" v-bind="triggerProps">
<BaseButton class="dropdown-trigger" @click="triggerProps.toggleOpen">
<icon icon="ellipsis-h" class="icon"/>
</BaseButton>
</slot>
</template>
<template v-if="namespace.isArchived">
<dropdown-item
:to="{ name: 'namespace.settings.archive', params: { id: namespace.id } }"
icon="archive"
>
{{ $t('menu.unarchive') }}
</dropdown-item>
</template>
<template v-else>
<dropdown-item
:to="{ name: 'namespace.settings.edit', params: { id: namespace.id } }"
icon="pen"
>
{{ $t('menu.edit') }}
</dropdown-item>
<dropdown-item
:to="{ name: 'namespace.settings.share', params: { namespaceId: namespace.id } }"
icon="share-alt"
>
{{ $t('menu.share') }}
</dropdown-item>
<dropdown-item
:to="{ name: 'project.create', params: { namespaceId: namespace.id } }"
icon="plus"
>
{{ $t('menu.newProject') }}
</dropdown-item>
<dropdown-item
:to="{ name: 'namespace.settings.archive', params: { id: namespace.id } }"
icon="archive"
>
{{ $t('menu.archive') }}
</dropdown-item>
<Subscription
class="has-no-shadow"
:is-button="false"
entity="namespace"
:entity-id="namespace.id"
:model-value="subscription"
@update:model-value="setSubscriptionInStore"
type="dropdown"
/>
<dropdown-item
:to="{ name: 'namespace.settings.delete', params: { id: namespace.id } }"
icon="trash-alt"
class="has-text-danger"
>
{{ $t('menu.delete') }}
</dropdown-item>
</template>
</dropdown>
</template>
<script setup lang="ts">
import {ref, onMounted, type PropType} from 'vue'
import BaseButton from '@/components/base/BaseButton.vue'
import Dropdown from '@/components/misc/dropdown.vue'
import DropdownItem from '@/components/misc/dropdown-item.vue'
import Subscription from '@/components/misc/subscription.vue'
import type {INamespace} from '@/modelTypes/INamespace'
import type {ISubscription} from '@/modelTypes/ISubscription'
import {useNamespaceStore} from '@/stores/namespaces'
const props = defineProps({
namespace: {
type: Object as PropType<INamespace>,
required: true,
},
})
const namespaceStore = useNamespaceStore()
const subscription = ref<ISubscription | null>(null)
onMounted(() => {
subscription.value = props.namespace.subscription
})
function setSubscriptionInStore(sub: ISubscription) {
subscription.value = sub
namespaceStore.setNamespaceById({
...props.namespace,
subscription: sub,
})
}
</script>
<style scoped lang="scss">
.dropdown-trigger {
padding: 0.5rem;
display: flex;
}
</style>

View File

@ -20,7 +20,8 @@
:user="n.notification.doer"
:show-username="false"
:avatar-size="16"
v-if="n.notification.doer"/>
v-if="n.notification.doer"
/>
<div class="detail">
<div>
<span class="has-text-weight-bold mr-1" v-if="n.notification.doer">
@ -35,6 +36,14 @@
</span>
</div>
</div>
<x-button
v-if="notifications.length > 0 && unreadNotifications > 0"
@click="markAllRead"
variant="tertiary"
class="mt-2 is-fullwidth"
>
{{ $t('notification.markAllRead') }}
</x-button>
<p class="nothing" v-if="notifications.length === 0">
{{ $t('notification.none') }}<br/>
<span class="explainer">
@ -59,11 +68,15 @@ import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import {formatDateLong, formatDateSince} from '@/helpers/time/formatDate'
import {getDisplayName} from '@/models/user'
import {useAuthStore} from '@/stores/auth'
import XButton from '@/components/input/button.vue'
import {success} from '@/message'
import {useI18n} from 'vue-i18n'
const LOAD_NOTIFICATIONS_INTERVAL = 10000
const authStore = useAuthStore()
const router = useRouter()
const {t} = useI18n()
const allNotifications = ref<INotification[]>([])
const showNotifications = ref(false)
@ -137,6 +150,12 @@ function to(n, index) {
allNotifications.value[index] = await notificationService.update(n)
}
}
async function markAllRead() {
const notificationService = new NotificationService()
await notificationService.markAllRead()
success({message: t('notification.markAllReadSuccess')})
}
</script>
<style lang="scss" scoped>
@ -145,12 +164,13 @@ function to(n, index) {
.trigger-button {
width: 100%;
position: relative;
}
.unread-indicator {
position: absolute;
top: .75rem;
right: 1.15rem;
top: 1rem;
right: .5rem;
width: .75rem;
height: .75rem;

View File

@ -1,9 +1,13 @@
<template>
<div
:class="{ 'is-loading': projectService.loading, 'is-archived': currentProject.isArchived}"
:class="{ 'is-loading': projectService.loading, 'is-archived': currentProject?.isArchived}"
class="loader-container"
>
<div class="switch-view-container">
<h1 class="project-title-print">
{{ getProjectTitle(currentProject) }}
</h1>
<div class="switch-view-container d-print-none">
<div class="switch-view">
<BaseButton
v-shortcut="'g l'"
@ -45,8 +49,8 @@
<slot name="header" />
</div>
<CustomTransition name="fade">
<Message variant="warning" v-if="currentProject.isArchived" class="mb-4">
{{ $t('project.archived') }}
<Message variant="warning" v-if="currentProject?.isArchived" class="mb-4">
{{ $t('project.archivedMessage') }}
</Message>
</CustomTransition>
@ -98,7 +102,7 @@ const currentProject = computed(() => {
maxRight: null,
} : baseStore.currentProject
})
useTitle(() => currentProject.value.id ? getProjectTitle(currentProject.value) : '')
useTitle(() => currentProject.value?.id ? getProjectTitle(currentProject.value) : '')
// watchEffect would be called every time the prop would get a value assigned, even if that value was the same as before.
// This resulted in loading and setting the project multiple times, even when navigating away from it.
@ -118,7 +122,7 @@ watch(
(
projectIdToLoad === loadedProjectId.value ||
typeof projectIdToLoad === 'undefined' ||
projectIdToLoad === currentProject.value.id
projectIdToLoad === currentProject.value?.id
)
&& typeof currentProject.value !== 'undefined' && currentProject.value.maxRight !== null
) {
@ -130,10 +134,8 @@ watch(
// Set the current project to the one we're about to load so that the title is already shown at the top
loadedProjectId.value = 0
const projectFromStore = projectStore.getProjectById(projectData.id)
if (projectFromStore !== null) {
baseStore.setBackground(null)
baseStore.setBlurHash(null)
const projectFromStore = projectStore.projects[projectData.id]
if (projectFromStore) {
baseStore.handleSetCurrentProject({project: projectFromStore})
}
@ -197,4 +199,15 @@ watch(
.is-archived .notification.is-warning {
margin-bottom: 1rem;
}
.project-title-print {
display: none;
font-size: 1.75rem;
text-align: center;
margin-bottom: .5rem;
@media print {
display: block;
}
}
</style>

View File

@ -15,9 +15,14 @@
:class="{'is-visible': background}"
:style="{'background-image': background !== null ? `url(${background})` : undefined}"
/>
<span v-if="project.isArchived" class="is-archived" >{{ $t('namespace.archived') }}</span>
<span v-if="project.isArchived" class="is-archived" >{{ $t('project.archived') }}</span>
<div class="project-title" aria-hidden="true">{{ project.title }}</div>
<div class="project-title" aria-hidden="true">
<span v-if="project.id < -1" class="saved-filter-icon icon">
<icon icon="filter"/>
</span>
{{ project.title }}
</div>
<BaseButton
class="project-button"
:aria-label="project.title"
@ -28,7 +33,7 @@
}"
/>
<BaseButton
v-if="!project.isArchived"
v-if="!project.isArchived && project.id > -1"
class="favorite"
:class="{'is-favorite': project.isFavorite}"
@click.prevent.stop="projectStore.toggleProjectFavorite(project)"
@ -39,8 +44,6 @@
</template>
<script lang="ts" setup>
import {toRef, type PropType} from 'vue'
import type {IProject} from '@/modelTypes/IProject'
import BaseButton from '@/components/base/BaseButton.vue'
@ -48,14 +51,13 @@ import BaseButton from '@/components/base/BaseButton.vue'
import {useProjectBackground} from './useProjectBackground'
import {useProjectStore} from '@/stores/projects'
const props = defineProps({
project: {
type: Object as PropType<IProject>,
required: true,
},
})
const {
project,
} = defineProps<{
project: IProject,
}>()
const {background, blurHashUrl} = useProjectBackground(toRef(props, 'project'))
const {background, blurHashUrl} = useProjectBackground(project)
const projectStore = useProjectStore()
</script>
@ -179,4 +181,9 @@ const projectStore = useProjectStore()
opacity: 1;
}
}
.saved-filter-icon {
color: var(--grey-300);
font-size: .75em;
}
</style>

View File

@ -147,6 +147,7 @@
<label class="label">{{ $t('task.attributes.labels') }}</label>
<div class="control labels-list">
<edit-labels
:creatable="false"
v-model="entities.labels"
@update:model-value="changeLabelFilter"
/>
@ -154,9 +155,10 @@
</div>
<template
v-if="['filters.create', 'project.edit', 'filter.settings.edit'].includes($route.name as string)">
v-if="['filters.create', 'project.edit', 'filter.settings.edit'].includes($route.name as string)"
>
<div class="field">
<label class="label">{{ $t('project.lists') }}</label>
<label class="label">{{ $t('project.projects') }}</label>
<div class="control">
<SelectProject
v-model="entities.projects"
@ -165,16 +167,6 @@
/>
</div>
</div>
<div class="field">
<label class="label">{{ $t('namespace.namespaces') }}</label>
<div class="control">
<SelectNamespace
v-model="entities.namespace"
@select="changeMultiselectFilter('namespace', 'namespace')"
@remove="changeMultiselectFilter('namespace', 'namespace')"
/>
</div>
</div>
</template>
</card>
</template>
@ -189,7 +181,6 @@ import {camelCase} from 'camel-case'
import type {ILabel} from '@/modelTypes/ILabel'
import type {IUser} from '@/modelTypes/IUser'
import type {INamespace} from '@/modelTypes/INamespace'
import type {IProject} from '@/modelTypes/IProject'
import {useLabelStore} from '@/stores/labels'
@ -201,7 +192,6 @@ import EditLabels from '@/components/tasks/partials/editLabels.vue'
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
import SelectUser from '@/components/input/SelectUser.vue'
import SelectProject from '@/components/input/SelectProject.vue'
import SelectNamespace from '@/components/input/SelectNamespace.vue'
import {parseDateOrString} from '@/helpers/time/parseDateOrString'
import {dateIsValid, formatISO} from '@/helpers/time/formatDate'
@ -209,7 +199,6 @@ import {objectToSnakeCase} from '@/helpers/case'
import UserService from '@/services/user'
import ProjectService from '@/services/project'
import NamespaceService from '@/services/namespace'
// FIXME: do not use this here for now. instead create new version from DEFAULT_PARAMS
import {getDefaultParams} from '@/composables/useTaskList'
@ -240,7 +229,6 @@ const DEFAULT_FILTERS = {
assignees: '',
labels: '',
project_id: '',
namespace: '',
} as const
const props = defineProps({
@ -265,23 +253,20 @@ const filters = ref({...DEFAULT_FILTERS})
const services = {
users: shallowReactive(new UserService()),
projects: shallowReactive(new ProjectService()),
namespace: shallowReactive(new NamespaceService()),
}
interface Entities {
users: IUser[]
labels: ILabel[]
projects: IProject[]
namespace: INamespace[]
}
type EntityType = 'users' | 'labels' | 'projects' | 'namespace'
type EntityType = 'users' | 'labels' | 'projects'
const entities: Entities = reactive({
users: [],
labels: [],
projects: [],
namespace: [],
})
onMounted(() => {
@ -327,8 +312,7 @@ function prepareFilters() {
prepareSingleValue('percent_done', 'percentDone', 'usePercentDone', true)
prepareDate('reminders')
prepareRelatedObjectFilter('users', 'assignees')
prepareRelatedObjectFilter('projects', 'project_id')
prepareRelatedObjectFilter('namespace')
prepareProjectsFilter()
prepareSingleValue('labels')
@ -531,6 +515,11 @@ async function prepareRelatedObjectFilter(kind: EntityType, filterName = null, s
entities[kind] = await services[servicePrefix].getAll({}, {s: filters.value[filterName]})
}
async function prepareProjectsFilter() {
await prepareRelatedObjectFilter('projects', 'project_id')
entities.projects = entities.projects.filter(p => p.id > 0)
}
function setDoneFilter() {
if (filters.value.done) {
removePropertyFromFilter('done')

View File

@ -1,19 +1,19 @@
import {ref, watch, type Ref} from 'vue'
import {ref, watch, type ShallowReactive} from 'vue'
import ProjectService from '@/services/project'
import type {IProject} from '@/modelTypes/IProject'
import {getBlobFromBlurHash} from '@/helpers/getBlobFromBlurHash'
export function useProjectBackground(project: Ref<IProject>) {
export function useProjectBackground(project: ShallowReactive<IProject>) {
const background = ref<string | null>(null)
const backgroundLoading = ref(false)
const blurHashUrl = ref('')
watch(
() => [project.value.id, project.value.backgroundBlurHash] as [IProject['id'], IProject['backgroundBlurHash']],
() => [project.id, project.backgroundBlurHash] as [IProject['id'], IProject['backgroundBlurHash']],
async ([projectId, blurHash], oldValue) => {
if (
project.value === null ||
!project.value.backgroundInformation ||
project === null ||
!project.backgroundInformation ||
backgroundLoading.value
) {
return
@ -36,7 +36,7 @@ export function useProjectBackground(project: Ref<IProject>) {
})
const projectService = new ProjectService()
const backgroundPromise = projectService.background(project.value).then((result) => {
const backgroundPromise = projectService.background(project).then((result) => {
background.value = result
})
await Promise.all([blurHashPromise, backgroundPromise])

View File

@ -72,6 +72,19 @@
@update:model-value="setSubscriptionInStore"
type="dropdown"
/>
<dropdown-item
:to="{ name: 'project.settings.webhooks', params: { projectId: project.id } }"
icon="bolt"
>
{{ $t('project.webhooks.title') }}
</dropdown-item>
<dropdown-item
v-if="level < 2"
:to="{ name: 'project.createFromParent', params: { parentProjectId: project.id } }"
icon="layer-group"
>
{{ $t('menu.createProject') }}
</dropdown-item>
<dropdown-item
:to="{ name: 'project.settings.delete', params: { projectId: project.id } }"
icon="trash-alt"
@ -96,17 +109,18 @@ import type {ISubscription} from '@/modelTypes/ISubscription'
import {isSavedFilter} from '@/services/savedFilter'
import {useConfigStore} from '@/stores/config'
import {useProjectStore} from '@/stores/projects'
import {useNamespaceStore} from '@/stores/namespaces'
const props = defineProps({
project: {
type: Object as PropType<IProject>,
required: true,
},
level: {
type: Number,
},
})
const projectStore = useProjectStore()
const namespaceStore = useNamespaceStore()
const subscription = ref<ISubscription | null>(null)
watchEffect(() => {
subscription.value = props.project.subscription ?? null
@ -122,6 +136,5 @@ function setSubscriptionInStore(sub: ISubscription) {
subscription: sub,
}
projectStore.setProject(updatedProject)
namespaceStore.setProjectInNamespaceById(updatedProject)
}
</script>

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