Compare commits

...

59 Commits

Author SHA1 Message Date
renovate 7b17ccbf1f fix(deps): update sentry-javascript monorepo to v7.8.0 (#2208)
Reviewed-on: vikunja/frontend#2208
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 18:45:49 +00:00
renovate ce7563ea4c fix(deps): update vueuse to v9 (major) (#2209)
Reviewed-on: vikunja/frontend#2209
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 17:39:53 +00:00
renovate d9f3555d8d fix(deps): update dependency vue-router to v4.1.3 (#2206)
Reviewed-on: vikunja/frontend#2206
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 17:14:05 +00:00
renovate b2dd63630c chore(deps): update typescript-eslint monorepo to v5.31.0 (#2207)
Reviewed-on: vikunja/frontend#2207
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 17:12:34 +00:00
renovate db1a41f845 fix(deps): update dependency @kyvg/vue3-notification to v2.3.6 (#2205)
Reviewed-on: vikunja/frontend#2205
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 17:01:26 +00:00
renovate 08ae0046de chore(deps): update dependency vue-tsc to v0.39.4 (#2187)
Reviewed-on: vikunja/frontend#2187
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 16:21:19 +00:00
renovate c173542b23 chore(deps): update dependency rollup to v2.77.2 (#2203)
Reviewed-on: vikunja/frontend#2203
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 16:19:32 +00:00
kolaente 518417c0de
feat: add more testcases for parsing weekdays
Related to vikunja/api#1223
2022-07-31 18:16:31 +02:00
renovate c2e58a2320 chore(deps): update dependency autoprefixer to v10.4.8 (#2202)
Reviewed-on: vikunja/frontend#2202
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 16:09:59 +00:00
renovate d94a25c83f fix(deps): update dependency date-fns to v2.29.1 (#2185)
Reviewed-on: vikunja/frontend#2185
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 13:48:43 +00:00
renovate ad9ca61969 chore(deps): update dependency sass to v1.54.0 (#2186)
Reviewed-on: vikunja/frontend#2186
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 13:48:12 +00:00
renovate d28d9218bd chore(deps): update caniuse-and-related (#2189)
Reviewed-on: vikunja/frontend#2189
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 13:47:43 +00:00
renovate af08713bf0 chore(deps): update dependency netlify-cli to v10.13.0 (#2190)
Reviewed-on: vikunja/frontend#2190
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 13:47:11 +00:00
renovate 0b01f2aace chore(deps): update dependency esbuild to v0.14.51 (#2191)
Reviewed-on: vikunja/frontend#2191
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 13:46:22 +00:00
renovate 579de70a7a chore(deps): update dependency eslint-plugin-vue to v9.3.0 (#2192)
Reviewed-on: vikunja/frontend#2192
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 13:45:46 +00:00
renovate 660ab928a2 chore(deps): update dependency vite to v3.0.4 (#2193)
Reviewed-on: vikunja/frontend#2193
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 13:44:53 +00:00
renovate 9394f57fc9 chore(deps): update font awesome to v6.1.2 (#2198)
Reviewed-on: vikunja/frontend#2198
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-31 13:44:12 +00:00
konrad 873169c371 chore(deps): update dependency vitest to v0.20.2 (#2188)
Reviewed-on: vikunja/frontend#2188
2022-07-31 13:43:34 +00:00
renovate 098fd0a875 chore(deps): update dependency vitest to v0.20.2 2022-07-31 12:02:53 +00:00
drone c0cd69dd82 [skip ci] Updated translations via Crowdin 2022-07-23 00:15:10 +00:00
kolaente 4666087aa9
feat: add issue template
Resolves vikunja/frontend#2177

(cherry picked from commit 5442306b7cf2b1e2e68c57cb9b3a464c31565fe6)
2022-07-21 16:49:21 +02:00
kolaente 56147dc9fb
fix: transition error when deleting a task 2022-07-21 16:14:12 +02:00
kolaente ff48178051
fix: general user settings empty when loading the settings page
Resolves vikunja/frontend#2183
2022-07-21 16:11:45 +02:00
kolaente cb3f269937
fix(kanban): reset loading state after creating a task
Resolves: vikunja/frontend#2184
2022-07-21 15:57:03 +02:00
kolaente 4c560f1a03
fix: don't try to load lists after logging out
Resolves vikunja/frontend#329
2022-07-21 15:53:15 +02:00
renovate 0fe2a16a7c fix(deps): update dependency @github/hotkey to v2.0.1 (#2182)
Reviewed-on: vikunja/frontend#2182
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-21 13:35:54 +00:00
kolaente 9cebf5305a
feat: allow for easy reset of a repeating amount
Resolves vikunja/frontend#2179
2022-07-21 15:10:48 +02:00
kolaente 71c8540c74
fix: don't allow negative repeat amounts
Partial fix for vikunja/frontend#2179
2022-07-21 15:06:17 +02:00
kolaente 8183fce829
fix: user menu dropdown
vikunja/frontend#2178
2022-07-21 14:08:14 +02:00
kolaente 3becf8738b
fix: logo spacing for link shares
Resolves #1142
2022-07-21 01:00:21 +02:00
kolaente 9ddb55a5ef
fix: vuex state mutation error when moving a kanban bucket 2022-07-20 17:18:27 +02:00
kolaente cdb63b578d
chore: use the <dropdown> and <dropdown-item> components everywhere
Resolves vikunja/frontend#2176
2022-07-20 17:08:58 +02:00
renovate d6a10b01dd chore(deps): update dependency vue-tsc to v0.38.9 (#2162)
Reviewed-on: vikunja/frontend#2162
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-20 09:06:08 +00:00
renovate b74c961723 chore(deps): update dependency cypress to v10.3.1 (#2175)
Reviewed-on: vikunja/frontend#2175
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-19 20:24:27 +00:00
kolaente 8b0e88b574
fix: lint 2022-07-19 21:20:44 +02:00
kolaente 175fb02629
fix: don't allow marking a task as done in a read-only list 2022-07-19 18:37:11 +02:00
kolaente dac9d918b5
feat(kanban): show loading indicators when handling tasks 2022-07-19 18:33:45 +02:00
kolaente e7de930129
fix: properly update state when duplicating a list 2022-07-19 17:15:39 +02:00
kolaente a0d0c2cb1f
fix(kanban): error when moving a task to an empty bucket 2022-07-19 17:13:22 +02:00
kolaente a4d3cafdf1
fix: pagination on table view should not open the list view
Resolves vikunja/frontend#2173
2022-07-19 17:11:11 +02:00
kolaente f5bb697032
fix: quick actions arrow key navigation in dark mode 2022-07-19 17:04:35 +02:00
kolaente 62bbffb17e
fix: user avatar settings 2022-07-19 17:00:40 +02:00
kolaente c2d5370e4a
fix: don't use transitions for elements where it is not possible
Resolves vikunja/frontend#2153
2022-07-19 16:56:09 +02:00
kolaente 6dc02c45dd
fix: don't try to pass nonexistant props to filters
Resolves vikunja/frontend#2152
2022-07-19 16:41:55 +02:00
Dominik Pschenitschni 0456f4a041 fix: use of sortable js with transition-group (#2160)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#2160
Reviewed-by: konrad <k@knt.li>
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2022-07-19 14:32:12 +00:00
renovate c1dd20a30b fix(deps): update dependency vue-i18n to v9.2.0-beta.40 (#2172)
Reviewed-on: vikunja/frontend#2172
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-19 14:20:44 +00:00
renovate acc7bf4305 fix(deps): update dependency dompurify to v2.3.10 (#2167)
Reviewed-on: vikunja/frontend#2167
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-18 20:36:42 +00:00
renovate 86688d7e95 chore(deps): update dependency vite to v3.0.2 (#2166)
Reviewed-on: vikunja/frontend#2166
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-18 20:36:11 +00:00
renovate aaee49b70e chore(deps): update typescript-eslint monorepo to v5.30.7 (#2168)
Reviewed-on: vikunja/frontend#2168
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-18 20:35:44 +00:00
renovate 6b23b954a8 chore(deps): update dependency @vitejs/plugin-vue to v3.0.1 (#2147)
Reviewed-on: vikunja/frontend#2147
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-18 20:35:17 +00:00
kolaente 9fd2f4ea5c
fix: datepicker confirm button overflow
Resolves #2165
2022-07-18 15:13:51 +02:00
konrad dca05f852c chore(deps): update dependency vite to v3 (#2149)
Reviewed-on: vikunja/frontend#2149
Reviewed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Reviewed-by: konrad <k@knt.li>
2022-07-18 13:05:36 +00:00
kolaente 5aa6cce185
chore: update lockfile 2022-07-18 12:15:07 +02:00
Dominik Pschenitschni d96ea384dc
fix: upgrade packages for vite 3.0 2022-07-18 12:13:28 +02:00
renovate 5d33144b8e
chore(deps): update dependency vite to v3 2022-07-18 12:13:25 +02:00
renovate d462d56202 chore(deps): update dependency eslint to v8.20.0 (#2159)
Reviewed-on: vikunja/frontend#2159
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-18 10:10:19 +00:00
renovate 2648592ac0 fix(deps): update vueuse to v8.9.4 (#2161)
Reviewed-on: vikunja/frontend#2161
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-18 10:09:49 +00:00
renovate 1ac78729a4 fix(deps): update dependency vue-i18n to v9.2.0-beta.39 (#2163)
Reviewed-on: vikunja/frontend#2163
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-18 10:09:19 +00:00
renovate 9d1195a2ed chore(deps): update dependency happy-dom to v6.0.4 (#2164)
Reviewed-on: vikunja/frontend#2164
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2022-07-18 10:06:05 +00:00
52 changed files with 1110 additions and 700 deletions

44
.gitea/issue_template.md Normal file
View File

@ -0,0 +1,44 @@
<!--
Please fill out this issue template to report a bug.
If you want to propose a new feature, please open a discussion thread in the forum: https://community.vikunja.io
-->
**Version information:**
Frontend Version:
API Version:
Browser and OS Version:
**Steps to reproduce:**
<!--
Add clear steps to reproduce the bug. Provide screenshots where applicable.
-->
1.
2.
...
**Expected behavior:**
<!--
Describe what happened.
-->
**Actual behavior:**
<!--
Describe what happened instead.
-->
**Checklist:**
* [ ] I have provided all required information
* [ ] I am using the latest release or the latest unstable build
* [ ] I was able to reproduce the bug on [try](https://try.vikunja.io)

View File

@ -18,19 +18,19 @@
"browserslist:update": "npx browserslist@latest --update-db"
},
"dependencies": {
"@github/hotkey": "2.0.0",
"@kyvg/vue3-notification": "2.3.5",
"@sentry/tracing": "7.7.0",
"@sentry/vue": "7.7.0",
"@github/hotkey": "2.0.1",
"@kyvg/vue3-notification": "2.3.6",
"@sentry/tracing": "7.8.0",
"@sentry/vue": "7.8.0",
"@types/is-touch-device": "1.0.0",
"@types/sortablejs": "1.13.0",
"@vueuse/core": "8.9.3",
"@vueuse/router": "8.9.3",
"@vueuse/core": "9.0.2",
"@vueuse/router": "9.0.2",
"blurhash": "1.1.5",
"bulma-css-variables": "0.9.33",
"camel-case": "4.1.2",
"date-fns": "2.28.0",
"dompurify": "2.3.9",
"date-fns": "2.29.1",
"dompurify": "2.3.10",
"easymde": "2.16.1",
"flatpickr": "4.6.13",
"flexsearch": "0.7.21",
@ -48,51 +48,51 @@
"vue-advanced-cropper": "2.8.3",
"vue-drag-resize": "2.0.3",
"vue-flatpickr-component": "9.0.6",
"vue-i18n": "9.2.0-beta.38",
"vue-router": "4.1.2",
"vue-i18n": "9.2.0-beta.40",
"vue-router": "4.1.3",
"vuex": "4.0.2",
"workbox-precaching": "6.5.3",
"zhyswan-vuedraggable": "4.1.3"
},
"devDependencies": {
"@4tw/cypress-drag-drop": "2.2.1",
"@cypress/vite-dev-server": "2.2.3",
"@cypress/vue": "3.1.2",
"@cypress/vite-dev-server": "3.0.0",
"@cypress/vue": "4.0.0",
"@faker-js/faker": "7.3.0",
"@fortawesome/fontawesome-svg-core": "6.1.1",
"@fortawesome/free-regular-svg-icons": "6.1.1",
"@fortawesome/free-solid-svg-icons": "6.1.1",
"@fortawesome/fontawesome-svg-core": "6.1.2",
"@fortawesome/free-regular-svg-icons": "6.1.2",
"@fortawesome/free-solid-svg-icons": "6.1.2",
"@fortawesome/vue-fontawesome": "3.0.1",
"@types/flexsearch": "0.7.3",
"@typescript-eslint/eslint-plugin": "5.30.6",
"@typescript-eslint/parser": "5.30.6",
"@vitejs/plugin-legacy": "1.8.2",
"@vitejs/plugin-vue": "2.3.3",
"@typescript-eslint/eslint-plugin": "5.31.0",
"@typescript-eslint/parser": "5.31.0",
"@vitejs/plugin-legacy": "2.0.0",
"@vitejs/plugin-vue": "3.0.1",
"@vue/eslint-config-typescript": "11.0.0",
"@vue/test-utils": "2.0.2",
"@vue/tsconfig": "0.1.3",
"autoprefixer": "10.4.7",
"autoprefixer": "10.4.8",
"axios": "0.27.2",
"browserslist": "4.21.1",
"caniuse-lite": "1.0.30001363",
"cypress": "10.3.0",
"esbuild": "0.14.49",
"eslint": "8.19.0",
"eslint-plugin-vue": "9.2.0",
"browserslist": "4.21.3",
"caniuse-lite": "1.0.30001373",
"cypress": "10.3.1",
"esbuild": "0.14.51",
"eslint": "8.20.0",
"eslint-plugin-vue": "9.3.0",
"express": "4.18.1",
"happy-dom": "6.0.3",
"netlify-cli": "10.9.0",
"happy-dom": "6.0.4",
"netlify-cli": "10.13.0",
"postcss": "8.4.14",
"postcss-preset-env": "7.7.2",
"rollup": "2.77.0",
"rollup": "2.77.2",
"rollup-plugin-visualizer": "5.7.1",
"sass": "1.53.0",
"sass": "1.54.0",
"typescript": "4.7.4",
"vite": "2.9.14",
"vite": "3.0.4",
"vite-plugin-pwa": "0.12.3",
"vite-svg-loader": "3.4.0",
"vitest": "0.18.1",
"vue-tsc": "0.38.7",
"vitest": "0.20.2",
"vue-tsc": "0.39.4",
"wait-on": "6.0.1",
"workbox-cli": "6.5.3"
},

View File

@ -10,9 +10,7 @@
</no-auth-wrapper>
<Notification/>
<transition name="fade">
<keyboard-shortcuts v-if="keyboardShortcutsActive"/>
</transition>
<keyboard-shortcuts v-if="keyboardShortcutsActive"/>
</ready>
</template>

View File

@ -48,44 +48,38 @@
</x-button>
</template>
<BaseButton
<dropdown-item
:to="{name: 'user.settings'}"
class="dropdown-item"
>
{{ $t('user.settings.title') }}
</BaseButton>
<BaseButton
</dropdown-item>
<dropdown-item
v-if="imprintUrl"
:href="imprintUrl"
class="dropdown-item"
>
{{ $t('navigation.imprint') }}
</BaseButton>
<BaseButton
</dropdown-item>
<dropdown-item
v-if="privacyPolicyUrl"
:href="privacyPolicyUrl"
class="dropdown-item"
>
{{ $t('navigation.privacy') }}
</BaseButton>
<BaseButton
</dropdown-item>
<dropdown-item
@click="$store.commit('keyboardShortcutsActive', true)"
class="dropdown-item"
>
{{ $t('keyboardShortcuts.title') }}
</BaseButton>
<BaseButton
</dropdown-item>
<dropdown-item
:to="{name: 'about'}"
class="dropdown-item"
>
{{ $t('about.title') }}
</BaseButton>
<BaseButton
</dropdown-item>
<dropdown-item
@click="logout()"
class="dropdown-item"
>
{{ $t('user.auth.logout') }}
</BaseButton>
</dropdown-item>
</dropdown>
</div>
</div>
@ -103,6 +97,7 @@ import Rights from '@/models/constants/rights.json'
import Update from '@/components/home/update.vue'
import ListSettingsDropdown from '@/components/list/list-settings-dropdown.vue'
import Dropdown from '@/components/misc/dropdown.vue'
import DropdownItem from '@/components/misc/dropdown-item.vue'
import Notifications from '@/components/notifications/notifications.vue'
import Logo from '@/components/home/Logo.vue'
import BaseButton from '@/components/base/BaseButton.vue'

View File

@ -38,16 +38,14 @@
</keep-alive>
</router-view>
<transition name="modal">
<modal
v-if="currentModal"
@close="closeModal()"
variant="scrolling"
class="task-detail-view-modal"
>
<component :is="currentModal"/>
</modal>
</transition>
<modal
v-if="currentModal"
@close="closeModal()"
variant="scrolling"
class="task-detail-view-modal"
>
<component :is="currentModal"/>
</modal>
<BaseButton
class="keyboard-shortcuts-button d-print-none"

View File

@ -6,7 +6,7 @@
>
<div class="container has-text-centered link-share-view">
<div class="column is-10 is-offset-1">
<Logo class="logo" />
<Logo class="logo"/>
<h1
:style="{ 'opacity': currentList.title === '' ? '0': '1' }"
class="title">
@ -14,7 +14,7 @@
</h1>
<div class="box has-text-left view">
<router-view/>
<PoweredByLink />
<PoweredByLink/>
</div>
</div>
</div>
@ -35,14 +35,15 @@ const background = computed(() => store.state.background)
<style lang="scss" scoped>
.link-share-container.has-background .view {
background-color: transparent;
border: none;
background-color: transparent;
border: none;
}
.logo {
max-width: 300px;
width: 90%;
margin: 2rem 0 1.5rem;
height: 100px;
}
.column {
@ -55,6 +56,6 @@ const background = computed(() => store.state.background)
// FIXME: this should be defined somewhere deep
.link-share-view .card {
background-color: var(--white);
background-color: var(--white);
}
</style>

View File

@ -88,13 +88,12 @@
@end="saveListPosition"
handle=".handle"
:disabled="n.id < 0 || undefined"
tag="transition-group"
tag="ul"
item-key="id"
:data-namespace-id="n.id"
:data-namespace-index="nk"
:component-data="{
type: 'transition',
tag: 'ul',
type: 'transition-group',
name: !drag ? 'flip-list' : null,
class: [
'menu-list can-be-hidden',
@ -555,8 +554,4 @@ $vikunja-nav-selected-width: 0.4rem;
.namespaces-list.loader-container.is-loading {
min-height: calc(100vh - #{$navbar-height + 1.5rem + 1rem + 1.5rem});
}
a.dropdown-item:hover {
background: var(--dropdown-item-hover-background-color) !important;
}
</style>

View File

@ -76,7 +76,7 @@
/>
<x-button
class="datepicker__close-button is-fullwidth"
class="datepicker__close-button"
:shadow="false"
@click="close"
v-cy="'closeDatepicker'"

View File

@ -56,12 +56,13 @@
{{ $t('menu.archive') }}
</dropdown-item>
<task-subscription
class="dropdown-item has-no-shadow"
class="has-no-shadow"
:is-button="false"
entity="list"
:entity-id="list.id"
:subscription="list.subscription"
@change="sub => subscription = sub"
type="dropdown"
/>
<dropdown-item
:to="{ name: 'list.settings.delete', params: { listId: list.id } }"

View File

@ -25,7 +25,6 @@
v-model="value"
ref="filters"
class="filter-popup"
:class="{'is-open': isOpen}"
/>
</modal>
</template>

View File

@ -1,25 +1,95 @@
<template>
<router-link
<component
:is="componentNodeName"
v-bind="elementBindings"
:to="to"
class="dropdown-item">
<span class="icon" v-if="icon !== ''">
<span class="icon" v-if="icon">
<icon :icon="icon"/>
</span>
<span>
<slot></slot>
</span>
</router-link>
</component>
</template>
<script lang="ts" setup>
defineProps({
to: {
required: true,
},
icon: {
type: String,
required: false,
default: '',
},
import {ref, useAttrs, watchEffect} from 'vue'
const props = defineProps<{
to?: object,
icon?: string | string[],
}>()
const componentNodeName = ref<Node['nodeName']>('a')
const elementBindings = ref({})
const attrs = useAttrs()
watchEffect(() => {
let nodeName = 'a'
if (props.to) {
nodeName = 'router-link'
}
if ('href' in attrs) {
nodeName = 'BaseButton'
}
componentNodeName.value = nodeName
elementBindings.value = {
...attrs,
}
})
</script>
<style scoped lang="scss">
.dropdown-item {
color: var(--text);
display: block;
font-size: 0.875rem;
line-height: 1.5;
padding: $item-padding;
position: relative;
}
a.dropdown-item,
button.dropdown-item {
text-align: inherit;
white-space: nowrap;
width: 100%;
display: flex;
align-items: center;
justify-content: left !important;
&:hover {
background-color: var(--grey-100) !important;
}
&.is-active {
background-color: var(--link);
color: var(--link-invert);
}
.icon {
padding-right: .5rem;
}
.icon:not(.has-text-success) {
color: var(--grey-300) !important;
}
&.has-text-danger .icon {
color: var(--danger) !important;
}
&.is-disabled {
cursor: not-allowed;
&:hover {
background-color: transparent;
}
}
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<div class="dropdown is-right is-active" ref="dropdown">
<div class="dropdown" ref="dropdown">
<slot name="trigger" :close="close" :toggleOpen="toggleOpen">
<BaseButton class="dropdown-trigger is-flex" @click="toggleOpen">
<icon :icon="triggerIcon" class="icon"/>
@ -51,7 +51,36 @@ onClickOutside(dropdown, (e: Event) => {
</script>
<style lang="scss" scoped>
.dropdown-menu .dropdown-content {
.dropdown {
display: inline-flex;
position: relative;
vertical-align: top;
}
.dropdown-menu {
min-width: 12rem;
padding-top: 4px;
position: absolute;
top: 100%;
z-index: 20;
display: block;
left: auto;
right: 0;
}
.dropdown-content {
background-color: var(--scheme-main);
border-radius: $radius;
padding-bottom: .5rem;
padding-top: .5rem;
box-shadow: var(--shadow-md);
}
</style>
.dropdown-divider {
background-color: var(--border-light);
border: none;
display: block;
height: 1px;
margin: 0.5rem 0;
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<Teleport to="body">
<!-- FIXME: transition should not be included in the modal -->
<transition name="modal">
<transition :name="transitionName">
<section
v-if="enabled"
class="modal-mask"

View File

@ -69,9 +69,9 @@ function createPagination(totalPages: number, currentPage: number) {
return pages
}
function getRouteForPagination(page = 1, type = 'list') {
function getRouteForPagination(page = 1, type = null) {
return {
name: 'list.' + type,
name: type,
params: {
type: type,
},

View File

@ -1,6 +1,6 @@
<template>
<x-button
v-if="isButton"
v-if="type === 'button'"
variant="secondary"
:icon="iconName"
v-tooltip="tooltipText"
@ -9,6 +9,15 @@
>
{{ buttonText }}
</x-button>
<DropdownItem
v-else-if="type === 'dropdown'"
v-tooltip="tooltipText"
@click="changeSubscription"
:class="{'is-disabled': disabled}"
:icon="iconName"
>
{{ buttonText }}
</DropdownItem>
<BaseButton
v-else
v-tooltip="tooltipText"
@ -27,6 +36,7 @@ import {computed, shallowRef} from 'vue'
import {useI18n} from 'vue-i18n'
import BaseButton from '@/components/base/BaseButton.vue'
import DropdownItem from '@/components/misc/dropdown-item.vue'
import SubscriptionService from '@/services/subscription'
import SubscriptionModel from '@/models/subscription'
@ -34,15 +44,15 @@ import SubscriptionModel from '@/models/subscription'
import {success} from '@/message'
interface Props {
entity: string
entityId: number
subscription: SubscriptionModel | null
isButton?: boolean
entity: string
entityId: number
subscription: SubscriptionModel | null
type?: 'button' | 'dropdown' | null
}
const props = withDefaults(defineProps<Props>(), {
isButton: true,
subscription: null,
type: 'button',
})
const subscriptionEntity = computed<string | null>(() => props.subscription?.entity ?? null)

View File

@ -34,12 +34,13 @@
{{ $t('menu.archive') }}
</dropdown-item>
<task-subscription
class="dropdown-item has-no-shadow"
class="has-no-shadow"
:is-button="false"
entity="namespace"
:entity-id="namespace.id"
:subscription="subscription"
@change="sub => subscription = sub"
type="dropdown"
/>
<dropdown-item
:to="{ name: 'namespace.settings.delete', params: { id: namespace.id } }"

View File

@ -526,7 +526,7 @@ export default defineComponent({
.result {
&-title {
background: var(--grey-50);
background: var(--grey-100);
padding: .5rem;
display: block;
font-size: .75rem;
@ -549,7 +549,7 @@ export default defineComponent({
cursor: pointer;
&:focus, &:hover {
background: var(--grey-50);
background: var(--grey-100);
box-shadow: none !important;
}

View File

@ -50,7 +50,6 @@ const props = defineProps({
for (const e of prop) {
if (!isDate(e) && !isString(e)) {
console.log('validation failed', e, e instanceof Date)
return false
}
}

View File

@ -1,9 +1,15 @@
<template>
<div class="control repeat-after-input">
<div class="buttons has-addons is-centered mt-2">
<x-button variant="secondary" class="is-small" @click="() => setRepeatAfter(1, 'days')">{{ $t('task.repeat.everyDay') }}</x-button>
<x-button variant="secondary" class="is-small" @click="() => setRepeatAfter(1, 'weeks')">{{ $t('task.repeat.everyWeek') }}</x-button>
<x-button variant="secondary" class="is-small" @click="() => setRepeatAfter(1, 'months')">{{ $t('task.repeat.everyMonth') }}</x-button>
<x-button variant="secondary" class="is-small" @click="() => setRepeatAfter(1, 'days')">
{{ $t('task.repeat.everyDay') }}
</x-button>
<x-button variant="secondary" class="is-small" @click="() => setRepeatAfter(1, 'weeks')">
{{ $t('task.repeat.everyWeek') }}
</x-button>
<x-button variant="secondary" class="is-small" @click="() => setRepeatAfter(1, 'months')">
{{ $t('task.repeat.everyMonth') }}
</x-button>
</div>
<div class="is-flex is-align-items-center mb-2">
<label for="repeatMode" class="is-fullwidth">
@ -14,7 +20,10 @@
<select @change="updateData" v-model="task.repeatMode" id="repeatMode">
<option :value="repeatModes.REPEAT_MODE_DEFAULT">{{ $t('misc.default') }}</option>
<option :value="repeatModes.REPEAT_MODE_MONTH">{{ $t('task.repeat.monthly') }}</option>
<option :value="repeatModes.REPEAT_MODE_FROM_CURRENT_DATE">{{ $t('task.repeat.fromCurrentDate') }}</option>
<option :value="repeatModes.REPEAT_MODE_FROM_CURRENT_DATE">{{
$t('task.repeat.fromCurrentDate')
}}
</option>
</select>
</div>
</div>
@ -32,6 +41,7 @@
:placeholder="$t('task.repeat.specifyAmount')"
v-model="repeatAfter.amount"
type="number"
min="0"
/>
</div>
<div class="control">
@ -56,8 +66,10 @@
<script setup lang="ts">
import {ref, reactive, watch} from 'vue'
import repeatModes from '@/models/constants/taskRepeatModes'
import repeatModes from '@/models/constants/taskRepeatModes.json'
import TaskModel from '@/models/task'
import {error} from '@/message'
import {useI18n} from 'vue-i18n'
const props = defineProps({
modelValue: {
@ -70,6 +82,8 @@ const props = defineProps({
},
})
const {t} = useI18n()
const emit = defineEmits(['update:modelValue', 'change'])
const task = ref<TaskModel>()
@ -93,14 +107,19 @@ function updateData() {
if (task.value.repeatMode !== repeatModes.REPEAT_MODE_DEFAULT && repeatAfter.amount === 0) {
return
}
if (repeatAfter.amount < 0) {
error({message: t('task.repeat.invalidAmount')})
return
}
Object.assign(task.value.repeatAfter, repeatAfter)
emit('update:modelValue', task.value)
emit('change')
}
function setRepeatAfter(amount: number, type) {
Object.assign(repeatAfter, { amount, type})
Object.assign(repeatAfter, {amount, type})
updateData()
}
</script>

View File

@ -0,0 +1,6 @@
// https://stackoverflow.com/a/32108184/10924593
export function objectIsEmpty(obj: any): boolean {
return obj
&& Object.keys(obj).length === 0
&& Object.getPrototypeOf(obj) === Object.prototype
}

View File

@ -780,7 +780,8 @@
"days": "Days",
"weeks": "Weeks",
"months": "Months",
"years": "Years"
"years": "Years",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "You can use Quick Add Magic",

View File

@ -780,7 +780,8 @@
"days": "Dnů",
"weeks": "Týdny",
"months": "Měsíce",
"years": "Roky"
"years": "Roky",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "Můžeš použít Kouzelné rychlé přidání",

View File

@ -780,7 +780,8 @@
"days": "Tage",
"weeks": "Wochen",
"months": "Monate",
"years": "Jahre"
"years": "Jahre",
"invalidAmount": "Bitte mehr als 0 eingeben."
},
"quickAddMagic": {
"hint": "Du kannst Quick Add Magic verwenden",

View File

@ -780,7 +780,8 @@
"days": "Tääg",
"weeks": "Wuchä",
"months": "Monet",
"years": "Jahr"
"years": "Jahr",
"invalidAmount": "Bitte mehr als 0 eingeben."
},
"quickAddMagic": {
"hint": "Du chasch Quick Add Magic verwendä",

View File

@ -783,7 +783,8 @@
"days": "Days",
"weeks": "Weeks",
"months": "Months",
"years": "Years"
"years": "Years",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "You can use Quick Add Magic",

View File

@ -780,7 +780,8 @@
"days": "Days",
"weeks": "Weeks",
"months": "Months",
"years": "Years"
"years": "Years",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "You can use Quick Add Magic",

View File

@ -780,7 +780,8 @@
"days": "Jours",
"weeks": "Semaines",
"months": "Mois",
"years": "Années"
"years": "Années",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "Tu peux utiliser Quick Add Magic",

View File

@ -780,7 +780,8 @@
"days": "Giorni",
"weeks": "Settimane",
"months": "Mesi",
"years": "Anni"
"years": "Anni",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "Puoi usare l'Aggiunta Rapida Magica",

View File

@ -780,7 +780,8 @@
"days": "Dagen",
"weeks": "Weken",
"months": "Maanden",
"years": "Jaren"
"years": "Jaren",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "You can use Quick Add Magic",

View File

@ -780,7 +780,8 @@
"days": "Dni",
"weeks": "Tygodnie",
"months": "Miesiące",
"years": "Lata"
"years": "Lata",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "Możesz użyć Quick Add Magic",

View File

@ -780,7 +780,8 @@
"days": "Days",
"weeks": "Weeks",
"months": "Months",
"years": "Years"
"years": "Years",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "You can use Quick Add Magic",

View File

@ -780,7 +780,8 @@
"days": "Dias",
"weeks": "Semanas",
"months": "Meses",
"years": "Anos"
"years": "Anos",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "Podes utilizar a Introdução Mágica Rápida",

View File

@ -780,7 +780,8 @@
"days": "Days",
"weeks": "Weeks",
"months": "Months",
"years": "Years"
"years": "Years",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "You can use Quick Add Magic",

View File

@ -780,7 +780,8 @@
"days": "Дней",
"weeks": "Недель",
"months": "Месяцев",
"years": "Лет"
"years": "Лет",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "Вы можете использовать Волшебное Быстрое Добавление",

View File

@ -780,7 +780,8 @@
"days": "Days",
"weeks": "Weeks",
"months": "Months",
"years": "Years"
"years": "Years",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "You can use Quick Add Magic",

View File

@ -780,7 +780,8 @@
"days": "Days",
"weeks": "Weeks",
"months": "Months",
"years": "Years"
"years": "Years",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "You can use Quick Add Magic",

View File

@ -780,7 +780,8 @@
"days": "Days",
"weeks": "Weeks",
"months": "Months",
"years": "Years"
"years": "Years",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "You can use Quick Add Magic",

View File

@ -780,7 +780,8 @@
"days": "Ngày",
"weeks": "Tuần",
"months": "Tháng",
"years": "Năm"
"years": "Năm",
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "Bạn có thể sử dụng Quick Add Magic",

View File

@ -314,6 +314,42 @@ describe('Parse Task Text', () => {
expect(result.text).toBe('Lorem Ipsum github')
expect(result.date).toBeNull()
})
describe('Should not recognize weekdays in words', () => {
const cases = [
'renewed',
'github',
'fix monitor stand',
'order wedding cake',
'investigate thumping noise',
'iron frilly napkins',
'take photo of saturn',
'fix sunglasses',
'monitor blood pressure',
'Monitor blood pressure',
'buy almonds',
]
cases.forEach(c => {
it(`should not recognize text with ${c} at the beginning as weekday`, () => {
const result = parseTaskText(`${c} dolor sit amet`)
expect(result.text).toBe(`${c} dolor sit amet`)
expect(result.date).toBeNull()
})
it(`should not recognize text with ${c} at the end as weekday`, () => {
const result = parseTaskText(`Lorem Ipsum ${c}`)
expect(result.text).toBe(`Lorem Ipsum ${c}`)
expect(result.date).toBeNull()
})
it(`should not recognize text with ${c} as weekday`, () => {
const result = parseTaskText(`Lorem Ipsum ${c} dolor`)
expect(result.text).toBe(`Lorem Ipsum ${c} dolor`)
expect(result.date).toBeNull()
})
})
})
it('should not recognize date number with no spacing around them', () => {
const result = parseTaskText('Lorem Ispum v1.1.1')

View File

@ -318,7 +318,7 @@ export default {
const oldBucket = cloneDeep(ctx.state.buckets[bucketIndex])
const updatedBucket = {
...ctx.state.buckets[bucketIndex],
...oldBucket,
...updatedBucketData,
}

View File

@ -16,7 +16,6 @@ import LabelTask from '@/models/labelTask'
import LabelModel from '@/models/label'
import UserService from '@/services/user'
// IDEA: maybe use a small fuzzy search here to prevent errors
function findPropertyByValue(object, key, value) {
return Object.values(object).find(
@ -286,12 +285,13 @@ export default {
return foundListId
},
async createNewTask({dispatch}, {
async createNewTask({dispatch, commit}, {
title,
bucketId,
listId,
position,
}) {
const cancel = setLoading({commit}, 'tasks')
const parsedTask = parseTaskText(title, getQuickAddMagicMode())
const foundListId = await dispatch('findListId', {
@ -321,10 +321,12 @@ export default {
const taskService = new TaskService()
const createdTask = await taskService.create(task)
return dispatch('addLabelsToTask', {
const result = await dispatch('addLabelsToTask', {
task: createdTask,
parsedLabels: parsedTask.labels,
})
cancel()
return result
},
},
}

View File

@ -24,9 +24,6 @@ $vikunja-font: 'Quicksand', sans-serif;
$pagination-current-border: var(--primary);
$navbar-item-active-color: var(--primary);
$dropdown-content-shadow: none;
$dropdown-item-hover-background-color: var(--grey-100);
$site-background: var(--grey-100);
$transition-duration: 150ms;

View File

@ -44,7 +44,7 @@
// imports from "bulma-css-variables/sass/components/_all";
// @import "bulma-css-variables/sass/components/breadcrumb"; // not used
@import "bulma-css-variables/sass/components/card";
@import "bulma-css-variables/sass/components/dropdown";
// @import "bulma-css-variables/sass/components/dropdown"; // moved to component
// @import "bulma-css-variables/sass/components/level"; // not used
@import "bulma-css-variables/sass/components/media";
@import "bulma-css-variables/sass/components/menu";

View File

@ -91,34 +91,6 @@ button.table {
margin-bottom: 0 !important;
}
// FIXME: merge with dropdown-item.vue
// for this to happen the component has to be used everywhere
.dropdown-item {
display: flex;
align-items: center;
justify-content: left !important;
.icon {
padding-right: .5rem;
}
.icon:not(.has-text-success) {
color: var(--grey-300) !important;
}
&.has-text-danger .icon {
color: var(--danger) !important;
}
&.is-disabled {
cursor: not-allowed;
&:hover {
background-color: transparent;
}
}
}
.is-max-width-desktop {
width: 100%;
max-width: $desktop;

View File

@ -76,12 +76,16 @@ const welcome = useDateTimeSalutation()
const store = useStore()
const listHistory = computed(() => {
// If we don't check this, it tries to load the list background right after logging out
if(!store.state.auth.authenticated) {
return []
}
return getHistory()
.map(l => store.getters['lists/getListById'](l.id))
.filter(l => l !== null)
})
const migratorsEnabled = computed(() => store.state.config.availableMigrators?.length > 0)
const userInfo = computed(() => store.state.auth.info)
const hasTasks = computed(() => store.state.hasTasks)

View File

@ -25,7 +25,7 @@
@start="() => dragBucket = true"
group="buckets"
:disabled="!canWrite"
tag="transition-group"
tag="ul"
:item-key="({id}) => `bucket${id}`"
:component-data="bucketDraggableComponentData"
>
@ -62,9 +62,8 @@
trigger-icon="ellipsis-v"
@close="() => showSetLimitInput = false"
>
<a
<dropdown-item
@click.stop="showSetLimitInput = true"
class="dropdown-item"
>
<div class="field has-addons" v-if="showSetLimitInput">
<div class="control">
@ -93,34 +92,32 @@
$t('list.kanban.limit', {limit: bucket.limit > 0 ? bucket.limit : $t('list.kanban.noLimit')})
}}
</template>
</a>
<a
</dropdown-item>
<dropdown-item
@click.stop="toggleDoneBucket(bucket)"
class="dropdown-item"
v-tooltip="$t('list.kanban.doneBucketHintExtended')"
>
<span class="icon is-small" :class="{'has-text-success': bucket.isDoneBucket}">
<icon icon="check-double"/>
</span>
{{ $t('list.kanban.doneBucket') }}
</a>
<a
class="dropdown-item"
</dropdown-item>
<dropdown-item
@click.stop="() => collapseBucket(bucket)"
>
{{ $t('list.kanban.collapse') }}
</a>
<a
</dropdown-item>
<dropdown-item
:class="{'is-disabled': buckets.length <= 1}"
@click.stop="() => deleteBucketModal(bucket.id)"
class="dropdown-item has-text-danger"
class="has-text-danger"
v-tooltip="buckets.length <= 1 ? $t('list.kanban.deleteLast') : ''"
>
<span class="icon is-small">
<icon icon="trash-alt"/>
</span>
{{ $t('misc.delete') }}
</a>
</dropdown-item>
</dropdown>
</div>
@ -133,17 +130,17 @@
:group="{name: 'tasks', put: shouldAcceptDrop(bucket) && !dragBucket}"
:disabled="!canWrite"
:data-bucket-index="bucketIndex"
tag="transition-group"
tag="ul"
:item-key="(task) => `bucket${bucket.id}-task${task.id}`"
:component-data="getTaskDraggableTaskComponentData(bucket)"
>
<template #footer>
<div class="bucket-footer" v-if="canWrite">
<div class="field" v-if="showNewTaskInput[bucket.id]">
<div class="control" :class="{'is-loading': loading}">
<div class="control" :class="{'is-loading': loading || taskLoading}">
<input
class="input"
:disabled="loading || undefined"
:disabled="loading || taskLoading || undefined"
@focusout="toggleShowNewTaskInput(bucket.id)"
@keyup.enter="addTaskToBucket(bucket.id)"
@keyup.esc="toggleShowNewTaskInput(bucket.id)"
@ -172,7 +169,7 @@
<template #item="{element: task}">
<div class="task-item">
<kanban-card class="kanban-card" :task="task"/>
<kanban-card class="kanban-card" :task="task" :loading="taskUpdating[task.id] ?? false"/>
</div>
</template>
</draggable>
@ -241,6 +238,7 @@ import Dropdown from '@/components/misc/dropdown.vue'
import {getCollapsedBucketState, saveCollapsedBucketState} from '@/helpers/saveCollapsedBucketState'
import {calculateItemPosition} from '../../helpers/calculateItemPosition'
import KanbanCard from '@/components/tasks/partials/kanban-card.vue'
import DropdownItem from '@/components/misc/dropdown-item.vue'
const DRAG_OPTIONS = {
// sortable options
@ -256,6 +254,7 @@ const MIN_SCROLL_HEIGHT_PERCENT = 0.25
export default defineComponent({
name: 'Kanban',
components: {
DropdownItem,
ListWrapper,
KanbanCard,
Dropdown,
@ -316,8 +315,7 @@ export default defineComponent({
return (bucket) => ({
ref: (el) => this.setTaskContainerRef(bucket.id, el),
onScroll: (event) => this.handleTaskContainerScroll(bucket.id, bucket.listId, event.target),
type: 'transition',
tag: 'div',
type: 'transition-group',
name: !this.drag ? 'move-card' : null,
class: [
'tasks',
@ -337,8 +335,7 @@ export default defineComponent({
},
bucketDraggableComponentData() {
return {
type: 'transition',
tag: 'div',
type: 'transition-group',
name: !this.dragBucket ? 'move-bucket' : null,
class: [
'kanban-bucket-container',
@ -426,6 +423,7 @@ export default defineComponent({
const task = newBucket.tasks[newTaskIndex]
const taskBefore = newBucket.tasks[newTaskIndex - 1] ?? null
const taskAfter = newBucket.tasks[newTaskIndex + 1] ?? null
this.taskUpdating[task.id] = true
const newTask = cloneDeep(task) // cloning the task to avoid vuex store mutations
newTask.bucketId = newBucket.id
@ -438,8 +436,7 @@ export default defineComponent({
await this.$store.dispatch('tasks/update', newTask)
// Make sure the first and second task don't both get position 0 assigned
if(newTaskIndex === 0 && taskAfter.kanbanPosition === 0) {
console.log('first', taskAfter.id, taskAfter.kanbanPosition)
if(newTaskIndex === 0 && taskAfter !== null && taskAfter.kanbanPosition === 0) {
const taskAfterAfter = newBucket.tasks[newTaskIndex + 2] ?? null
const newTaskAfter = cloneDeep(taskAfter) // cloning the task to avoid vuex store mutations
newTaskAfter.bucketId = newBucket.id
@ -466,7 +463,7 @@ export default defineComponent({
return
}
this.newTaskError[bucketId] = false
const task = await this.$store.dispatch('tasks/createNewTask', {
title: this.newTaskText,
bucketId,
@ -730,10 +727,6 @@ $filter-container-height: '1rem - #{$switch-view-height}';
}
}
a.dropdown-item {
padding-right: 1rem;
}
&.is-collapsed {
align-self: flex-start;
transform: rotate(90deg) translateY(-100%);

View File

@ -84,14 +84,17 @@
handle=".handle"
:disabled="!canWrite"
item-key="id"
tag="ul"
:component-data="{
class: { 'dragging-disabled': !canWrite || isAlphabeticalSorting },
type: 'transition-group'
}"
>
<template #item="{element: t}">
<single-task-in-list
:show-list-color="false"
:disabled="!canWrite"
:can-mark-as-done="canWrite || (list.isSavedFilter && list.isSavedFilter())"
:the-task="t"
@taskUpdated="updateTasks"
>

View File

@ -21,9 +21,9 @@
<script setup lang="ts">
import {ref, shallowReactive} from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { useI18n } from 'vue-i18n'
import {useRoute, useRouter} from 'vue-router'
import {useStore} from 'vuex'
import {useI18n} from 'vue-i18n'
import ListDuplicateService from '@/services/listDuplicateService'
import CreateEdit from '@/components/misc/create-edit.vue'
@ -32,29 +32,30 @@ import Multiselect from '@/components/input/multiselect.vue'
import ListDuplicateModel from '@/models/listDuplicateModel'
import NamespaceModel from '@/models/namespace'
import { success } from '@/message'
import { useTitle } from '@/composables/useTitle'
import { useNameSpaceSearch } from '@/composables/useNamespaceSearch'
import {success} from '@/message'
import {useTitle} from '@/composables/useTitle'
import {useNameSpaceSearch} from '@/composables/useNamespaceSearch'
const { t } = useI18n({useScope: 'global'})
const {t} = useI18n({useScope: 'global'})
useTitle(() => t('list.duplicate.title'))
const {
namespaces,
findNamespaces,
} = useNameSpaceSearch()
const selectedNamespace = ref<NamespaceModel>()
function selectNamespace(namespace: NamespaceModel) {
selectedNamespace.value = namespace
}
const route = useRoute()
const router= useRouter()
const router = useRouter()
const store = useStore()
const listDuplicateService = shallowReactive(new ListDuplicateService())
async function duplicateList() {
const listDuplicate = new ListDuplicateModel({
// FIXME: should be parameter
@ -64,7 +65,6 @@ async function duplicateList() {
const duplicate = await listDuplicateService.create(listDuplicate)
const store = useStore()
store.commit('namespaces/addListToNamespace', duplicate.list)
store.commit('lists/setList', duplicate.list)
success({message: t('list.duplicate.success')})

View File

@ -154,9 +154,19 @@
<transition name="flash-background" appear>
<div class="column" v-if="activeFields.repeatAfter">
<!-- Repeat after -->
<div class="detail-title">
<icon icon="history"/>
{{ $t('task.attributes.repeat') }}
<div class="is-flex is-justify-content-space-between">
<div class="detail-title">
<icon icon="history"/>
{{ $t('task.attributes.repeat') }}
</div>
<BaseButton
@click="() => {task.repeatAfter.amount = 0;saveTask()}"
v-if="canWrite"
class="remove">
<span class="icon is-small">
<icon icon="times"></icon>
</span>
</BaseButton>
</div>
<repeat-after
:disabled="!canWrite"
@ -396,20 +406,18 @@
<created-updated :task="task" v-if="!canWrite && !shouldShowClosePopup"/>
</div>
<transition name="modal">
<modal
@close="showDeleteModal = false"
@submit="deleteTask()"
v-if="showDeleteModal"
>
<template #header><span>{{ $t('task.detail.delete.header') }}</span></template>
<modal
@close="showDeleteModal = false"
@submit="deleteTask()"
v-if="showDeleteModal"
>
<template #header><span>{{ $t('task.detail.delete.header') }}</span></template>
<template #text>
<p>{{ $t('task.detail.delete.text1') }}<br/>
{{ $t('task.detail.delete.text2') }}</p>
</template>
</modal>
</transition>
<template #text>
<p>{{ $t('task.detail.delete.text1') }}<br/>
{{ $t('task.detail.delete.text2') }}</p>
</template>
</modal>
</div>
</template>
@ -770,13 +778,13 @@ $flash-background-duration: 750ms;
.date-input {
display: flex;
align-items: center;
}
.remove {
color: var(--danger);
vertical-align: middle;
padding-left: .5rem;
line-height: 1;
}
.remove {
color: var(--danger);
vertical-align: middle;
padding-left: .5rem;
line-height: 1;
}
:deep(.datepicker) {

View File

@ -66,7 +66,7 @@ export default defineComponent({
</script>
<script setup lang="ts">
import {ref, shallowReactive} from 'vue'
import {computed, ref, shallowReactive} from 'vue'
import {useI18n} from 'vue-i18n'
import {useStore} from 'vuex'
import {Cropper} from 'vue-advanced-cropper'
@ -80,13 +80,13 @@ import { success } from '@/message'
const {t} = useI18n({useScope: 'global'})
const store = useStore()
const AVATAR_PROVIDERS = {
const AVATAR_PROVIDERS = computed(() => ({
default: t('misc.default'),
initials: t('user.settings.avatar.initials'),
gravatar: t('user.settings.avatar.gravatar'),
marble: t('user.settings.avatar.marble'),
upload: t('user.settings.avatar.upload'),
}
}))
useTitle(() => `${t('user.settings.avatar.title')} - ${t('user.settings.title')}`)

View File

@ -85,7 +85,8 @@
v-for="lang in availableLanguageOptions"
:key="lang.code"
:value="lang.code"
>{{ lang.title }}</option>
>{{ lang.title }}
</option>
</select>
</div>
</label>
@ -154,7 +155,7 @@ export default defineComponent({
</script>
<script setup lang="ts">
import {computed, watch, ref, reactive} from 'vue'
import {computed, watch, ref} from 'vue'
import {useI18n} from 'vue-i18n'
import {useStore} from 'vuex'
@ -170,7 +171,8 @@ import {success} from '@/message'
import {AuthenticatedHTTPFactory} from '@/http-common'
import {useColorScheme} from '@/composables/useColorScheme'
import { useTitle } from '@/composables/useTitle'
import {useTitle} from '@/composables/useTitle'
import {objectIsEmpty} from '@/helpers/objectIsEmpty'
const {t} = useI18n({useScope: 'global'})
useTitle(() => `${t('user.settings.general.title')} - ${t('user.settings.title')}`)
@ -213,6 +215,7 @@ function useAvailableTimezones() {
return availableTimezones
}
const availableTimezones = useAvailableTimezones()
function getPlaySoundWhenDoneSetting() {
@ -223,7 +226,7 @@ const playSoundWhenDone = ref(getPlaySoundWhenDoneSetting())
const quickAddMagicMode = ref(getQuickAddMagicMode())
const store = useStore()
const settings = reactive({...store.state.auth.settings})
const settings = ref({...store.state.auth.settings})
const id = ref(createRandomID())
const availableLanguageOptions = ref(
Object.entries(availableLanguages)
@ -231,10 +234,22 @@ const availableLanguageOptions = ref(
.sort((a, b) => a.title.localeCompare(b.title)),
)
watch(
() => store.state.auth.settings,
() => {
// Only setting if we don't have values set yet to avoid overriding edited values
if (!objectIsEmpty(settings.value)) {
return
}
settings.value = {...store.state.auth.settings}
},
{immediate: true},
)
const defaultList = computed({
get: () => store.getters['lists/getListById'](settings.defaultListId),
get: () => store.getters['lists/getListById'](settings.value.defaultListId),
set(l) {
settings.defaultListId = l ? l.id : DEFAULT_LIST_ID
settings.value.defaultListId = l ? l.id : DEFAULT_LIST_ID
},
})
const loading = computed(() => store.state.loading && store.state.loadingModule === 'general-settings')
@ -244,13 +259,12 @@ watch(
(play) => play && playPopSound(),
)
async function updateSettings() {
localStorage.setItem(playSoundWhenDoneKey, playSoundWhenDone.value ? 'true' : 'false')
setQuickAddMagicMode(quickAddMagicMode.value)
await store.dispatch('auth/saveUserSettings', {
settings: {...settings},
settings: {...settings.value},
})
}
</script>

1116
yarn.lock

File diff suppressed because it is too large Load Diff