Merge branch 'main' into feature/ready-state
This commit is contained in:
commit
ce3a53ad17
|
@ -21,5 +21,9 @@ indent_size = 2
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.{scss,css}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
[.nvmrc]
|
[.nvmrc]
|
||||||
insert_final_newline = false
|
insert_final_newline = false
|
35
package.json
35
package.json
|
@ -10,6 +10,7 @@
|
||||||
"build:modern-only": "BUILD_MODERN_ONLY=true vite build && workbox copyLibraries dist/",
|
"build:modern-only": "BUILD_MODERN_ONLY=true vite build && workbox copyLibraries dist/",
|
||||||
"build:dev": "vite build -m development --outDir dist-dev/",
|
"build:dev": "vite build -m development --outDir dist-dev/",
|
||||||
"lint": "eslint --ignore-pattern '*.test.*' ./src --ext .vue,.js,.ts",
|
"lint": "eslint --ignore-pattern '*.test.*' ./src --ext .vue,.js,.ts",
|
||||||
|
"lint:markup": "vue-tsc --noEmit",
|
||||||
"cypress:open": "cypress open",
|
"cypress:open": "cypress open",
|
||||||
"test:unit": "jest",
|
"test:unit": "jest",
|
||||||
"test:frontend": "cypress run",
|
"test:frontend": "cypress run",
|
||||||
|
@ -17,9 +18,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kyvg/vue3-notification": "2.3.4",
|
"@kyvg/vue3-notification": "2.3.4",
|
||||||
"@sentry/tracing": "6.13.3",
|
"@sentry/tracing": "6.14.1",
|
||||||
"@sentry/vue": "6.13.3",
|
"@sentry/vue": "6.14.1",
|
||||||
"@vue/compat": "3.2.20",
|
"@vue/compat": "3.2.21",
|
||||||
"bulma": "0.9.3",
|
"bulma": "0.9.3",
|
||||||
"camel-case": "4.1.2",
|
"camel-case": "4.1.2",
|
||||||
"codemirror": "5.63.3",
|
"codemirror": "5.63.3",
|
||||||
|
@ -32,15 +33,15 @@
|
||||||
"is-touch-device": "1.0.1",
|
"is-touch-device": "1.0.1",
|
||||||
"lodash.clonedeep": "4.5.0",
|
"lodash.clonedeep": "4.5.0",
|
||||||
"lodash.debounce": "4.0.8",
|
"lodash.debounce": "4.0.8",
|
||||||
"marked": "3.0.8",
|
"marked": "4.0.0",
|
||||||
"register-service-worker": "1.7.2",
|
"register-service-worker": "1.7.2",
|
||||||
"snake-case": "3.0.4",
|
"snake-case": "3.0.4",
|
||||||
"ufo": "0.7.9",
|
"ufo": "0.7.9",
|
||||||
"vue": "3.2.20",
|
"vue": "3.2.21",
|
||||||
"vue-advanced-cropper": "2.6.3",
|
"vue-advanced-cropper": "2.6.3",
|
||||||
"vue-drag-resize": "2.0.3",
|
"vue-drag-resize": "2.0.3",
|
||||||
"vue-flatpickr-component": "9.0.5",
|
"vue-flatpickr-component": "9.0.5",
|
||||||
"vue-i18n": "9.2.0-beta.16",
|
"vue-i18n": "9.2.0-beta.18",
|
||||||
"vue-router": "4.0.12",
|
"vue-router": "4.0.12",
|
||||||
"vuedraggable": "4.1.0",
|
"vuedraggable": "4.1.0",
|
||||||
"vuex": "4.0.2",
|
"vuex": "4.0.2",
|
||||||
|
@ -53,30 +54,31 @@
|
||||||
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
||||||
"@fortawesome/vue-fontawesome": "3.0.0-5",
|
"@fortawesome/vue-fontawesome": "3.0.0-5",
|
||||||
"@types/jest": "27.0.2",
|
"@types/jest": "27.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "5.2.0",
|
"@typescript-eslint/eslint-plugin": "5.3.1",
|
||||||
"@typescript-eslint/parser": "5.2.0",
|
"@typescript-eslint/parser": "5.3.1",
|
||||||
"@vitejs/plugin-legacy": "1.6.2",
|
"@vitejs/plugin-legacy": "1.6.2",
|
||||||
"@vitejs/plugin-vue": "1.9.4",
|
"@vitejs/plugin-vue": "1.9.4",
|
||||||
"@vue/eslint-config-typescript": "9.0.0",
|
"@vue/eslint-config-typescript": "9.0.1",
|
||||||
"autoprefixer": "10.4.0",
|
"autoprefixer": "10.4.0",
|
||||||
"axios": "0.24.0",
|
"axios": "0.24.0",
|
||||||
"browserslist": "4.17.5",
|
"browserslist": "4.17.6",
|
||||||
"cypress": "8.7.0",
|
"cypress": "8.7.0",
|
||||||
"cypress-file-upload": "5.0.8",
|
"cypress-file-upload": "5.0.8",
|
||||||
"esbuild": "0.13.12",
|
"esbuild": "0.13.13",
|
||||||
"eslint": "8.1.0",
|
"eslint": "8.2.0",
|
||||||
"eslint-plugin-vue": "7.20.0",
|
"eslint-plugin-vue": "8.0.3",
|
||||||
"express": "4.17.1",
|
"express": "4.17.1",
|
||||||
"faker": "5.5.3",
|
"faker": "5.5.3",
|
||||||
"jest": "27.3.1",
|
"jest": "27.3.1",
|
||||||
"postcss": "8.3.11",
|
"postcss": "8.3.11",
|
||||||
"rollup": "2.58.3",
|
"rollup": "2.59.0",
|
||||||
"rollup-plugin-visualizer": "5.5.2",
|
"rollup-plugin-visualizer": "5.5.2",
|
||||||
"sass": "1.43.4",
|
"sass": "1.43.4",
|
||||||
"ts-jest": "27.0.7",
|
"ts-jest": "27.0.7",
|
||||||
"typescript": "4.4.4",
|
"typescript": "4.4.4",
|
||||||
"vite": "2.6.13",
|
"vite": "2.6.14",
|
||||||
"vite-plugin-pwa": "0.11.3",
|
"vite-plugin-pwa": "0.11.3",
|
||||||
|
"vue-tsc": "0.29.3",
|
||||||
"wait-on": "6.0.0",
|
"wait-on": "6.0.0",
|
||||||
"workbox-cli": "6.3.0"
|
"workbox-cli": "6.3.0"
|
||||||
},
|
},
|
||||||
|
@ -108,7 +110,8 @@
|
||||||
"semi": [
|
"semi": [
|
||||||
"error",
|
"error",
|
||||||
"never"
|
"never"
|
||||||
]
|
],
|
||||||
|
"vue/multi-word-component-names": 0
|
||||||
},
|
},
|
||||||
"parser": "vue-eslint-parser",
|
"parser": "vue-eslint-parser",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Shell script because yaml doesn't understand the header is a string literal and not a yaml symbol
|
|
||||||
|
|
||||||
curl -d operation=pull -H "Authorization: Token $WEBLATE_TOKEN" https://hosted.weblate.org/api/projects/vikunja/repository/
|
|
||||||
curl -d operation=push -H "Authorization: Token $WEBLATE_TOKEN" https://hosted.weblate.org/api/projects/vikunja/repository/
|
|
|
@ -74,9 +74,9 @@
|
||||||
<namespace-settings-dropdown :namespace="n" v-if="n.id > 0"/>
|
<namespace-settings-dropdown :namespace="n" v-if="n.id > 0"/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
v-if="listsVisible[n.id] ?? true"
|
||||||
:key="n.id + 'child'"
|
:key="n.id + 'child'"
|
||||||
class="more-container"
|
class="more-container"
|
||||||
v-if="typeof listsVisible[n.id] !== 'undefined' ? listsVisible[n.id] : true"
|
|
||||||
>
|
>
|
||||||
<!--
|
<!--
|
||||||
NOTE: a v-model / computed setter is not possible, since the updateActiveLists function
|
NOTE: a v-model / computed setter is not possible, since the updateActiveLists function
|
||||||
|
@ -134,8 +134,7 @@
|
||||||
:class="{'is-favorite': l.isFavorite}"
|
:class="{'is-favorite': l.isFavorite}"
|
||||||
@click.prevent.stop="toggleFavoriteList(l)"
|
@click.prevent.stop="toggleFavoriteList(l)"
|
||||||
class="favorite">
|
class="favorite">
|
||||||
<icon icon="star" v-if="l.isFavorite"/>
|
<icon :icon="l.isFavorite ? 'star' : ['far', 'star']" />
|
||||||
<icon :icon="['far', 'star']" v-else/>
|
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
@ -283,22 +282,20 @@ $vikunja-nav-background: $light-background;
|
||||||
$vikunja-nav-color: $grey-700;
|
$vikunja-nav-color: $grey-700;
|
||||||
$vikunja-nav-selected-width: 0.4rem;
|
$vikunja-nav-selected-width: 0.4rem;
|
||||||
|
|
||||||
|
|
||||||
.namespace-container {
|
.namespace-container {
|
||||||
background: $vikunja-nav-background;
|
|
||||||
z-index: 6;
|
z-index: 6;
|
||||||
|
background: $vikunja-nav-background;
|
||||||
color: $vikunja-nav-color;
|
color: $vikunja-nav-color;
|
||||||
padding: 0;
|
padding: 0 0 1rem;
|
||||||
transition: all $transition;
|
transition: transform $transition-duration ease-in;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
|
||||||
top: $navbar-height;
|
top: $navbar-height;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
transform: translateX(-100%);
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
width: $navbar-width;
|
width: $navbar-width;
|
||||||
|
|
||||||
padding: 0 0 1rem;
|
|
||||||
left: -147vw;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
@media screen and (max-width: $tablet) {
|
@media screen and (max-width: $tablet) {
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -306,7 +303,8 @@ $vikunja-nav-selected-width: 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
left: 0;
|
transform: translateX(0);
|
||||||
|
transition: transform $transition-duration ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
|
|
|
@ -21,7 +21,14 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<x-button :disabled="isEmpty" @click="reset" class="is-small ml-2" :shadow="false" type="secondary">
|
<x-button
|
||||||
|
v-if="!isEmpty"
|
||||||
|
:disabled="isEmpty"
|
||||||
|
@click="reset"
|
||||||
|
class="is-small ml-2"
|
||||||
|
:shadow="false"
|
||||||
|
type="secondary"
|
||||||
|
>
|
||||||
{{ $t('input.resetColor') }}
|
{{ $t('input.resetColor') }}
|
||||||
</x-button>
|
</x-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import VueEasymde from './vue-easymde/vue-easymde.vue'
|
import VueEasymde from './vue-easymde/vue-easymde.vue'
|
||||||
import marked from 'marked'
|
import {marked} from 'marked'
|
||||||
import DOMPurify from 'dompurify'
|
import DOMPurify from 'dompurify'
|
||||||
import hljs from 'highlight.js/lib/common'
|
import hljs from 'highlight.js/lib/common'
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
@focus="focus"
|
@focus="focus"
|
||||||
>
|
>
|
||||||
<div class="control" :class="{'is-loading': loading || localLoading}">
|
<div class="control" :class="{'is-loading': loading || localLoading}">
|
||||||
<div class="input-wrapper input" :class="{'has-multiple': multiple && Array.isArray(internalValue) && internalValue.length > 0}">
|
<div
|
||||||
|
class="input-wrapper input"
|
||||||
|
:class="{'has-multiple': hasMultiple}">
|
||||||
<template v-if="Array.isArray(internalValue)">
|
<template v-if="Array.isArray(internalValue)">
|
||||||
<template v-for="(item, key) in internalValue">
|
<template v-for="(item, key) in internalValue">
|
||||||
<slot name="tag" :item="item">
|
<slot name="tag" :item="item">
|
||||||
|
@ -81,7 +83,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { i18n } from '@/i18n'
|
import {i18n} from '@/i18n'
|
||||||
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -134,9 +136,7 @@ export default {
|
||||||
// If true, will provide an "add this as a new value" entry which fires an @create event when clicking on it.
|
// If true, will provide an "add this as a new value" entry which fires an @create event when clicking on it.
|
||||||
creatable: {
|
creatable: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default() {
|
default: false,
|
||||||
return false
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
// The text shown next to the new value option.
|
// The text shown next to the new value option.
|
||||||
createPlaceholder: {
|
createPlaceholder: {
|
||||||
|
@ -155,23 +155,17 @@ export default {
|
||||||
// If true, allows for selecting multiple items. v-model will be an array with all selected values in that case.
|
// If true, allows for selecting multiple items. v-model will be an array with all selected values in that case.
|
||||||
multiple: {
|
multiple: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default() {
|
default: false,
|
||||||
return false
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
// If true, displays the search results inline instead of using a dropdown.
|
// If true, displays the search results inline instead of using a dropdown.
|
||||||
inline: {
|
inline: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default() {
|
default: false,
|
||||||
return false
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
// If true, shows search results when no query is specified.
|
// If true, shows search results when no query is specified.
|
||||||
showEmpty: {
|
showEmpty: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default() {
|
default: true,
|
||||||
return true
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
// The delay in ms after which the search event will be fired. Used to avoid hitting the network on every keystroke.
|
// The delay in ms after which the search event will be fired. Used to avoid hitting the network on every keystroke.
|
||||||
searchDelay: {
|
searchDelay: {
|
||||||
|
@ -180,8 +174,12 @@ export default {
|
||||||
return 200
|
return 200
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
closeAfterSelect: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Available events:
|
* Available events:
|
||||||
* @search: Triggered every time the search query input changes
|
* @search: Triggered every time the search query input changes
|
||||||
|
@ -234,6 +232,9 @@ export default {
|
||||||
|
|
||||||
return this.searchResults
|
return this.searchResults
|
||||||
},
|
},
|
||||||
|
hasMultiple() {
|
||||||
|
return this.multiple && Array.isArray(this.internalValue) && this.internalValue.length > 0
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// Searching will be triggered with a 200ms delay to avoid searching on every keyup event.
|
// Searching will be triggered with a 200ms delay to avoid searching on every keyup event.
|
||||||
|
@ -285,7 +286,9 @@ export default {
|
||||||
this.$emit('update:modelValue', this.internalValue)
|
this.$emit('update:modelValue', this.internalValue)
|
||||||
this.$emit('select', object)
|
this.$emit('select', object)
|
||||||
this.setSelectedObject(object)
|
this.setSelectedObject(object)
|
||||||
this.closeSearchResults()
|
if (this.closeAfterSelect && this.filteredSearchResults.length > 0 && !this.creatableAvailable) {
|
||||||
|
this.closeSearchResults()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
setSelectedObject(object, resetOnly = false) {
|
setSelectedObject(object, resetOnly = false) {
|
||||||
this.internalValue = object
|
this.internalValue = object
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import EasyMDE from 'easymde'
|
import EasyMDE from 'easymde'
|
||||||
import marked from 'marked'
|
import {marked} from 'marked'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'vue-easymde',
|
name: 'vue-easymde',
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
}"
|
}"
|
||||||
:to="{ name: 'list.index', params: { listId: list.id} }"
|
:to="{ name: 'list.index', params: { listId: list.id} }"
|
||||||
class="list-card"
|
class="list-card"
|
||||||
tag="span"
|
|
||||||
v-if="list !== null && (showArchived ? true : !list.isArchived)"
|
v-if="list !== null && (showArchived ? true : !list.isArchived)"
|
||||||
>
|
>
|
||||||
<div class="is-archived-container">
|
<div class="is-archived-container">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="notification is-danger">
|
<div class="notification is-danger">
|
||||||
<i18n-t keypath="loadingError.failed">
|
<i18n-t keypath="loadingError.failed">
|
||||||
<a @click="() => location.reload()">{{ $t('loadingError.tryAgain') }}</a>
|
<a @click="reload">{{ $t('loadingError.tryAgain') }}</a>
|
||||||
<a href="https://vikunja.io/contact/" rel="noreferrer noopener nofollow" target="_blank">{{ $t('loadingError.contact') }}</a>
|
<a href="https://vikunja.io/contact/" rel="noreferrer noopener nofollow" target="_blank">{{ $t('loadingError.contact') }}</a>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,5 +10,10 @@
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'error',
|
name: 'error',
|
||||||
|
methods: {
|
||||||
|
reload() {
|
||||||
|
window.location.reload()
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -8,15 +8,13 @@
|
||||||
<router-link
|
<router-link
|
||||||
:disabled="currentPage === 1 || null"
|
:disabled="currentPage === 1 || null"
|
||||||
:to="getRouteForPagination(currentPage - 1)"
|
:to="getRouteForPagination(currentPage - 1)"
|
||||||
class="pagination-previous"
|
class="pagination-previous">
|
||||||
tag="button">
|
|
||||||
{{ $t('misc.previous') }}
|
{{ $t('misc.previous') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link
|
<router-link
|
||||||
:disabled="currentPage === totalPages || null"
|
:disabled="currentPage === totalPages || null"
|
||||||
:to="getRouteForPagination(currentPage + 1)"
|
:to="getRouteForPagination(currentPage + 1)"
|
||||||
class="pagination-next"
|
class="pagination-next">
|
||||||
tag="button">
|
|
||||||
{{ $t('misc.next') }}
|
{{ $t('misc.next') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<ul class="pagination-list">
|
<ul class="pagination-list">
|
||||||
|
|
|
@ -6,11 +6,14 @@
|
||||||
:disabled="taskService.loading || null"
|
:disabled="taskService.loading || null"
|
||||||
class="input"
|
class="input"
|
||||||
:placeholder="$t('list.list.addPlaceholder')"
|
:placeholder="$t('list.list.addPlaceholder')"
|
||||||
type="text"
|
cols="1"
|
||||||
v-focus
|
v-focus
|
||||||
v-model="newTaskTitle"
|
v-model="newTaskTitle"
|
||||||
ref="newTaskInput"
|
ref="newTaskInput"
|
||||||
:style="{'height': `calc(${textAreaHeight}px - 2px + 1rem)`}"
|
:style="{
|
||||||
|
'minHeight': `${initialTextAreaHeight}px`,
|
||||||
|
'height': `calc(${textAreaHeight}px - 2px + 1rem)`
|
||||||
|
}"
|
||||||
@keyup="errorMessage = ''"
|
@keyup="errorMessage = ''"
|
||||||
@keydown.enter="handleEnter"
|
@keydown.enter="handleEnter"
|
||||||
/>
|
/>
|
||||||
|
@ -70,10 +73,10 @@ export default {
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
newTaskTitle(newVal) {
|
newTaskTitle(newVal) {
|
||||||
// Calculating the textarea height based on lines of input in it. That is more reliable when removing a
|
// Calculating the textarea height based on lines of input in it.
|
||||||
// line from the input.
|
// That is more reliable when removing a line from the input.
|
||||||
const numberOfLines = newVal.split(/\r\n|\r|\n/).length
|
const numberOfLines = newVal.split(/\r\n|\r|\n/).length
|
||||||
const fontSize = parseInt(window.getComputedStyle(this.$refs.newTaskInput, null).getPropertyValue('font-size'))
|
const fontSize = parseFloat(window.getComputedStyle(this.$refs.newTaskInput, null).getPropertyValue('font-size'))
|
||||||
|
|
||||||
this.textAreaHeight = numberOfLines * fontSize * LINE_HEIGHT + INPUT_BORDER_PX
|
this.textAreaHeight = numberOfLines * fontSize * LINE_HEIGHT + INPUT_BORDER_PX
|
||||||
},
|
},
|
||||||
|
@ -145,4 +148,8 @@ export default {
|
||||||
.input, .textarea {
|
.input, .textarea {
|
||||||
transition: border-color $transition;
|
transition: border-color $transition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
:create-placeholder="$t('task.label.createPlaceholder')"
|
:create-placeholder="$t('task.label.createPlaceholder')"
|
||||||
v-model="labels"
|
v-model="labels"
|
||||||
:search-delay="10"
|
:search-delay="10"
|
||||||
|
:close-after-select="false"
|
||||||
>
|
>
|
||||||
<template #tag="props">
|
<template #tag="props">
|
||||||
<span
|
<span
|
||||||
|
@ -148,3 +149,9 @@ export default {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tag {
|
||||||
|
margin: .5rem 0 0 .5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -58,6 +58,9 @@
|
||||||
<span v-if="task.description" class="icon">
|
<span v-if="task.description" class="icon">
|
||||||
<icon icon="align-left"/>
|
<icon icon="align-left"/>
|
||||||
</span>
|
</span>
|
||||||
|
<span class="icon" v-if="task.repeatAfter.amount > 0">
|
||||||
|
<icon icon="history"/>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
{{ task.title }}
|
{{ task.title }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<labels class="labels" :labels="task.labels"/>
|
<labels class="labels ml-2 mr-1" :labels="task.labels" v-if="task.labels.length > 0"/>
|
||||||
<user
|
<user
|
||||||
:avatar-size="27"
|
:avatar-size="27"
|
||||||
:is-inline="true"
|
:is-inline="true"
|
||||||
|
@ -58,6 +58,9 @@
|
||||||
<span class="list-task-icon" v-if="task.description">
|
<span class="list-task-icon" v-if="task.description">
|
||||||
<icon icon="align-left"/>
|
<icon icon="align-left"/>
|
||||||
</span>
|
</span>
|
||||||
|
<span class="list-task-icon" v-if="task.repeatAfter.amount > 0">
|
||||||
|
<icon icon="history"/>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<checklist-summary :task="task"/>
|
<checklist-summary :task="task"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
@ -252,11 +255,6 @@ export default {
|
||||||
flex: 0 0 10px;
|
flex: 0 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.labels {
|
|
||||||
padding-left: 0.5rem;
|
|
||||||
padding-right: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
|
|
|
@ -234,7 +234,7 @@
|
||||||
"list": {
|
"list": {
|
||||||
"title": "Danh sách",
|
"title": "Danh sách",
|
||||||
"add": "Thêm",
|
"add": "Thêm",
|
||||||
"addPlaceholder": "Thêm một công việc mới…",
|
"addPlaceholder": "Thêm việc cần làm…",
|
||||||
"empty": "Danh sách này đang trống trơn.",
|
"empty": "Danh sách này đang trống trơn.",
|
||||||
"newTaskCta": "Thêm một công việc mới.",
|
"newTaskCta": "Thêm một công việc mới.",
|
||||||
"editTask": "Chỉnh sửa Công việc"
|
"editTask": "Chỉnh sửa Công việc"
|
||||||
|
@ -459,7 +459,7 @@
|
||||||
"nextMonday": "Thứ Hai tới",
|
"nextMonday": "Thứ Hai tới",
|
||||||
"thisWeekend": "Cuối tuần này",
|
"thisWeekend": "Cuối tuần này",
|
||||||
"laterThisWeek": "Cuối tuần này",
|
"laterThisWeek": "Cuối tuần này",
|
||||||
"nextWeek": "Tuần kế tiếp",
|
"nextWeek": "Tuần tới",
|
||||||
"chooseDate": "Chọn một ngày"
|
"chooseDate": "Chọn một ngày"
|
||||||
},
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
|
@ -509,8 +509,8 @@
|
||||||
"from": "Công việc từ",
|
"from": "Công việc từ",
|
||||||
"until": "cho đến",
|
"until": "cho đến",
|
||||||
"today": "Hôm nay",
|
"today": "Hôm nay",
|
||||||
"nextWeek": "Tuần kế tiếp",
|
"nextWeek": "Tuần tới",
|
||||||
"nextMonth": "Tháng kế tiếp",
|
"nextMonth": "Tháng tới",
|
||||||
"noTasks": "Hôm nay thảnh thơi — Hãy tận hưởng ngày tuyệt vời!"
|
"noTasks": "Hôm nay thảnh thơi — Hãy tận hưởng ngày tuyệt vời!"
|
||||||
},
|
},
|
||||||
"detail": {
|
"detail": {
|
||||||
|
|
|
@ -120,12 +120,9 @@ export default {
|
||||||
// Not sure if this is the right place to put the logic in, maybe a seperate js component would be better suited.
|
// Not sure if this is the right place to put the logic in, maybe a seperate js component would be better suited.
|
||||||
async register(ctx, credentials) {
|
async register(ctx, credentials) {
|
||||||
const HTTP = HTTPFactory()
|
const HTTP = HTTPFactory()
|
||||||
|
ctx.commit(LOADING, true, {root: true})
|
||||||
try {
|
try {
|
||||||
await HTTP.post('register', {
|
await HTTP.post('register', credentials)
|
||||||
username: credentials.username,
|
|
||||||
email: credentials.email,
|
|
||||||
password: credentials.password,
|
|
||||||
})
|
|
||||||
return ctx.dispatch('login', credentials)
|
return ctx.dispatch('login', credentials)
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
if (e.response?.data?.message) {
|
if (e.response?.data?.message) {
|
||||||
|
|
|
@ -223,13 +223,12 @@ export default {
|
||||||
|
|
||||||
const labelAddsToWaitFor = parsedLabels.map(async labelTitle => {
|
const labelAddsToWaitFor = parsedLabels.map(async labelTitle => {
|
||||||
let label = validateLabel(labels, labelTitle)
|
let label = validateLabel(labels, labelTitle)
|
||||||
if (typeof label !== 'undefined') {
|
if (typeof label === 'undefined') {
|
||||||
return label
|
// label not found, create it
|
||||||
|
const labelModel = new LabelModel({title: labelTitle})
|
||||||
|
label = await dispatch('labels/createLabel', labelModel, {root: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
// label not found, create it
|
|
||||||
const labelModel = new LabelModel({title: labelTitle})
|
|
||||||
await dispatch('labels/createLabel', labelModel, {root: true})
|
|
||||||
return addLabelToTask(task, label)
|
return addLabelToTask(task, label)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,6 @@
|
||||||
.notifications {
|
.notifications {
|
||||||
left: 0.5rem !important;
|
left: 0.5rem !important;
|
||||||
bottom: 1rem !important;
|
bottom: 1rem !important;
|
||||||
|
|
||||||
.notification-wrapper .notification {
|
|
||||||
border-radius: 0;
|
|
||||||
border-top-width: 0;
|
|
||||||
border-right-width: 0;
|
|
||||||
border-bottom-width: 0;
|
|
||||||
border-left-width: 0.4rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.message .message-body {
|
.message .message-body {
|
||||||
|
|
|
@ -7,23 +7,14 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
import ShowTasks from './ShowTasks'
|
import ShowTasks from './ShowTasks'
|
||||||
|
|
||||||
function getNextWeekDate() {
|
function getNextWeekDate() {
|
||||||
return new Date((new Date()).getTime() + 7 * 24 * 60 * 60 * 1000)
|
return new Date((new Date()).getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
const startDate = ref(new Date())
|
||||||
name: 'ShowTasksInRange',
|
const endDate = ref(getNextWeekDate())
|
||||||
components: {
|
|
||||||
ShowTasks,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
startDate: new Date(),
|
|
||||||
endDate: getNextWeekDate(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
|
@ -131,12 +131,10 @@ export default {
|
||||||
}),
|
}),
|
||||||
methods: {
|
methods: {
|
||||||
async submit() {
|
async submit() {
|
||||||
this.$store.commit(LOADING, true)
|
|
||||||
this.errorMessage = ''
|
this.errorMessage = ''
|
||||||
|
|
||||||
if (this.credentials.password2 !== this.credentials.password) {
|
if (this.credentials.password2 !== this.credentials.password) {
|
||||||
this.errorMessage = this.$t('user.auth.passwordsDontMatch')
|
this.errorMessage = this.$t('user.auth.passwordsDontMatch')
|
||||||
this.$store.commit(LOADING, false)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in New Issue