forked from vikunja/frontend
Compare commits
19 Commits
52b0a4c6ac
...
b005d023c9
Author | SHA1 | Date | |
---|---|---|---|
|
b005d023c9 | ||
|
92727e1c01 | ||
5da6a6bb39 | |||
34ded051d1 | |||
65be56eb39 | |||
3dbd36eef7 | |||
a9d3446ce3 | |||
e37145cd43 | |||
641ccd1026 | |||
0cd9d43a7c | |||
c92062b6a5 | |||
4a3b4982ab | |||
0f42ed3cf7 | |||
7802cf619f | |||
2a8ecb8a8c | |||
|
15b5ad9d84 | ||
|
714c01403c | ||
|
194b1291e6 | ||
|
422ce6795a |
|
@ -19,4 +19,4 @@ indent_size = 2
|
|||
|
||||
[*.json]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
indent_size = 2
|
|
@ -3,7 +3,6 @@ import {UserFactory} from '../../factories/user'
|
|||
import '../../support/authenticateUser'
|
||||
import {ListFactory} from '../../factories/list'
|
||||
import {NamespaceFactory} from '../../factories/namespace'
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
|
||||
describe('Namepaces', () => {
|
||||
let namespaces
|
||||
|
@ -99,4 +98,48 @@ describe('Namepaces', () => {
|
|||
cy.get('.namespace-container .menu.namespaces-lists')
|
||||
.should('not.contain', newNamespaces[0].title)
|
||||
})
|
||||
|
||||
it('Should not show archived lists & namespaces if the filter is not checked', () => {
|
||||
const n = NamespaceFactory.create(1, {
|
||||
id: 2,
|
||||
is_archived: true,
|
||||
}, false)
|
||||
ListFactory.create(1, {
|
||||
id: 2,
|
||||
namespace_id: n[0].id,
|
||||
}, false)
|
||||
|
||||
ListFactory.create(1, {
|
||||
id: 3,
|
||||
is_archived: true,
|
||||
}, false)
|
||||
|
||||
// Initial
|
||||
cy.visit('/namespaces')
|
||||
cy.get('.namespaces-list .namespace')
|
||||
.should('not.contain', 'Archived')
|
||||
|
||||
// Show archived
|
||||
cy.get('.namespaces-list .fancycheckbox.show-archived-check label.check span')
|
||||
.should('be.visible')
|
||||
.click()
|
||||
cy.get('.namespaces-list .fancycheckbox.show-archived-check input')
|
||||
.should('be.checked')
|
||||
cy.get('.namespaces-list .namespace')
|
||||
.should('contain', 'Archived')
|
||||
|
||||
// Don't show archived
|
||||
cy.get('.namespaces-list .fancycheckbox.show-archived-check label.check span')
|
||||
.should('be.visible')
|
||||
.click()
|
||||
cy.get('.namespaces-list .fancycheckbox.show-archived-check input')
|
||||
.should('not.be.checked')
|
||||
|
||||
// Second time visiting after unchecking
|
||||
cy.visit('/namespaces')
|
||||
cy.get('.namespaces-list .fancycheckbox.show-archived-check input')
|
||||
.should('not.be.checked')
|
||||
cy.get('.namespaces-list .namespace')
|
||||
.should('not.contain', 'Archived')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -21,7 +21,7 @@ export class Factory {
|
|||
* @param override
|
||||
* @returns {[]}
|
||||
*/
|
||||
static create(count = 1, override = {}) {
|
||||
static create(count = 1, override = {}, truncate = true) {
|
||||
const data = []
|
||||
|
||||
for (let i = 1; i <= count; i++) {
|
||||
|
@ -38,7 +38,7 @@ export class Factory {
|
|||
data.push(entry)
|
||||
}
|
||||
|
||||
seed(this.table, data)
|
||||
seed(this.table, data, truncate)
|
||||
|
||||
return data
|
||||
}
|
||||
|
|
|
@ -8,14 +8,14 @@
|
|||
* @param table
|
||||
* @param data
|
||||
*/
|
||||
export function seed(table, data = {}) {
|
||||
if(data === null) {
|
||||
export function seed(table, data = {}, truncate = true) {
|
||||
if (data === null) {
|
||||
data = []
|
||||
}
|
||||
|
||||
cy.request({
|
||||
method: 'PATCH',
|
||||
url: `${Cypress.env('API_URL')}/test/${table}`,
|
||||
url: `${Cypress.env('API_URL')}/test/${table}?truncate=${truncate ? 'true' : 'false'}`,
|
||||
headers: {
|
||||
'Authorization': Cypress.env('TEST_SECRET'),
|
||||
},
|
||||
|
|
212
package.json
212
package.json
|
@ -1,109 +1,113 @@
|
|||
{
|
||||
"name": "vikunja-frontend",
|
||||
"version": "0.10.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"serve:dist": "node scripts/serve-dist.js",
|
||||
"build": "vue-cli-service build --modern",
|
||||
"build:report": "vue-cli-service build --report",
|
||||
"lint": "vue-cli-service lint --ignore-pattern '*.test.*'",
|
||||
"cypress:open": "cypress open",
|
||||
"test:unit": "jest",
|
||||
"test:frontend": "cypress run"
|
||||
"name": "vikunja-frontend",
|
||||
"version": "0.10.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"serve:dist": "node scripts/serve-dist.js",
|
||||
"build": "vue-cli-service build --modern",
|
||||
"build:report": "vue-cli-service build --report",
|
||||
"lint": "vue-cli-service lint --ignore-pattern '*.test.*'",
|
||||
"cypress:open": "cypress open",
|
||||
"test:unit": "jest",
|
||||
"test:frontend": "cypress run"
|
||||
},
|
||||
"dependencies": {
|
||||
"browserslist": "4.16.6",
|
||||
"bulma": "0.9.2",
|
||||
"camel-case": "4.1.2",
|
||||
"copy-to-clipboard": "3.3.1",
|
||||
"date-fns": "2.22.1",
|
||||
"dompurify": "2.2.9",
|
||||
"highlight.js": "11.0.1",
|
||||
"lodash": "4.17.21",
|
||||
"marked": "2.0.7",
|
||||
"register-service-worker": "1.7.2",
|
||||
"sass": "1.34.1",
|
||||
"snake-case": "3.0.4",
|
||||
"verte": "0.0.12",
|
||||
"vue": "2.6.14",
|
||||
"vue-advanced-cropper": "1.6.0",
|
||||
"vue-drag-resize": "1.5.4",
|
||||
"vue-easymde": "1.4.0",
|
||||
"vue-shortkey": "3.1.7",
|
||||
"vue-smooth-dnd": "0.8.1",
|
||||
"vuex": "3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.35",
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.3",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.3",
|
||||
"@fortawesome/vue-fontawesome": "2.0.2",
|
||||
"@vue/cli": "4.5.13",
|
||||
"@vue/cli-plugin-babel": "4.5.13",
|
||||
"@vue/cli-plugin-eslint": "4.5.13",
|
||||
"@vue/cli-plugin-pwa": "4.5.13",
|
||||
"@vue/cli-service": "4.5.13",
|
||||
"axios": "0.21.1",
|
||||
"babel-eslint": "10.1.0",
|
||||
"cypress": "7.4.0",
|
||||
"cypress-file-upload": "5.0.7",
|
||||
"eslint": "7.28.0",
|
||||
"eslint-plugin-vue": "7.10.0",
|
||||
"faker": "5.5.3",
|
||||
"jest": "27.0.4",
|
||||
"sass-loader": "10.2.0",
|
||||
"vue-flatpickr-component": "8.1.6",
|
||||
"vue-notification": "1.3.20",
|
||||
"vue-router": "3.5.1",
|
||||
"vue-template-compiler": "2.6.14",
|
||||
"wait-on": "5.3.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"dependencies": {
|
||||
"browserslist": "4.16.6",
|
||||
"bulma": "0.9.2",
|
||||
"camel-case": "4.1.2",
|
||||
"copy-to-clipboard": "3.3.1",
|
||||
"date-fns": "2.22.1",
|
||||
"dompurify": "2.2.9",
|
||||
"highlight.js": "11.0.0",
|
||||
"lodash": "4.17.21",
|
||||
"marked": "2.0.7",
|
||||
"register-service-worker": "1.7.2",
|
||||
"sass": "1.34.0",
|
||||
"snake-case": "3.0.4",
|
||||
"verte": "0.0.12",
|
||||
"vue": "2.6.13",
|
||||
"vue-advanced-cropper": "1.6.0",
|
||||
"vue-drag-resize": "1.5.4",
|
||||
"vue-easymde": "1.4.0",
|
||||
"vue-shortkey": "3.1.7",
|
||||
"vue-smooth-dnd": "0.8.1",
|
||||
"vuex": "3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.35",
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.3",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.3",
|
||||
"@fortawesome/vue-fontawesome": "2.0.2",
|
||||
"@vue/cli": "4.5.13",
|
||||
"@vue/cli-plugin-babel": "4.5.13",
|
||||
"@vue/cli-plugin-eslint": "4.5.13",
|
||||
"@vue/cli-plugin-pwa": "4.5.13",
|
||||
"@vue/cli-service": "4.5.13",
|
||||
"axios": "0.21.1",
|
||||
"babel-eslint": "10.1.0",
|
||||
"cypress": "7.4.0",
|
||||
"cypress-file-upload": "5.0.7",
|
||||
"eslint": "7.27.0",
|
||||
"eslint-plugin-vue": "7.10.0",
|
||||
"faker": "5.5.3",
|
||||
"jest": "27.0.3",
|
||||
"sass-loader": "10.2.0",
|
||||
"vue-flatpickr-component": "8.1.6",
|
||||
"vue-notification": "1.3.20",
|
||||
"vue-router": "3.5.1",
|
||||
"vue-template-compiler": "2.6.13",
|
||||
"wait-on": "5.3.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"rules": {
|
||||
"vue/html-quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"comma-dangle": [
|
||||
"error",
|
||||
"always-multiline"
|
||||
]
|
||||
},
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"*.test.js",
|
||||
"cypress/*"
|
||||
]
|
||||
},
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
"autoprefixer": {}
|
||||
}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie < 11"
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"jest": {
|
||||
"testPathIgnorePatterns": [
|
||||
"cypress"
|
||||
]
|
||||
"rules": {
|
||||
"vue/html-quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"comma-dangle": [
|
||||
"error",
|
||||
"always-multiline"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"never"
|
||||
]
|
||||
},
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"*.test.js",
|
||||
"cypress/*"
|
||||
]
|
||||
},
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
"autoprefixer": {}
|
||||
}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie < 11"
|
||||
],
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"jest": {
|
||||
"testPathIgnorePatterns": [
|
||||
"cypress"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -55,6 +55,7 @@ export default {
|
|||
},
|
||||
created() {
|
||||
this.renewTokenOnFocus()
|
||||
this.loadLabels()
|
||||
},
|
||||
computed: mapState({
|
||||
namespaces(state) {
|
||||
|
@ -126,6 +127,12 @@ export default {
|
|||
showKeyboardShortcuts() {
|
||||
this.$store.commit(KEYBOARD_SHORTCUTS_ACTIVE, true)
|
||||
},
|
||||
loadLabels() {
|
||||
this.$store.dispatch('labels/loadAllLabels')
|
||||
.catch(e => {
|
||||
this.error(e, this)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -118,6 +118,7 @@ import {calculateDayInterval} from '@/helpers/time/calculateDayInterval'
|
|||
import {calculateNearestHours} from '@/helpers/time/calculateNearestHours'
|
||||
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
||||
import {createDateFromString} from '@/helpers/time/createDateFromString'
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'datepicker',
|
||||
|
@ -127,14 +128,6 @@ export default {
|
|||
show: false,
|
||||
changed: false,
|
||||
|
||||
flatPickerConfig: {
|
||||
altFormat: 'j M Y H:i',
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
enableTime: true,
|
||||
time_24hr: true,
|
||||
inline: true,
|
||||
},
|
||||
// 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.
|
||||
flatPickrDate: null,
|
||||
|
@ -172,6 +165,19 @@ export default {
|
|||
this.updateData()
|
||||
},
|
||||
},
|
||||
computed: mapState({
|
||||
flatPickerConfig: state => ({
|
||||
altFormat: 'j M Y H:i',
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
enableTime: true,
|
||||
time_24hr: true,
|
||||
inline: true,
|
||||
locale: {
|
||||
firstDayOfWeek: state.auth.settings.weekStart,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
methods: {
|
||||
setDateValue(newVal) {
|
||||
if(newVal === null) {
|
||||
|
|
|
@ -34,11 +34,13 @@
|
|||
|
||||
<p class="has-text-centered has-text-grey is-italic" v-if="isPreviewActive && text === '' && emptyText !== ''">
|
||||
{{ emptyText }}
|
||||
<a @click="toggleEdit">Edit</a>.
|
||||
<template v-if="isEditEnabled">
|
||||
<a @click="toggleEdit">Edit</a>.
|
||||
</template>
|
||||
</p>
|
||||
|
||||
<ul class="actions">
|
||||
<template v-if="hasEditBottom">
|
||||
<template v-if="hasEditBottom && isEditEnabled">
|
||||
<li>
|
||||
<a v-if="!isEditActive" @click="toggleEdit">Edit</a>
|
||||
<a v-else @click="toggleEdit">Done</a>
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
@keyup="search"
|
||||
@keyup.enter.exact.prevent="() => createOrSelectOnEnter()"
|
||||
:placeholder="placeholder"
|
||||
@keydown.down.exact.prevent="() => preSelect(0, true)"
|
||||
@keydown.down.exact.prevent="() => preSelect(0)"
|
||||
ref="searchInput"
|
||||
@focus="() => showSearchResults = true"
|
||||
/>
|
||||
|
@ -35,27 +35,6 @@
|
|||
|
||||
<transition name="fade">
|
||||
<div class="search-results" :class="{'search-results-inline': inline}" v-if="searchResultsVisible">
|
||||
<button
|
||||
v-if="creatableAvailable"
|
||||
class="is-fullwidth"
|
||||
ref="result--1"
|
||||
@keydown.up.prevent="() => preSelect(-2)"
|
||||
@keydown.down.prevent="() => preSelect(0)"
|
||||
@keyup.enter.prevent="create"
|
||||
@click.prevent.stop="create"
|
||||
>
|
||||
<span>
|
||||
<slot name="searchResult" :option="query">
|
||||
<span class="search-result">
|
||||
{{ query }}
|
||||
</span>
|
||||
</slot>
|
||||
</span>
|
||||
<span class="hint-text">
|
||||
{{ createPlaceholder }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="is-fullwidth"
|
||||
v-for="(data, key) in filteredSearchResults"
|
||||
|
@ -74,6 +53,27 @@
|
|||
{{ selectPlaceholder }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-if="creatableAvailable"
|
||||
class="is-fullwidth"
|
||||
:ref="`result-${filteredSearchResults.length}`"
|
||||
@keydown.up.prevent="() => preSelect(filteredSearchResults.length - 1)"
|
||||
@keydown.down.prevent="() => preSelect(filteredSearchResults.length + 1)"
|
||||
@keyup.enter.prevent="create"
|
||||
@click.prevent.stop="create"
|
||||
>
|
||||
<span>
|
||||
<slot name="searchResult" :option="query">
|
||||
<span class="search-result">
|
||||
{{ query }}
|
||||
</span>
|
||||
</slot>
|
||||
</span>
|
||||
<span class="hint-text">
|
||||
{{ createPlaceholder }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
|
@ -202,14 +202,14 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
searchResultsVisible() {
|
||||
if(this.query === '' && !this.showEmpty) {
|
||||
if (this.query === '' && !this.showEmpty) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.showSearchResults && (
|
||||
(this.filteredSearchResults.length > 0) ||
|
||||
(this.creatable && this.query !== '')
|
||||
)
|
||||
(this.filteredSearchResults.length > 0) ||
|
||||
(this.creatable && this.query !== '')
|
||||
)
|
||||
},
|
||||
creatableAvailable() {
|
||||
return this.creatable && this.query !== '' && !this.filteredSearchResults.some(elem => {
|
||||
|
@ -295,13 +295,8 @@ export default {
|
|||
|
||||
this.query = this.label !== '' ? object[this.label] : object
|
||||
},
|
||||
preSelect(index, lookForCreatable = false) {
|
||||
|
||||
if (index === 0 && this.creatable && lookForCreatable) {
|
||||
index = -1
|
||||
}
|
||||
|
||||
if (index < -1) {
|
||||
preSelect(index) {
|
||||
if (index < 0) {
|
||||
this.$refs.searchInput.focus()
|
||||
return
|
||||
}
|
||||
|
|
|
@ -131,7 +131,6 @@
|
|||
<label class="label">Labels</label>
|
||||
<div class="control">
|
||||
<multiselect
|
||||
:loading="labelService.loading"
|
||||
placeholder="Type to search for a label..."
|
||||
@search="findLabels"
|
||||
:search-results="foundLabels"
|
||||
|
@ -202,9 +201,9 @@ import PercentDoneSelect from '@/components/tasks/partials/percentDoneSelect'
|
|||
import Multiselect from '@/components/input/multiselect'
|
||||
|
||||
import UserService from '@/services/user'
|
||||
import LabelService from '@/services/label'
|
||||
import ListService from '@/services/list'
|
||||
import NamespaceService from '@/services/namespace'
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'filters',
|
||||
|
@ -243,21 +242,12 @@ export default {
|
|||
list_id: '',
|
||||
namespace: '',
|
||||
},
|
||||
flatPickerConfig: {
|
||||
altFormat: 'j M Y H:i',
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
enableTime: true,
|
||||
time_24hr: true,
|
||||
mode: 'range',
|
||||
},
|
||||
|
||||
usersService: UserService,
|
||||
foundusers: [],
|
||||
users: [],
|
||||
|
||||
labelService: LabelService,
|
||||
foundLabels: [],
|
||||
labelQuery: '',
|
||||
labels: [],
|
||||
|
||||
listsService: ListService,
|
||||
|
@ -271,7 +261,6 @@ export default {
|
|||
},
|
||||
created() {
|
||||
this.usersService = new UserService()
|
||||
this.labelService = new LabelService()
|
||||
this.listsService = new ListService()
|
||||
this.namespaceService = new NamespaceService()
|
||||
},
|
||||
|
@ -291,6 +280,30 @@ export default {
|
|||
this.prepareFilters()
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
foundLabels() {
|
||||
const labels = (Object.values(this.$store.state.labels.labels).filter(l => {
|
||||
return l.title.toLowerCase().includes(this.labelQuery.toLowerCase())
|
||||
}) ?? [])
|
||||
|
||||
return differenceWith(labels, this.labels, (first, second) => {
|
||||
return first.id === second.id
|
||||
})
|
||||
},
|
||||
...mapState({
|
||||
flatPickerConfig: state => ({
|
||||
altFormat: 'j M Y H:i',
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
enableTime: true,
|
||||
time_24hr: true,
|
||||
mode: 'range',
|
||||
locale: {
|
||||
firstDayOfWeek: state.auth.settings.weekStart,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
change() {
|
||||
this.$emit('input', this.params)
|
||||
|
@ -560,25 +573,8 @@ export default {
|
|||
this.$set(this.filters, filterName, ids.join(','))
|
||||
this.setSingleValueFilter(filterName, filterName, '', 'in')
|
||||
},
|
||||
clearLabels() {
|
||||
this.$set(this, 'foundLabels', [])
|
||||
},
|
||||
findLabels(query) {
|
||||
|
||||
if (query === '') {
|
||||
this.clearLabels()
|
||||
}
|
||||
|
||||
this.labelService.getAll({}, {s: query})
|
||||
.then(response => {
|
||||
// Filter the results to not include labels already selected
|
||||
this.$set(this, 'foundLabels', differenceWith(response, this.labels, (first, second) => {
|
||||
return first.id === second.id
|
||||
}))
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this)
|
||||
})
|
||||
this.labelQuery = query
|
||||
},
|
||||
addLabel() {
|
||||
this.$nextTick(() => {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div class="has-text-grey-light p-4" v-if="hintText !== ''">
|
||||
<div class="help has-text-grey-light p-2" v-if="hintText !== ''">
|
||||
{{ hintText }}
|
||||
</div>
|
||||
|
||||
|
@ -72,6 +72,11 @@ const CMD_NEW_LIST = 'newList'
|
|||
const CMD_NEW_NAMESPACE = 'newNamespace'
|
||||
const CMD_NEW_TEAM = 'newTeam'
|
||||
|
||||
const SEARCH_MODE_ALL = 'all'
|
||||
const SEARCH_MODE_TASKS = 'tasks'
|
||||
const SEARCH_MODE_LISTS = 'lists'
|
||||
const SEARCH_MODE_TEAMS = 'teams'
|
||||
|
||||
export default {
|
||||
name: 'quick-actions',
|
||||
data() {
|
||||
|
@ -84,6 +89,7 @@ export default {
|
|||
taskService: null,
|
||||
|
||||
foundTeams: [],
|
||||
teamSearchTimeout: null,
|
||||
teamService: null,
|
||||
|
||||
namespaceService: null,
|
||||
|
@ -99,9 +105,17 @@ export default {
|
|||
return active
|
||||
},
|
||||
results() {
|
||||
const lists = (Object.values(this.$store.state.lists).filter(l => {
|
||||
return l.title.toLowerCase().includes(this.query.toLowerCase())
|
||||
}) ?? [])
|
||||
let lists = []
|
||||
if (this.searchMode === SEARCH_MODE_ALL || this.searchMode === SEARCH_MODE_LISTS) {
|
||||
let query = this.query
|
||||
if (this.searchMode === SEARCH_MODE_LISTS) {
|
||||
query = query.substr(1)
|
||||
}
|
||||
|
||||
lists = (Object.values(this.$store.state.lists).filter(l => {
|
||||
return l.title.toLowerCase().includes(query.toLowerCase())
|
||||
}) ?? [])
|
||||
}
|
||||
|
||||
const cmds = this.availableCmds
|
||||
.filter(a => a.title.toLowerCase().includes(this.query.toLowerCase()))
|
||||
|
@ -167,7 +181,7 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
return 'You can use # to only seach for tasks, * to only search for lists and @ to only search for teams.'
|
||||
},
|
||||
currentList() {
|
||||
return Object.keys(this.$store.state[CURRENT_LIST]).length === 0 ? null : this.$store.state[CURRENT_LIST]
|
||||
|
@ -196,6 +210,23 @@ export default {
|
|||
|
||||
return cmds
|
||||
},
|
||||
searchMode() {
|
||||
if (this.query === '') {
|
||||
return SEARCH_MODE_ALL
|
||||
}
|
||||
|
||||
if (this.query.startsWith('#')) {
|
||||
return SEARCH_MODE_TASKS
|
||||
}
|
||||
if (this.query.startsWith('*')) {
|
||||
return SEARCH_MODE_LISTS
|
||||
}
|
||||
if (this.query.startsWith('@')) {
|
||||
return SEARCH_MODE_TEAMS
|
||||
}
|
||||
|
||||
return SEARCH_MODE_ALL
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.taskService = new TaskService()
|
||||
|
@ -206,9 +237,20 @@ export default {
|
|||
methods: {
|
||||
search() {
|
||||
this.searchTasks()
|
||||
this.searchTeams()
|
||||
},
|
||||
searchTasks() {
|
||||
if (this.query === '' || this.selectedCmd !== null) {
|
||||
if (this.searchMode !== SEARCH_MODE_ALL && this.searchMode !== SEARCH_MODE_TASKS) {
|
||||
this.foundTasks = []
|
||||
return
|
||||
}
|
||||
|
||||
let query = this.query
|
||||
if (this.searchMode === SEARCH_MODE_TASKS) {
|
||||
query = query.substr(1)
|
||||
}
|
||||
|
||||
if (query === '' || this.selectedCmd !== null) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -218,7 +260,7 @@ export default {
|
|||
}
|
||||
|
||||
this.taskSearchTimeout = setTimeout(() => {
|
||||
this.taskService.getAll({}, {s: this.query})
|
||||
this.taskService.getAll({}, {s: query})
|
||||
.then(r => {
|
||||
r = r.map(t => {
|
||||
t.type = TYPE_TASK
|
||||
|
@ -233,6 +275,37 @@ export default {
|
|||
})
|
||||
}, 150)
|
||||
},
|
||||
searchTeams() {
|
||||
if (this.searchMode !== SEARCH_MODE_ALL && this.searchMode !== SEARCH_MODE_TEAMS) {
|
||||
this.foundTeams = []
|
||||
return
|
||||
}
|
||||
|
||||
let query = this.query
|
||||
if (this.searchMode === SEARCH_MODE_TEAMS) {
|
||||
query = query.substr(1)
|
||||
}
|
||||
|
||||
if (query === '' || this.selectedCmd !== null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.teamSearchTimeout !== null) {
|
||||
clearTimeout(this.teamSearchTimeout)
|
||||
this.teamSearchTimeout = null
|
||||
}
|
||||
|
||||
this.teamSearchTimeout = setTimeout(() => {
|
||||
this.teamService.getAll({}, {s: query})
|
||||
.then(r => {
|
||||
r = r.map(t => {
|
||||
t.title = t.name
|
||||
return t
|
||||
})
|
||||
this.$set(this, 'foundTeams', r)
|
||||
})
|
||||
}, 150)
|
||||
},
|
||||
closeQuickActions() {
|
||||
this.$store.commit(QUICK_ACTIONS_ACTIVE, false)
|
||||
},
|
||||
|
@ -321,7 +394,6 @@ export default {
|
|||
.then(r => {
|
||||
this.$store.commit('namespaces/addNamespace', r)
|
||||
this.success({message: 'The namespace was successfully created.'}, this)
|
||||
this.$router.back()
|
||||
this.closeQuickActions()
|
||||
})
|
||||
.catch((e) => {
|
||||
|
|
|
@ -27,21 +27,24 @@
|
|||
Add
|
||||
</x-button>
|
||||
</p>
|
||||
<p class="help is-warning" v-if="!validListIdAvailable">
|
||||
No default list set. Please go to settings and specify default list.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LabelTask from '../../models/labelTask';
|
||||
import LabelModel from '../../models/label';
|
||||
import LabelTask from '../../models/labelTask'
|
||||
import LabelModel from '../../models/label'
|
||||
|
||||
import { HAS_TASKS } from '@/store/mutation-types';
|
||||
import { HAS_TASKS } from '@/store/mutation-types'
|
||||
// import Nothing from "@/components/misc/nothing";
|
||||
|
||||
import ListService from '../../services/list';
|
||||
import TaskService from '../../services/task';
|
||||
import TaskModel from '../../models/task';
|
||||
import LabelService from '../../services/label';
|
||||
import LabelTaskService from '../../services/labelTask';
|
||||
import ListService from '../../services/list'
|
||||
import TaskService from '../../services/task'
|
||||
import TaskModel from '../../models/task'
|
||||
import LabelService from '../../services/label'
|
||||
import LabelTaskService from '../../services/labelTask'
|
||||
|
||||
export default {
|
||||
name: 'add-task',
|
||||
|
@ -54,7 +57,7 @@ export default {
|
|||
labelTaskService: LabelTaskService,
|
||||
listIdForNewTask: undefined,
|
||||
validListIdAvailable: false,
|
||||
};
|
||||
}
|
||||
},
|
||||
components: {},
|
||||
props: {
|
||||
|
@ -64,151 +67,178 @@ export default {
|
|||
},
|
||||
},
|
||||
created() {
|
||||
this.listService = new ListService();
|
||||
this.taskService = new TaskService();
|
||||
this.labelService = new LabelService();
|
||||
this.labelTaskService = new LabelTaskService();
|
||||
this.listService = new ListService()
|
||||
this.taskService = new TaskService()
|
||||
this.labelService = new LabelService()
|
||||
this.labelTaskService = new LabelTaskService()
|
||||
},
|
||||
beforeMount() {
|
||||
console.log(this.listId);
|
||||
console.log(this.listId)
|
||||
if (this.listId !== undefined) {
|
||||
this.listIdForNewTask = this.listId;
|
||||
this.validListIdAvailable = true;
|
||||
this.listIdForNewTask = this.listId
|
||||
this.validListIdAvailable = true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addTask() {
|
||||
if (this.newTaskText === '') {
|
||||
this.showError = true;
|
||||
return;
|
||||
this.showError = true
|
||||
return
|
||||
}
|
||||
this.showError = false;
|
||||
this.showError = false
|
||||
|
||||
const task = new TaskModel({
|
||||
title: this.newTaskText,
|
||||
listId: this.listIdForNewTask,
|
||||
});
|
||||
})
|
||||
|
||||
this.taskService
|
||||
.create(task)
|
||||
.then(task => {
|
||||
this.newTaskText = '';
|
||||
this.newTaskText = ''
|
||||
|
||||
// Unlike a proper programming language, Javascript only knows references to objects and does not
|
||||
// allow you to control what is a reference and what isnt. Because of this we can't just add
|
||||
// all labels to the task they belong to right after we found and added them to the task since
|
||||
// the task update method also ensures all data the api sees has the right format. That means
|
||||
// it processes labels. That processing changes the date format and the label color and makes
|
||||
// the label pretty much unusable for everything else. Normally, this is not a big deal, because
|
||||
// the labels on a task get thrown away anyway and replaced with the new models from the api
|
||||
// when we get the updated answer back. However, in this specific case because we're passing a
|
||||
// label we obtained from vuex that reference is kept and not thrown away. The task itself gets
|
||||
// a new label object - you won't notice the bad reference until you want to add the same label
|
||||
// again and notice it doesn't have a color anymore.
|
||||
// I think this is what happens: (or rather would happen without the hack I've put in)
|
||||
// 1. Query the store for a label which matches the name
|
||||
// 2. Find one - remember, we get only a *reference* to the label from the store, not a new label object.
|
||||
// (Now there's *two* places with a reference to the same label object: in the store and in the
|
||||
// variable which holds the label from the search in the store)
|
||||
// 3. .push the label to the task
|
||||
// 4. Update the task to remove the labels from the name
|
||||
// 4.1. The task update processes all labels belonging to that task, changing attributes of our
|
||||
// label in the process. Because this is a reference, it is also "updated" in the store.
|
||||
// 5. Get an api response back. The service handler now creates a new label object for all labels
|
||||
// returned from the api. It will throw away all references to the old label in the process.
|
||||
// 6. Now we have two objects with the same label data: The old one we originally obtained from
|
||||
// the store and the one that was created when parsing the api response. The old one was
|
||||
// modified before sending the api request and thus, our store which still holds a reference
|
||||
// to the old label now contains old data.
|
||||
// I guess this is the point where normally the GC would come in and collect the old label
|
||||
// object if the store wouldn't still hold a reference to it.
|
||||
//
|
||||
// Now, as a workaround, I'm putting all new labels added to that task in this separate variable to
|
||||
// add them only after the task was updated to circumvent the task update service processing the
|
||||
// label before sending it. Feels more hacky than it probably is.
|
||||
const newLabels = []
|
||||
|
||||
// Check if the task has words starting with ~ in the title and make them to labels
|
||||
const parts = task.title.split(' ~');
|
||||
const parts = task.title.split(' ~')
|
||||
// The first element will always contain the title, even if there is no occurrence of ~
|
||||
if (parts.length > 1) {
|
||||
// First, create an unresolved promise for each entry in the array to wait
|
||||
// until all labels are added to update the task title once again
|
||||
let labelAddings = [];
|
||||
let labelAddsToWaitFor = [];
|
||||
let labelAddings = []
|
||||
let labelAddsToWaitFor = []
|
||||
parts.forEach((p, index) => {
|
||||
if (index < 1) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
labelAddsToWaitFor.push(
|
||||
new Promise((resolve, reject) => {
|
||||
labelAddings.push({ resolve: resolve, reject: reject });
|
||||
labelAddings.push({ resolve: resolve, reject: reject })
|
||||
}),
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
// Then do everything that is involved in finding, creating and adding the label to the task
|
||||
parts.forEach((p, index) => {
|
||||
if (index < 1) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// The part up until the next space
|
||||
const labelTitle = p.split(' ')[0];
|
||||
const labelTitle = p.split(' ')[0]
|
||||
|
||||
// Don't create an empty label
|
||||
if (labelTitle === '') {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the label exists
|
||||
this.labelService
|
||||
.getAll({}, { s: labelTitle })
|
||||
.then(res => {
|
||||
// Label found, use it
|
||||
if (res.length > 0 && res[0].title === labelTitle) {
|
||||
const label = Object.values(this.$store.state.labels.labels).find(l => {
|
||||
return l.title.toLowerCase() === labelTitle.toLowerCase()
|
||||
})
|
||||
|
||||
// Label found, use it
|
||||
if (typeof label !== 'undefined') {
|
||||
const labelTask = new LabelTask({
|
||||
taskId: task.id,
|
||||
labelId: label.id,
|
||||
})
|
||||
this.labelTaskService.create(labelTask)
|
||||
.then(result => {
|
||||
newLabels.push(label)
|
||||
|
||||
// Remove the label text from the task title
|
||||
task.title = task.title.replace(` ~${labelTitle}`, '')
|
||||
|
||||
// Make the promise done (the one with the index 0 does not exist)
|
||||
labelAddings[index - 1].resolve(result)
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this)
|
||||
})
|
||||
} else {
|
||||
// label not found, create it
|
||||
const label = new LabelModel({title: labelTitle})
|
||||
this.$store.dispatch('labels/createLabel', label)
|
||||
.then(res => {
|
||||
const labelTask = new LabelTask({
|
||||
taskId: task.id,
|
||||
labelId: res[0].id,
|
||||
});
|
||||
this.labelTaskService
|
||||
.create(labelTask)
|
||||
labelId: res.id,
|
||||
})
|
||||
this.labelTaskService.create(labelTask)
|
||||
.then(result => {
|
||||
task.labels.push(res[0]);
|
||||
newLabels.push(res)
|
||||
|
||||
// Remove the label text from the task title
|
||||
task.title = task.title.replace(` ~${labelTitle}`, '');
|
||||
task.title = task.title.replace(` ~${labelTitle}`, '')
|
||||
|
||||
// Make the promise done (the one with the index 0 does not exist)
|
||||
labelAddings[index - 1].resolve(result);
|
||||
labelAddings[index - 1].resolve(result)
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this);
|
||||
});
|
||||
} else {
|
||||
// label not found, create it
|
||||
const label = new LabelModel({ title: labelTitle });
|
||||
this.labelService
|
||||
.create(label)
|
||||
.then(res => {
|
||||
const labelTask = new LabelTask({
|
||||
taskId: task.id,
|
||||
labelId: res.id,
|
||||
});
|
||||
this.labelTaskService
|
||||
.create(labelTask)
|
||||
.then(result => {
|
||||
task.labels.push(res);
|
||||
|
||||
// Remove the label text from the task title
|
||||
task.title = task.title.replace(
|
||||
` ~${labelTitle}`,
|
||||
'',
|
||||
);
|
||||
|
||||
// Make the promise done (the one with the index 0 does not exist)
|
||||
labelAddings[index - 1].resolve(result);
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this);
|
||||
});
|
||||
this.error(e, this)
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this);
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// This waits to update the task until all labels have been added and the title has
|
||||
// been modified to remove each label text
|
||||
Promise.all(labelAddsToWaitFor).then(() => {
|
||||
this.taskService
|
||||
.update(task)
|
||||
.then(() => {
|
||||
this.$store.commit(HAS_TASKS, true);
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this);
|
||||
});
|
||||
});
|
||||
Promise.all(labelAddsToWaitFor)
|
||||
.then(() => {
|
||||
this.taskService.update(task)
|
||||
.then(updatedTask => {
|
||||
updatedTask.labels = newLabels
|
||||
this.$store.commit(HAS_TASKS, true)
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this)
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
this.$emit('taskAdded', task);
|
||||
this.$emit('taskAdded', task)
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this);
|
||||
});
|
||||
this.error(e, this)
|
||||
})
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -94,14 +94,6 @@ export default {
|
|||
newTask: TaskModel,
|
||||
isTaskEdit: false,
|
||||
taskEditTask: TaskModel,
|
||||
flatPickerConfig: {
|
||||
altFormat: 'j M Y H:i',
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
enableTime: true,
|
||||
onOpen: this.updateLastReminderDate,
|
||||
onClose: this.addReminderDate,
|
||||
},
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
<script>
|
||||
import TaskService from '../../../services/task'
|
||||
import flatPickr from 'vue-flatpickr-component'
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'defer-task',
|
||||
|
@ -51,15 +52,6 @@ export default {
|
|||
dueDate: null,
|
||||
lastValue: null,
|
||||
changeInterval: null,
|
||||
|
||||
flatPickerConfig: {
|
||||
altFormat: 'j M Y H:i',
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
enableTime: true,
|
||||
time_24hr: true,
|
||||
inline: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
@ -102,6 +94,19 @@ export default {
|
|||
this.lastValue = this.dueDate
|
||||
},
|
||||
},
|
||||
computed: mapState({
|
||||
flatPickerConfig: state => ({
|
||||
altFormat: 'j M Y H:i',
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
enableTime: true,
|
||||
time_24hr: true,
|
||||
inline: true,
|
||||
locale: {
|
||||
firstDayOfWeek: state.auth.settings.weekStart,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
methods: {
|
||||
deferDays(days) {
|
||||
this.dueDate = new Date(this.dueDate)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<multiselect
|
||||
:loading="labelService.loading || labelTaskService.loading"
|
||||
:loading="loading"
|
||||
placeholder="Type to add a new label..."
|
||||
:multiple="true"
|
||||
@search="findLabel"
|
||||
|
@ -11,6 +11,7 @@
|
|||
@create="createAndAddLabel"
|
||||
create-placeholder="Add this as new label"
|
||||
v-model="labels"
|
||||
:search-delay="10"
|
||||
>
|
||||
<template v-slot:tag="props">
|
||||
<span
|
||||
|
@ -39,11 +40,11 @@
|
|||
<script>
|
||||
import differenceWith from 'lodash/differenceWith'
|
||||
|
||||
import LabelService from '../../../services/label'
|
||||
import LabelModel from '../../../models/label'
|
||||
import LabelTaskService from '../../../services/labelTask'
|
||||
|
||||
import Multiselect from '@/components/input/multiselect'
|
||||
import {LOADING, LOADING_MODULE} from '@/store/mutation-types'
|
||||
|
||||
export default {
|
||||
name: 'edit-labels',
|
||||
|
@ -62,12 +63,10 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
labelService: LabelService,
|
||||
labelTaskService: LabelTaskService,
|
||||
foundLabels: [],
|
||||
labelTimeout: null,
|
||||
labels: [],
|
||||
searchQuery: '',
|
||||
query: '',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
@ -79,38 +78,26 @@ export default {
|
|||
},
|
||||
},
|
||||
created() {
|
||||
this.labelService = new LabelService()
|
||||
this.labelTaskService = new LabelTaskService()
|
||||
this.labels = this.value
|
||||
},
|
||||
computed: {
|
||||
foundLabels() {
|
||||
const labels = (Object.values(this.$store.state.labels.labels).filter(l => {
|
||||
return l.title.toLowerCase().includes(this.query.toLowerCase())
|
||||
}) ?? [])
|
||||
|
||||
return differenceWith(labels, this.labels, (first, second) => {
|
||||
return first.id === second.id
|
||||
})
|
||||
},
|
||||
loading() {
|
||||
return this.labelTaskService.loading || (this.$store.state[LOADING] && this.$store.state[LOADING_MODULE] === 'labels')
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
findLabel(query) {
|
||||
this.searchQuery = query
|
||||
if (query === '') {
|
||||
this.clearAllLabels()
|
||||
return
|
||||
}
|
||||
|
||||
if (this.labelTimeout !== null) {
|
||||
clearTimeout(this.labelTimeout)
|
||||
}
|
||||
|
||||
// Delay the search 300ms to not send a request on every keystroke
|
||||
this.labelTimeout = setTimeout(() => {
|
||||
this.labelService.getAll({}, {s: query})
|
||||
.then(response => {
|
||||
this.$set(this, 'foundLabels', differenceWith(response, this.labels, (first, second) => {
|
||||
return first.id === second.id
|
||||
}))
|
||||
this.labelTimeout = null
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this)
|
||||
})
|
||||
}, 300)
|
||||
},
|
||||
clearAllLabels() {
|
||||
this.$set(this, 'foundLabels', [])
|
||||
this.query = query
|
||||
},
|
||||
addLabel(label, showNotification = true) {
|
||||
this.$store.dispatch('tasks/addLabel', {label: label, taskId: this.taskId})
|
||||
|
@ -141,8 +128,8 @@ export default {
|
|||
})
|
||||
},
|
||||
createAndAddLabel(title) {
|
||||
let newLabel = new LabelModel({title: title})
|
||||
this.labelService.create(newLabel)
|
||||
const newLabel = new LabelModel({title: title})
|
||||
this.$store.dispatch('labels/createLabel', newLabel)
|
||||
.then(r => {
|
||||
this.addLabel(r, false)
|
||||
this.labels.push(r)
|
||||
|
@ -156,7 +143,3 @@ export default {
|
|||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -9,6 +9,7 @@ export default class UserSettingsModel extends AbstractModel {
|
|||
discoverableByName: false,
|
||||
discoverableByEmail: false,
|
||||
overdueTasksRemindersEnabled: true,
|
||||
weekStart: 0,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -129,8 +129,7 @@ export default class AbstractService {
|
|||
* @param route
|
||||
* @returns object
|
||||
*/
|
||||
getRouteReplacements(route) {
|
||||
let parameters = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}
|
||||
getRouteReplacements(route, parameters = {}) {
|
||||
let replace$$1 = {}
|
||||
let pattern = this.getRouteParameterPattern()
|
||||
pattern = new RegExp(pattern instanceof RegExp ? pattern.source : pattern, 'g')
|
||||
|
|
|
@ -17,6 +17,7 @@ import kanban from './modules/kanban'
|
|||
import tasks from './modules/tasks'
|
||||
import lists from './modules/lists'
|
||||
import attachments from './modules/attachments'
|
||||
import labels from './modules/labels'
|
||||
|
||||
import ListService from '../services/list'
|
||||
import {setTitle} from '@/helpers/setTitle'
|
||||
|
@ -32,6 +33,7 @@ export const store = new Vuex.Store({
|
|||
tasks,
|
||||
lists,
|
||||
attachments,
|
||||
labels,
|
||||
},
|
||||
state: {
|
||||
loading: false,
|
||||
|
|
|
@ -2,6 +2,13 @@ import {HTTPFactory} from '@/http-common'
|
|||
import {ERROR_MESSAGE, LOADING} from '../mutation-types'
|
||||
import UserModel from '../../models/user'
|
||||
|
||||
const defaultSettings = settings => {
|
||||
if (typeof settings.weekStart === 'undefined' || settings.weekStart === '') {
|
||||
settings.weekStart = 0
|
||||
}
|
||||
return settings
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: () => ({
|
||||
|
@ -20,12 +27,12 @@ export default {
|
|||
state.avatarUrl = info.getAvatarUrl()
|
||||
|
||||
if (info.settings) {
|
||||
state.settings = info.settings
|
||||
state.settings = defaultSettings(info.settings)
|
||||
}
|
||||
}
|
||||
},
|
||||
setUserSettings(state, settings) {
|
||||
state.settings = settings
|
||||
state.settings = defaultSettings(settings)
|
||||
const info = state.info !== null ? state.info : {}
|
||||
info.name = settings.name
|
||||
state.info = info
|
||||
|
|
100
src/store/modules/labels.js
Normal file
100
src/store/modules/labels.js
Normal file
|
@ -0,0 +1,100 @@
|
|||
import LabelService from '@/services/label'
|
||||
import Vue from 'vue'
|
||||
import {setLoading} from '@/store/helper'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
// The state is an object which has the label ids as keys.
|
||||
state: () => ({
|
||||
labels: {},
|
||||
loaded: false,
|
||||
}),
|
||||
mutations: {
|
||||
setLabels(state, labels) {
|
||||
labels.forEach(l => {
|
||||
Vue.set(state.labels, l.id, l)
|
||||
})
|
||||
},
|
||||
setLabel(state, label) {
|
||||
Vue.set(state.labels, label.id, label)
|
||||
},
|
||||
removeLabelById(state, label) {
|
||||
Vue.delete(state.labels, label.id)
|
||||
},
|
||||
setLoaded(state, loaded) {
|
||||
state.loaded = loaded
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
loadAllLabels(ctx, {forceLoad} = {}) {
|
||||
if (ctx.state.loaded && !forceLoad) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
const cancel = setLoading(ctx, 'labels')
|
||||
const labelService = new LabelService()
|
||||
|
||||
const getAllLabels = (page = 1) => {
|
||||
return labelService.getAll({}, {}, page)
|
||||
.then(labels => {
|
||||
if (page < labelService.totalPages) {
|
||||
return getAllLabels(page + 1)
|
||||
.then(nextLabels => {
|
||||
return labels.concat(nextLabels)
|
||||
})
|
||||
} else {
|
||||
return labels
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
return Promise.reject(e)
|
||||
})
|
||||
}
|
||||
|
||||
return getAllLabels()
|
||||
.then(r => {
|
||||
ctx.commit('setLabels', r)
|
||||
ctx.commit('setLoaded', true)
|
||||
return Promise.resolve(r)
|
||||
})
|
||||
.catch(e => Promise.reject(e))
|
||||
.finally(() => cancel())
|
||||
},
|
||||
deleteLabel(ctx, label) {
|
||||
const cancel = setLoading(ctx, 'labels')
|
||||
const labelService = new LabelService()
|
||||
|
||||
return labelService.delete(label)
|
||||
.then(r => {
|
||||
ctx.commit('removeLabelById', label)
|
||||
return Promise.resolve(r)
|
||||
})
|
||||
.catch(e => Promise.reject(e))
|
||||
.finally(() => cancel())
|
||||
},
|
||||
updateLabel(ctx, label) {
|
||||
const cancel = setLoading(ctx, 'labels')
|
||||
const labelService = new LabelService()
|
||||
|
||||
return labelService.update(label)
|
||||
.then(r => {
|
||||
ctx.commit('setLabel', r)
|
||||
return Promise.resolve(r)
|
||||
})
|
||||
.catch(e => Promise.reject(e))
|
||||
.finally(() => cancel())
|
||||
},
|
||||
createLabel(ctx, label) {
|
||||
const cancel = setLoading(ctx, 'labels')
|
||||
const labelService = new LabelService()
|
||||
|
||||
return labelService.create(label)
|
||||
.then(r => {
|
||||
ctx.commit('setLabel', r)
|
||||
return Promise.resolve(r)
|
||||
})
|
||||
.catch(e => Promise.reject(e))
|
||||
.finally(() => cancel())
|
||||
},
|
||||
},
|
||||
}
|
|
@ -152,23 +152,6 @@ $filter-container-top-link-share-list: -47px;
|
|||
margin-top: $filter-container-top-default;
|
||||
}
|
||||
|
||||
.is-archived {
|
||||
$notification-height: 1.25rem + 1.25rem + 1.5rem + 1.5rem;
|
||||
|
||||
.filter-container {
|
||||
margin-top: calc(#{$filter-container-top-default} - #{$notification-height});
|
||||
}
|
||||
|
||||
.link-share-container .gantt-chart-container .filter-container,
|
||||
.gantt-chart-container .filter-container {
|
||||
margin-top: calc(#{$filter-container-top-link-share-gantt} - 2rem - #{$notification-height});
|
||||
}
|
||||
|
||||
.link-share-container .list-view .filter-container {
|
||||
margin-top: calc(#{$filter-container-top-link-share-list} - #{$notification-height});
|
||||
}
|
||||
|
||||
.link-share-container .filter-container {
|
||||
margin-top: calc(#{$filter-container-top-default} - #{$notification-height});
|
||||
}
|
||||
.is-archived .notification.is-warning {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
|
|
@ -199,6 +199,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.link-share-container:not(.has-background) .task-view {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.task-view-container {
|
||||
padding-bottom: 1rem;
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
<h2>
|
||||
Hi {{ userInfo.name !== '' ? userInfo.name : userInfo.username }}!
|
||||
</h2>
|
||||
<div>{{ defaultListId }}</div>
|
||||
<add-task
|
||||
:listId="defaultListId"
|
||||
@taskAdded="updateTaskList"
|
||||
|
@ -35,9 +34,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import ShowTasks from './tasks/ShowTasks';
|
||||
import AddTask from '../components/tasks/add-task';
|
||||
import { mapState } from 'vuex'
|
||||
import ShowTasks from './tasks/ShowTasks'
|
||||
import AddTask from '../components/tasks/add-task'
|
||||
|
||||
export default {
|
||||
name: 'Home',
|
||||
|
@ -52,7 +51,7 @@ export default {
|
|||
tasks: [],
|
||||
defaultListId: undefined,
|
||||
showTasksKey: 0,
|
||||
};
|
||||
}
|
||||
},
|
||||
created() {
|
||||
//TODO: Load the value from user settings. Until then it will not render the add task component.
|
||||
|
@ -66,23 +65,23 @@ export default {
|
|||
hasTasks: state => state.hasTasks,
|
||||
defaultNamespaceId: state => {
|
||||
if (state.namespaces.namespaces.length === 0) {
|
||||
return 0;
|
||||
return 0
|
||||
}
|
||||
|
||||
return state.namespaces.namespaces[0].id;
|
||||
return state.namespaces.namespaces[0].id
|
||||
},
|
||||
hasLists: state => {
|
||||
if (state.namespaces.namespaces.length === 0) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
return state.namespaces.namespaces[0].lists.length > 0;
|
||||
return state.namespaces.namespaces[0].lists.length > 0
|
||||
},
|
||||
}),
|
||||
methods: {
|
||||
updateTaskList() {
|
||||
this.showTasksKey += 1;
|
||||
this.showTasksKey += 1
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div :class="{ 'is-loading': labelService.loading}" class="loader-container">
|
||||
<div :class="{ 'is-loading': loading}" class="loader-container">
|
||||
<x-button
|
||||
:to="{name:'labels.create'}"
|
||||
class="is-pulled-right"
|
||||
|
@ -76,7 +76,7 @@
|
|||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<x-button
|
||||
:loading="labelService.loading"
|
||||
:loading="loading"
|
||||
class="is-fullwidth"
|
||||
@click="editLabelSubmit()"
|
||||
>
|
||||
|
@ -101,11 +101,11 @@
|
|||
<script>
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
import LabelService from '../../services/label'
|
||||
import LabelModel from '../../models/label'
|
||||
import ColorPicker from '../../components/input/colorPicker'
|
||||
import LoadingComponent from '../../components/misc/loading'
|
||||
import ErrorComponent from '../../components/misc/error'
|
||||
import {LOADING, LOADING_MODULE} from '@/store/mutation-types'
|
||||
|
||||
export default {
|
||||
name: 'ListLabels',
|
||||
|
@ -120,15 +120,12 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
labelService: LabelService,
|
||||
labels: [],
|
||||
labelEditLabel: LabelModel,
|
||||
isLabelEdit: false,
|
||||
editorActive: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.labelService = new LabelService()
|
||||
this.labelEditLabel = new LabelModel()
|
||||
this.loadLabels()
|
||||
},
|
||||
|
@ -137,43 +134,19 @@ export default {
|
|||
},
|
||||
computed: mapState({
|
||||
userInfo: state => state.auth.info,
|
||||
labels: state => state.labels.labels,
|
||||
loading: state => state[LOADING] && state[LOADING_MODULE] === 'labels',
|
||||
}),
|
||||
methods: {
|
||||
loadLabels() {
|
||||
const getAllLabels = (page = 1) => {
|
||||
return this.labelService.getAll({}, {}, page)
|
||||
.then(labels => {
|
||||
if (page < this.labelService.totalPages) {
|
||||
return getAllLabels(page + 1)
|
||||
.then(nextLabels => {
|
||||
return labels.concat(nextLabels)
|
||||
})
|
||||
} else {
|
||||
return labels
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
return Promise.reject(e)
|
||||
})
|
||||
}
|
||||
|
||||
getAllLabels()
|
||||
.then(r => {
|
||||
this.$set(this, 'labels', r)
|
||||
})
|
||||
this.$store.dispatch('labels/loadAllLabels')
|
||||
.catch(e => {
|
||||
this.error(e, this)
|
||||
})
|
||||
},
|
||||
deleteLabel(label) {
|
||||
this.labelService.delete(label)
|
||||
this.$store.dispatch('labels/deleteLabel', label)
|
||||
.then(() => {
|
||||
// Remove the label from the list
|
||||
for (const l in this.labels) {
|
||||
if (this.labels[l].id === label.id) {
|
||||
this.labels.splice(l, 1)
|
||||
}
|
||||
}
|
||||
this.success({message: 'The label was successfully deleted.'}, this)
|
||||
})
|
||||
.catch(e => {
|
||||
|
@ -181,13 +154,8 @@ export default {
|
|||
})
|
||||
},
|
||||
editLabelSubmit() {
|
||||
this.labelService.update(this.labelEditLabel)
|
||||
.then(r => {
|
||||
for (const l in this.labels) {
|
||||
if (this.labels[l].id === r.id) {
|
||||
this.$set(this.labels, l, r)
|
||||
}
|
||||
}
|
||||
this.$store.dispatch('labels/updateLabel', this.labelEditLabel)
|
||||
.then(() => {
|
||||
this.success({message: 'The label was successfully updated.'}, this)
|
||||
})
|
||||
.catch(e => {
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
<label class="label" for="labelTitle">Label Title</label>
|
||||
<div
|
||||
class="control is-expanded"
|
||||
:class="{ 'is-loading': labelService.loading }"
|
||||
:class="{ 'is-loading': loading }"
|
||||
>
|
||||
<input
|
||||
:class="{ disabled: labelService.loading }"
|
||||
:class="{ disabled: loading }"
|
||||
class="input"
|
||||
placeholder="The label title goes here..."
|
||||
type="text"
|
||||
|
@ -28,7 +28,7 @@
|
|||
<div class="field">
|
||||
<label class="label">Color</label>
|
||||
<div class="control">
|
||||
<color-picker v-model="label.hexColor" />
|
||||
<color-picker v-model="label.hexColor"/>
|
||||
</div>
|
||||
</div>
|
||||
</create-edit>
|
||||
|
@ -36,17 +36,16 @@
|
|||
|
||||
<script>
|
||||
import labelModel from '../../models/label'
|
||||
import labelService from '../../services/label'
|
||||
import LabelModel from '../../models/label'
|
||||
import LabelService from '../../services/label'
|
||||
import CreateEdit from '@/components/misc/create-edit'
|
||||
import ColorPicker from '../../components/input/colorPicker'
|
||||
import {mapState} from 'vuex'
|
||||
import {LOADING, LOADING_MODULE} from '@/store/mutation-types'
|
||||
|
||||
export default {
|
||||
name: 'NewLabel',
|
||||
data() {
|
||||
return {
|
||||
labelService: labelService,
|
||||
label: labelModel,
|
||||
showError: false,
|
||||
}
|
||||
|
@ -56,12 +55,14 @@ export default {
|
|||
ColorPicker,
|
||||
},
|
||||
created() {
|
||||
this.labelService = new LabelService()
|
||||
this.label = new LabelModel()
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle('Create a new label')
|
||||
},
|
||||
computed: mapState({
|
||||
loading: state => state[LOADING] && state[LOADING_MODULE] === 'labels',
|
||||
}),
|
||||
methods: {
|
||||
newLabel() {
|
||||
if (this.label.title === '') {
|
||||
|
@ -70,17 +71,13 @@ export default {
|
|||
}
|
||||
this.showError = false
|
||||
|
||||
this.labelService
|
||||
.create(this.label)
|
||||
.then((response) => {
|
||||
this.$store.dispatch('labels/createLabel', this.label)
|
||||
.then(r => {
|
||||
this.$router.push({
|
||||
name: 'labels.index',
|
||||
params: { id: response.id },
|
||||
params: {id: r.id},
|
||||
})
|
||||
this.success(
|
||||
{ message: 'The label was successfully created.' },
|
||||
this,
|
||||
)
|
||||
this.success({message: 'The label was successfully created.'}, this)
|
||||
})
|
||||
.catch((e) => {
|
||||
this.error(e, this)
|
||||
|
|
|
@ -66,6 +66,7 @@ import GanttChart from '../../../components/tasks/gantt-component'
|
|||
import flatPickr from 'vue-flatpickr-component'
|
||||
import Fancycheckbox from '../../../components/input/fancycheckbox'
|
||||
import {saveListView} from '@/helpers/saveListView'
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'Gantt',
|
||||
|
@ -85,14 +86,19 @@ export default {
|
|||
dayWidth: 35,
|
||||
dateFrom: null,
|
||||
dateTo: null,
|
||||
flatPickerConfig: {
|
||||
altFormat: 'j M Y',
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d',
|
||||
enableTime: false,
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: mapState({
|
||||
flatPickerConfig: state => ({
|
||||
altFormat: 'j M Y',
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d',
|
||||
enableTime: false,
|
||||
locale: {
|
||||
firstDayOfWeek: state.auth.settings.weekStart,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
beforeMount() {
|
||||
this.dateFrom = new Date((new Date()).setDate((new Date()).getDate() - 15))
|
||||
this.dateTo = new Date((new Date()).setDate((new Date()).getDate() + 30))
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
@focusout="() => saveBucketTitle(bucket.id)"
|
||||
@keydown.enter.prevent.stop="() => saveBucketTitle(bucket.id)"
|
||||
class="title input"
|
||||
contenteditable="true"
|
||||
:contenteditable="canWrite"
|
||||
spellcheck="false">{{ bucket.title }}</h2>
|
||||
<span
|
||||
:class="{'is-max': bucket.tasks.length >= bucket.limit}"
|
||||
|
|
|
@ -64,19 +64,16 @@
|
|||
<add-task
|
||||
:listId="Number($route.params.listId)"
|
||||
@taskAdded="updateTaskList"
|
||||
ref="newTaskInput"
|
||||
/>
|
||||
<p class="help is-danger" v-if="showError && newTaskText === ''">
|
||||
Please specify a list title.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<nothing
|
||||
v-if="
|
||||
ctaVisible && tasks.length === 0 && !taskCollectionService.loading
|
||||
"
|
||||
>
|
||||
<nothing v-if="ctaVisible && tasks.length === 0 && !taskCollectionService.loading">
|
||||
This list is currently empty.
|
||||
<a @click="$refs.newTaskInput.focus()">Create a new task.</a>
|
||||
<a @click="focusNewTaskInput()">Create a new task.</a>
|
||||
</nothing>
|
||||
|
||||
<div class="tasks-container">
|
||||
|
@ -165,21 +162,20 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import TaskService from '../../../services/task';
|
||||
import TaskModel from '../../../models/task';
|
||||
import LabelTaskService from '../../../services/labelTask';
|
||||
import LabelService from '../../../services/label';
|
||||
import TaskService from '../../../services/task'
|
||||
import TaskModel from '../../../models/task'
|
||||
import LabelTaskService from '../../../services/labelTask'
|
||||
|
||||
import EditTask from '../../../components/tasks/edit-task';
|
||||
import AddTask from '../../../components/tasks/add-task';
|
||||
import SingleTaskInList from '../../../components/tasks/partials/singleTaskInList';
|
||||
import taskList from '../../../components/tasks/mixins/taskList';
|
||||
import { saveListView } from '@/helpers/saveListView';
|
||||
import Rights from '../../../models/rights.json';
|
||||
import { mapState } from 'vuex';
|
||||
import FilterPopup from '@/components/list/partials/filter-popup';
|
||||
import { HAS_TASKS } from '@/store/mutation-types';
|
||||
import Nothing from '@/components/misc/nothing';
|
||||
import EditTask from '../../../components/tasks/edit-task'
|
||||
import AddTask from '../../../components/tasks/add-task'
|
||||
import SingleTaskInList from '../../../components/tasks/partials/singleTaskInList'
|
||||
import taskList from '../../../components/tasks/mixins/taskList'
|
||||
import { saveListView } from '@/helpers/saveListView'
|
||||
import Rights from '../../../models/rights.json'
|
||||
import { mapState } from 'vuex'
|
||||
import FilterPopup from '@/components/list/partials/filter-popup'
|
||||
import { HAS_TASKS } from '@/store/mutation-types'
|
||||
import Nothing from '@/components/misc/nothing'
|
||||
|
||||
export default {
|
||||
name: 'List',
|
||||
|
@ -192,10 +188,9 @@ export default {
|
|||
|
||||
showError: false,
|
||||
labelTaskService: LabelTaskService,
|
||||
labelService: LabelService,
|
||||
|
||||
ctaVisible: false,
|
||||
};
|
||||
}
|
||||
},
|
||||
mixins: [taskList],
|
||||
components: {
|
||||
|
@ -206,56 +201,58 @@ export default {
|
|||
AddTask,
|
||||
},
|
||||
created() {
|
||||
this.taskService = new TaskService();
|
||||
this.labelService = new LabelService();
|
||||
this.labelTaskService = new LabelTaskService();
|
||||
this.taskService = new TaskService()
|
||||
this.labelTaskService = new LabelTaskService()
|
||||
|
||||
// Save the current list view to local storage
|
||||
// We use local storage and not vuex here to make it persistent across reloads.
|
||||
saveListView(this.$route.params.listId, this.$route.name);
|
||||
saveListView(this.$route.params.listId, this.$route.name)
|
||||
},
|
||||
computed: mapState({
|
||||
canWrite: state => state.currentList.maxRight > Rights.READ,
|
||||
list: state => state.currentList,
|
||||
}),
|
||||
mounted() {
|
||||
this.$nextTick(() => (this.ctaVisible = true));
|
||||
this.$nextTick(() => (this.ctaVisible = true))
|
||||
},
|
||||
methods: {
|
||||
// This function initializes the tasks page and loads the first page of tasks
|
||||
initTasks(page, search = '') {
|
||||
this.taskEditTask = null;
|
||||
this.isTaskEdit = false;
|
||||
this.loadTasks(page, search);
|
||||
this.taskEditTask = null
|
||||
this.isTaskEdit = false
|
||||
this.loadTasks(page, search)
|
||||
},
|
||||
focusNewTaskInput() {
|
||||
this.$refs.newTaskInput.$refs.newTaskInput.focus()
|
||||
},
|
||||
updateTaskList(task) {
|
||||
this.tasks.push(task);
|
||||
this.sortTasks();
|
||||
this.$store.commit(HAS_TASKS, true);
|
||||
this.tasks.push(task)
|
||||
this.sortTasks()
|
||||
this.$store.commit(HAS_TASKS, true)
|
||||
},
|
||||
editTask(id) {
|
||||
// Find the selected task and set it to the current object
|
||||
let theTask = this.getTaskById(id); // Somehow this does not work if we directly assign this to this.taskEditTask
|
||||
this.taskEditTask = theTask;
|
||||
this.isTaskEdit = true;
|
||||
let theTask = this.getTaskById(id) // Somehow this does not work if we directly assign this to this.taskEditTask
|
||||
this.taskEditTask = theTask
|
||||
this.isTaskEdit = true
|
||||
},
|
||||
getTaskById(id) {
|
||||
for (const t in this.tasks) {
|
||||
if (this.tasks[t].id === parseInt(id)) {
|
||||
return this.tasks[t];
|
||||
return this.tasks[t]
|
||||
}
|
||||
}
|
||||
return {}; // FIXME: This should probably throw something to make it clear to the user noting was found
|
||||
return {} // FIXME: This should probably throw something to make it clear to the user noting was found
|
||||
},
|
||||
updateTasks(updatedTask) {
|
||||
for (const t in this.tasks) {
|
||||
if (this.tasks[t].id === updatedTask.id) {
|
||||
this.$set(this.tasks, t, updatedTask);
|
||||
break;
|
||||
this.$set(this.tasks, t, updatedTask)
|
||||
break
|
||||
}
|
||||
}
|
||||
this.sortTasks();
|
||||
this.sortTasks()
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -108,7 +108,7 @@ export default {
|
|||
}
|
||||
},
|
||||
created() {
|
||||
this.showArchived = localStorage.getItem('showArchived') ?? false
|
||||
this.showArchived = JSON.parse(localStorage.getItem('showArchived')) ?? false
|
||||
this.loadBackgroundsForLists()
|
||||
},
|
||||
mounted() {
|
||||
|
@ -147,7 +147,7 @@ export default {
|
|||
.catch(e => this.error(e, this))
|
||||
},
|
||||
saveShowArchivedState() {
|
||||
localStorage.setItem('showArchived', this.showArchived)
|
||||
localStorage.setItem('showArchived', JSON.stringify(this.showArchived))
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="is-max-width-desktop show-tasks">
|
||||
<fancycheckbox
|
||||
@change="loadPendingTasks"
|
||||
@change="setDate"
|
||||
class="is-pulled-right"
|
||||
v-if="!showAll"
|
||||
v-model="showNulls"
|
||||
|
@ -15,7 +15,7 @@
|
|||
:class="{ 'disabled': taskService.loading}"
|
||||
:config="flatPickerConfig"
|
||||
:disabled="taskService.loading"
|
||||
@on-close="loadPendingTasks"
|
||||
@on-close="setDate"
|
||||
class="input"
|
||||
v-model="cStartDate"
|
||||
/>
|
||||
|
@ -24,7 +24,7 @@
|
|||
:class="{ 'disabled': taskService.loading}"
|
||||
:config="flatPickerConfig"
|
||||
:disabled="taskService.loading"
|
||||
@on-close="loadPendingTasks"
|
||||
@on-close="setDate"
|
||||
class="input"
|
||||
v-model="cEndDate"
|
||||
/>
|
||||
|
@ -81,14 +81,6 @@ export default {
|
|||
cEndDate: null,
|
||||
|
||||
showNothingToDo: false,
|
||||
|
||||
flatPickerConfig: {
|
||||
altFormat: 'j M Y H:i',
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
enableTime: true,
|
||||
time_24hr: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
props: {
|
||||
|
@ -116,8 +108,29 @@ export default {
|
|||
},
|
||||
computed: mapState({
|
||||
userAuthenticated: state => state.auth.authenticated,
|
||||
flatPickerConfig: state => ({
|
||||
altFormat: 'j M Y H:i',
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
enableTime: true,
|
||||
time_24hr: true,
|
||||
locale: {
|
||||
firstDayOfWeek: state.auth.settings.weekStart,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
methods: {
|
||||
setDate() {
|
||||
this.$router.push({
|
||||
name: this.$route.name,
|
||||
query: {
|
||||
from: +new Date(this.cStartDate),
|
||||
to: +new Date(this.cEndDate),
|
||||
showOverdue: this.showOverdue,
|
||||
showNulls: this.showNulls,
|
||||
},
|
||||
})
|
||||
},
|
||||
loadPendingTasks() {
|
||||
// Since this route is authentication only, users would get an error message if they access the page unauthenticated.
|
||||
// Since this component is mounted as the home page before unauthenticated users get redirected
|
||||
|
@ -127,8 +140,15 @@ export default {
|
|||
}
|
||||
|
||||
// Make sure all dates are date objects
|
||||
this.cStartDate = new Date(this.cStartDate)
|
||||
this.cEndDate = new Date(this.cEndDate)
|
||||
if (typeof this.$route.query.from !== 'undefined' && typeof this.$route.query.to !== 'undefined') {
|
||||
this.cStartDate = new Date(Number(this.$route.query.from))
|
||||
this.cEndDate = new Date(Number(this.$route.query.to))
|
||||
} else {
|
||||
this.cStartDate = new Date(this.cStartDate)
|
||||
this.cEndDate = new Date(this.cEndDate)
|
||||
}
|
||||
this.showOverdue = this.$route.query.showOverdue
|
||||
this.showNulls = this.$route.query.showNulls
|
||||
|
||||
if (this.showAll) {
|
||||
this.setTitle('Current Tasks')
|
||||
|
@ -177,9 +197,7 @@ export default {
|
|||
r.sort((a, b) => {
|
||||
return a.dueDate === null && b.dueDate === null ? -1 : 1
|
||||
})
|
||||
const tasks = r.
|
||||
filter(t => t.dueDate !== null).
|
||||
concat(r.filter(t => t.dueDate === null))
|
||||
const tasks = r.filter(t => t.dueDate !== null).concat(r.filter(t => t.dueDate === null))
|
||||
|
||||
this.$set(this, 'tasks', tasks)
|
||||
this.$store.commit(HAS_TASKS, r.length > 0)
|
||||
|
@ -205,21 +223,20 @@ export default {
|
|||
this.cStartDate = new Date()
|
||||
this.cEndDate = new Date((new Date()).getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
this.showOverdue = false
|
||||
this.loadPendingTasks()
|
||||
this.setDate()
|
||||
},
|
||||
setDatesToNextMonth() {
|
||||
this.cStartDate = new Date()
|
||||
this.cEndDate = new Date((new Date()).setMonth((new Date()).getMonth() + 1))
|
||||
this.showOverdue = false
|
||||
this.loadPendingTasks()
|
||||
this.setDate()
|
||||
},
|
||||
showTodaysTasks() {
|
||||
const d = new Date()
|
||||
this.cStartDate = new Date()
|
||||
this.cEndDate = new Date(d.setDate(d.getDate() + 1))
|
||||
this.showNulls = false
|
||||
this.showOverdue = true
|
||||
this.loadPendingTasks()
|
||||
this.setDate()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -17,13 +17,10 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
startDate: new Date(this.$route.params.startDateUnix),
|
||||
endDate: new Date(this.$route.params.endDateUnix),
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route': 'setDatesToNextWeek',
|
||||
},
|
||||
created() {
|
||||
this.setDatesToNextWeek()
|
||||
},
|
||||
|
|
|
@ -509,6 +509,9 @@ export default {
|
|||
this.loadTask()
|
||||
},
|
||||
computed: {
|
||||
currentList() {
|
||||
return this.$store.state[CURRENT_LIST]
|
||||
},
|
||||
parent() {
|
||||
if (!this.task.listId) {
|
||||
return {
|
||||
|
@ -522,11 +525,11 @@ export default {
|
|||
}
|
||||
|
||||
const list = this.$store.getters['namespaces/getListAndNamespaceById'](this.task.listId)
|
||||
this.$store.commit(CURRENT_LIST, list.list)
|
||||
this.$store.commit(CURRENT_LIST, list !== null ? list.list : this.currentList)
|
||||
return list
|
||||
},
|
||||
canWrite() {
|
||||
return this.task && this.task.maxRight && this.task.maxRight > rights.READ
|
||||
return typeof this.task !== 'undefined' && typeof this.task.maxRight !== 'undefined' && this.task.maxRight > rights.READ
|
||||
},
|
||||
updatedSince() {
|
||||
return this.formatDateSince(this.task.updated)
|
||||
|
|
|
@ -132,6 +132,19 @@
|
|||
Play a sound when marking tasks as done
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="is-flex is-align-items-center">
|
||||
<span>
|
||||
Week starts on
|
||||
</span>
|
||||
<div class="select ml-2">
|
||||
<select v-model.number="settings.weekStart">
|
||||
<option value="0">Sunday</option>
|
||||
<option value="1">Monday</option>
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<x-button
|
||||
:loading="userSettingsService.loading"
|
||||
|
|
239
yarn.lock
239
yarn.lock
|
@ -1716,15 +1716,15 @@
|
|||
debug "^3.1.0"
|
||||
lodash.once "^4.1.1"
|
||||
|
||||
"@eslint/eslintrc@^0.4.1":
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.1.tgz#442763b88cecbe3ee0ec7ca6d6dd6168550cbf14"
|
||||
integrity sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ==
|
||||
"@eslint/eslintrc@^0.4.2":
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179"
|
||||
integrity sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==
|
||||
dependencies:
|
||||
ajv "^6.12.4"
|
||||
debug "^4.1.1"
|
||||
espree "^7.3.0"
|
||||
globals "^12.1.0"
|
||||
globals "^13.9.0"
|
||||
ignore "^4.0.6"
|
||||
import-fresh "^3.2.1"
|
||||
js-yaml "^3.13.1"
|
||||
|
@ -1843,13 +1843,13 @@
|
|||
jest-util "^27.0.2"
|
||||
slash "^3.0.0"
|
||||
|
||||
"@jest/core@^27.0.3":
|
||||
version "27.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.0.3.tgz#b5a38675fa0466450a7fd465f4b226762cb592a2"
|
||||
integrity sha512-rN8lr/OJ8iApcQUh4khnMaOCVX4oRnLwy2tPW3Vh70y62K8Da8fhkxMUq0xX9VPa4+yWUm0tGc/jUSJi+Jzuwg==
|
||||
"@jest/core@^27.0.4":
|
||||
version "27.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.0.4.tgz#679bf9ac07900da2ddbb9667bb1afa8029038f53"
|
||||
integrity sha512-+dsmV8VUs1h/Szb+rEWk8xBM1fp1I///uFy9nk3wXGvRsF2lBp8EVPmtWc+QFRb3MY2b7u2HbkGF1fzoDzQTLA==
|
||||
dependencies:
|
||||
"@jest/console" "^27.0.2"
|
||||
"@jest/reporters" "^27.0.2"
|
||||
"@jest/reporters" "^27.0.4"
|
||||
"@jest/test-result" "^27.0.2"
|
||||
"@jest/transform" "^27.0.2"
|
||||
"@jest/types" "^27.0.2"
|
||||
|
@ -1860,15 +1860,15 @@
|
|||
exit "^0.1.2"
|
||||
graceful-fs "^4.2.4"
|
||||
jest-changed-files "^27.0.2"
|
||||
jest-config "^27.0.3"
|
||||
jest-config "^27.0.4"
|
||||
jest-haste-map "^27.0.2"
|
||||
jest-message-util "^27.0.2"
|
||||
jest-regex-util "^27.0.1"
|
||||
jest-resolve "^27.0.2"
|
||||
jest-resolve-dependencies "^27.0.3"
|
||||
jest-runner "^27.0.3"
|
||||
jest-runtime "^27.0.3"
|
||||
jest-snapshot "^27.0.2"
|
||||
jest-resolve "^27.0.4"
|
||||
jest-resolve-dependencies "^27.0.4"
|
||||
jest-runner "^27.0.4"
|
||||
jest-runtime "^27.0.4"
|
||||
jest-snapshot "^27.0.4"
|
||||
jest-util "^27.0.2"
|
||||
jest-validate "^27.0.2"
|
||||
jest-watcher "^27.0.2"
|
||||
|
@ -1909,10 +1909,10 @@
|
|||
"@jest/types" "^27.0.2"
|
||||
expect "^27.0.2"
|
||||
|
||||
"@jest/reporters@^27.0.2":
|
||||
version "27.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.0.2.tgz#ad73835d1cd54da08b0998a70b14446405e8e0d9"
|
||||
integrity sha512-SVQjew/kafNxSN1my4praGQP+VPVGHsU8zqiEDppLvq6j1lryIjdNb9P+bZSsKeifU4bIoaPnf9Ui0tK9WOpFA==
|
||||
"@jest/reporters@^27.0.4":
|
||||
version "27.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.0.4.tgz#95609b1be97afb80d55d8aa3d7c3179c15810e65"
|
||||
integrity sha512-Xa90Nm3JnV0xCe4M6A10M9WuN9krb+WFKxV1A98Y4ePCw40n++r7uxFUNU7DT1i9Behj7fjrAIju9oU0t1QtCg==
|
||||
dependencies:
|
||||
"@bcoe/v8-coverage" "^0.2.3"
|
||||
"@jest/console" "^27.0.2"
|
||||
|
@ -1930,7 +1930,7 @@
|
|||
istanbul-lib-source-maps "^4.0.0"
|
||||
istanbul-reports "^3.0.2"
|
||||
jest-haste-map "^27.0.2"
|
||||
jest-resolve "^27.0.2"
|
||||
jest-resolve "^27.0.4"
|
||||
jest-util "^27.0.2"
|
||||
jest-worker "^27.0.2"
|
||||
slash "^3.0.0"
|
||||
|
@ -1958,15 +1958,15 @@
|
|||
"@types/istanbul-lib-coverage" "^2.0.0"
|
||||
collect-v8-coverage "^1.0.0"
|
||||
|
||||
"@jest/test-sequencer@^27.0.3":
|
||||
version "27.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.0.3.tgz#2a8632b86a9a6f8900e514917cdab6a062e71049"
|
||||
integrity sha512-DcLTzraZ8xLr5fcIl+CF14vKeBBpBrn55wFxI9Ju+dhEBdjRdJQ/Z/pLkMehkPZWIQ+rR23J8e+wFDkfjree0Q==
|
||||
"@jest/test-sequencer@^27.0.4":
|
||||
version "27.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.0.4.tgz#976493b277594d81e589896f0ed21f198308928a"
|
||||
integrity sha512-6UFEVwdmxYdyNffBxVVZxmXEdBE4riSddXYSnFNH0ELFQFk/bvagizim8WfgJTqF4EKd+j1yFxvhb8BMHfOjSQ==
|
||||
dependencies:
|
||||
"@jest/test-result" "^27.0.2"
|
||||
graceful-fs "^4.2.4"
|
||||
jest-haste-map "^27.0.2"
|
||||
jest-runtime "^27.0.3"
|
||||
jest-runtime "^27.0.4"
|
||||
|
||||
"@jest/transform@^27.0.2":
|
||||
version "27.0.2"
|
||||
|
@ -6243,13 +6243,13 @@ eslint-visitor-keys@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
|
||||
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
|
||||
|
||||
eslint@7.27.0:
|
||||
version "7.27.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.27.0.tgz#665a1506d8f95655c9274d84bd78f7166b07e9c7"
|
||||
integrity sha512-JZuR6La2ZF0UD384lcbnd0Cgg6QJjiCwhMD6eU4h/VGPcVGwawNNzKU41tgokGXnfjOOyI6QIffthhJTPzzuRA==
|
||||
eslint@7.28.0:
|
||||
version "7.28.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.28.0.tgz#435aa17a0b82c13bb2be9d51408b617e49c1e820"
|
||||
integrity sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==
|
||||
dependencies:
|
||||
"@babel/code-frame" "7.12.11"
|
||||
"@eslint/eslintrc" "^0.4.1"
|
||||
"@eslint/eslintrc" "^0.4.2"
|
||||
ajv "^6.10.0"
|
||||
chalk "^4.0.0"
|
||||
cross-spawn "^7.0.2"
|
||||
|
@ -6266,7 +6266,7 @@ eslint@7.27.0:
|
|||
fast-deep-equal "^3.1.3"
|
||||
file-entry-cache "^6.0.1"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
glob-parent "^5.0.0"
|
||||
glob-parent "^5.1.2"
|
||||
globals "^13.6.0"
|
||||
ignore "^4.0.6"
|
||||
import-fresh "^3.0.0"
|
||||
|
@ -7250,13 +7250,6 @@ glob-parent@^3.1.0:
|
|||
is-glob "^3.1.0"
|
||||
path-dirname "^1.0.0"
|
||||
|
||||
glob-parent@^5.0.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2"
|
||||
integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
glob-parent@^5.1.0:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
|
||||
|
@ -7264,7 +7257,7 @@ glob-parent@^5.1.0:
|
|||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
glob-parent@~5.1.0:
|
||||
glob-parent@^5.1.2, glob-parent@~5.1.0:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
||||
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
||||
|
@ -7307,13 +7300,6 @@ globals@^11.1.0:
|
|||
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
||||
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
||||
|
||||
globals@^12.1.0:
|
||||
version "12.3.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-12.3.0.tgz#1e564ee5c4dded2ab098b0f88f24702a3c56be13"
|
||||
integrity sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==
|
||||
dependencies:
|
||||
type-fest "^0.8.1"
|
||||
|
||||
globals@^13.6.0:
|
||||
version "13.6.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-13.6.0.tgz#d77138e53738567bb96a3916ff6f6b487af20ef7"
|
||||
|
@ -7321,6 +7307,13 @@ globals@^13.6.0:
|
|||
dependencies:
|
||||
type-fest "^0.20.2"
|
||||
|
||||
globals@^13.9.0:
|
||||
version "13.9.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-13.9.0.tgz#4bf2bf635b334a173fb1daf7c5e6b218ecdc06cb"
|
||||
integrity sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==
|
||||
dependencies:
|
||||
type-fest "^0.20.2"
|
||||
|
||||
globby@^11.0.2:
|
||||
version "11.0.2"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.2.tgz#1af538b766a3b540ebfb58a32b2e2d5897321d83"
|
||||
|
@ -7619,10 +7612,10 @@ hex-color-regex@^1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
|
||||
integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
|
||||
|
||||
highlight.js@11.0.0:
|
||||
version "11.0.0"
|
||||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.0.0.tgz#e22ac9ca45edc4f87a2187685d591a108ceb8449"
|
||||
integrity sha512-ByaTMfsSuoqerTwemOgpIhfULEIaK52JJYhky/sK7/Yqc0+t7Uh5DHay9vIC94YXSupnQ1Vqfc9VXrYP4eXW3Q==
|
||||
highlight.js@11.0.1:
|
||||
version "11.0.1"
|
||||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.0.1.tgz#a78bafccd9aa297978799fe5eed9beb7ee1ef887"
|
||||
integrity sha512-EqYpWyTF2s8nMfttfBA2yLKPNoZCO33pLS4MnbXQ4hECf1TKujCt1Kq7QAdrio7roL4+CqsfjqwYj4tYgq0pJQ==
|
||||
|
||||
highlight.js@^9.6.0:
|
||||
version "9.17.1"
|
||||
|
@ -8554,10 +8547,10 @@ jest-changed-files@^27.0.2:
|
|||
execa "^5.0.0"
|
||||
throat "^6.0.1"
|
||||
|
||||
jest-circus@^27.0.3:
|
||||
version "27.0.3"
|
||||
resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.0.3.tgz#32006967de484e03589da944064d72e172ce3261"
|
||||
integrity sha512-tdMfzs7SgD5l7jRcI1iB3vtQi5fHwCgo4RlO8bzZnYc05PZ+tlAOMZeS8eGYkZ2tPaRY/aRLMFWQp/8zXBrolQ==
|
||||
jest-circus@^27.0.4:
|
||||
version "27.0.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.0.4.tgz#3b261514ee3b3da33def736a6352c98ff56bb6e6"
|
||||
integrity sha512-QD+eblDiRphta630WRKewuASLs/oY1Zki2G4bccntRvrTHQ63ljwFR5TLduuK4Zg0ZPzW0+8o6AP7KRd1yKOjw==
|
||||
dependencies:
|
||||
"@jest/environment" "^27.0.3"
|
||||
"@jest/test-result" "^27.0.2"
|
||||
|
@ -8571,39 +8564,39 @@ jest-circus@^27.0.3:
|
|||
jest-each "^27.0.2"
|
||||
jest-matcher-utils "^27.0.2"
|
||||
jest-message-util "^27.0.2"
|
||||
jest-runtime "^27.0.3"
|
||||
jest-snapshot "^27.0.2"
|
||||
jest-runtime "^27.0.4"
|
||||
jest-snapshot "^27.0.4"
|
||||
jest-util "^27.0.2"
|
||||
pretty-format "^27.0.2"
|
||||
slash "^3.0.0"
|
||||
stack-utils "^2.0.3"
|
||||
throat "^6.0.1"
|
||||
|
||||
jest-cli@^27.0.3:
|
||||
version "27.0.3"
|
||||
resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.0.3.tgz#b733871acb526054a0f8c971d0466595c5f8316d"
|
||||
integrity sha512-7bt9Sgv4nWH5pUnyJfdLf8CHWfo4+7lSPxeBwQx4r0vBj9jweJam/piE2U91SXtQI+ckm+TIN97OVnqIYpVhSg==
|
||||
jest-cli@^27.0.4:
|
||||
version "27.0.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.0.4.tgz#491b12c754c0d7c6873b13a66f26b3a80a852910"
|
||||
integrity sha512-E0T+/i2lxsWAzV7LKYd0SB7HUAvePqaeIh5vX43/G5jXLhv1VzjYzJAGEkTfvxV774ll9cyE2ljcL73PVMEOXQ==
|
||||
dependencies:
|
||||
"@jest/core" "^27.0.3"
|
||||
"@jest/core" "^27.0.4"
|
||||
"@jest/test-result" "^27.0.2"
|
||||
"@jest/types" "^27.0.2"
|
||||
chalk "^4.0.0"
|
||||
exit "^0.1.2"
|
||||
graceful-fs "^4.2.4"
|
||||
import-local "^3.0.2"
|
||||
jest-config "^27.0.3"
|
||||
jest-config "^27.0.4"
|
||||
jest-util "^27.0.2"
|
||||
jest-validate "^27.0.2"
|
||||
prompts "^2.0.1"
|
||||
yargs "^16.0.3"
|
||||
|
||||
jest-config@^27.0.3:
|
||||
version "27.0.3"
|
||||
resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.0.3.tgz#31871583573c6d669dcdb5bb2d1a8738f3b91c20"
|
||||
integrity sha512-zgtI2YQo+ekKsmYNyDlXFY/7w7WWBSJFoj/WRe173WB88CDUrEYWr0sLdbLOQe+sRu6l1Y2S0MCS6BOJm5jkoA==
|
||||
jest-config@^27.0.4:
|
||||
version "27.0.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.0.4.tgz#c4f41378acf40ca77860fb4e213b12109d87b8cf"
|
||||
integrity sha512-VkQFAHWnPQefdvHU9A+G3H/Z3NrrTKqWpvxgQz3nkUdkDTWeKJE6e//BL+R7z79dXOMVksYgM/z6ndtN0hfChg==
|
||||
dependencies:
|
||||
"@babel/core" "^7.1.0"
|
||||
"@jest/test-sequencer" "^27.0.3"
|
||||
"@jest/test-sequencer" "^27.0.4"
|
||||
"@jest/types" "^27.0.2"
|
||||
babel-jest "^27.0.2"
|
||||
chalk "^4.0.0"
|
||||
|
@ -8611,14 +8604,14 @@ jest-config@^27.0.3:
|
|||
glob "^7.1.1"
|
||||
graceful-fs "^4.2.4"
|
||||
is-ci "^3.0.0"
|
||||
jest-circus "^27.0.3"
|
||||
jest-circus "^27.0.4"
|
||||
jest-environment-jsdom "^27.0.3"
|
||||
jest-environment-node "^27.0.3"
|
||||
jest-get-type "^27.0.1"
|
||||
jest-jasmine2 "^27.0.3"
|
||||
jest-jasmine2 "^27.0.4"
|
||||
jest-regex-util "^27.0.1"
|
||||
jest-resolve "^27.0.2"
|
||||
jest-runner "^27.0.3"
|
||||
jest-resolve "^27.0.4"
|
||||
jest-runner "^27.0.4"
|
||||
jest-util "^27.0.2"
|
||||
jest-validate "^27.0.2"
|
||||
micromatch "^4.0.4"
|
||||
|
@ -8702,10 +8695,10 @@ jest-haste-map@^27.0.2:
|
|||
optionalDependencies:
|
||||
fsevents "^2.3.2"
|
||||
|
||||
jest-jasmine2@^27.0.3:
|
||||
version "27.0.3"
|
||||
resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.0.3.tgz#fa6f6499566ea1b01b68b3ad13f49d1592b02c85"
|
||||
integrity sha512-odJ2ia8P5c+IsqOcWJPmku4AqbXIfTVLRjYTKHri3TEvbmTdLw0ghy13OAPIl/0v7cVH0TURK7+xFOHKDLvKIA==
|
||||
jest-jasmine2@^27.0.4:
|
||||
version "27.0.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.0.4.tgz#c669519ccf4904a485338555e1e66cad36bb0670"
|
||||
integrity sha512-yj3WrjjquZwkJw+eA4c9yucHw4/+EHndHWSqgHbHGQfT94ihaaQsa009j1a0puU8CNxPDk0c1oAPeOpdJUElwA==
|
||||
dependencies:
|
||||
"@babel/traverse" "^7.1.0"
|
||||
"@jest/environment" "^27.0.3"
|
||||
|
@ -8720,8 +8713,8 @@ jest-jasmine2@^27.0.3:
|
|||
jest-each "^27.0.2"
|
||||
jest-matcher-utils "^27.0.2"
|
||||
jest-message-util "^27.0.2"
|
||||
jest-runtime "^27.0.3"
|
||||
jest-snapshot "^27.0.2"
|
||||
jest-runtime "^27.0.4"
|
||||
jest-snapshot "^27.0.4"
|
||||
jest-util "^27.0.2"
|
||||
pretty-format "^27.0.2"
|
||||
throat "^6.0.1"
|
||||
|
@ -8777,19 +8770,19 @@ jest-regex-util@^27.0.1:
|
|||
resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.1.tgz#69d4b1bf5b690faa3490113c47486ed85dd45b68"
|
||||
integrity sha512-6nY6QVcpTgEKQy1L41P4pr3aOddneK17kn3HJw6SdwGiKfgCGTvH02hVXL0GU8GEKtPH83eD2DIDgxHXOxVohQ==
|
||||
|
||||
jest-resolve-dependencies@^27.0.3:
|
||||
version "27.0.3"
|
||||
resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.3.tgz#7e258f7d0458bb910855f8a50f5c1e9d92c319dc"
|
||||
integrity sha512-HdjWOvFAgT5CYChF2eiBN2rRKicjaTCCtA3EtH47REIdGzEHGUhYrWYgLahXsiOovvWN6edhcHL5WCa3gbc04A==
|
||||
jest-resolve-dependencies@^27.0.4:
|
||||
version "27.0.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.4.tgz#a07a242d70d668afd3fcf7f4270755eebb1fe579"
|
||||
integrity sha512-F33UPfw1YGWCV2uxJl7wD6TvcQn5IC0LtguwY3r4L7R6H4twpLkp5Q2ZfzRx9A2I3G8feiy0O0sqcn/Qoym71A==
|
||||
dependencies:
|
||||
"@jest/types" "^27.0.2"
|
||||
jest-regex-util "^27.0.1"
|
||||
jest-snapshot "^27.0.2"
|
||||
jest-snapshot "^27.0.4"
|
||||
|
||||
jest-resolve@^27.0.2:
|
||||
version "27.0.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.0.2.tgz#087a3ed17182722a3415f92bfacc99c49cf8a965"
|
||||
integrity sha512-rmfLGyZhwAUR5z3EwPAW7LQTorWAuCYCcsQJoQxT2it+BOgX3zKxa67r1pfpK3ihy2k9TjYD3/lMp5rPm/CL1Q==
|
||||
jest-resolve@^27.0.4:
|
||||
version "27.0.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.0.4.tgz#8a27bc3f2f00c8ea28f3bc99bbf6f468300a703d"
|
||||
integrity sha512-BcfyK2i3cG79PDb/6gB6zFeFQlcqLsQjGBqznFCpA0L/3l1L/oOsltdUjs5eISAWA9HS9qtj8v2PSZr/yWxONQ==
|
||||
dependencies:
|
||||
"@jest/types" "^27.0.2"
|
||||
chalk "^4.0.0"
|
||||
|
@ -8801,10 +8794,10 @@ jest-resolve@^27.0.2:
|
|||
resolve "^1.20.0"
|
||||
slash "^3.0.0"
|
||||
|
||||
jest-runner@^27.0.3:
|
||||
version "27.0.3"
|
||||
resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.0.3.tgz#d9747af3bee5a6ffaeb9e10b653263b780258b54"
|
||||
integrity sha512-zH23uIIh1ro1JCD7XX1bQ0bQwXEsBzLX2UJVE/AVLsk4YJRmTfyXIzzRzBWRdnMHHg1NWkJ4fGs7eFP15IqZpQ==
|
||||
jest-runner@^27.0.4:
|
||||
version "27.0.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.0.4.tgz#2787170a9509b792ae129794f6944d27d5d12a4f"
|
||||
integrity sha512-NfmvSYLCsCJk2AG8Ar2NAh4PhsJJpO+/r+g4bKR5L/5jFzx/indUpnVBdrfDvuqhGLLAvrKJ9FM/Nt8o1dsqxg==
|
||||
dependencies:
|
||||
"@jest/console" "^27.0.2"
|
||||
"@jest/environment" "^27.0.3"
|
||||
|
@ -8817,20 +8810,22 @@ jest-runner@^27.0.3:
|
|||
exit "^0.1.2"
|
||||
graceful-fs "^4.2.4"
|
||||
jest-docblock "^27.0.1"
|
||||
jest-environment-jsdom "^27.0.3"
|
||||
jest-environment-node "^27.0.3"
|
||||
jest-haste-map "^27.0.2"
|
||||
jest-leak-detector "^27.0.2"
|
||||
jest-message-util "^27.0.2"
|
||||
jest-resolve "^27.0.2"
|
||||
jest-runtime "^27.0.3"
|
||||
jest-resolve "^27.0.4"
|
||||
jest-runtime "^27.0.4"
|
||||
jest-util "^27.0.2"
|
||||
jest-worker "^27.0.2"
|
||||
source-map-support "^0.5.6"
|
||||
throat "^6.0.1"
|
||||
|
||||
jest-runtime@^27.0.3:
|
||||
version "27.0.3"
|
||||
resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.0.3.tgz#32499c1047e5d953cfbb67fe790ab0167a614d28"
|
||||
integrity sha512-k1Hl2pWWHBkSXdCggX2lyLRuDnnnmMlnJd+DPLb8LmmAeHW87WgGC6TplD377VxY3KQu73sklkhGUIdwFgsRVQ==
|
||||
jest-runtime@^27.0.4:
|
||||
version "27.0.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.0.4.tgz#2e4a6aa77cac32ac612dfe12768387a8aa15c2f0"
|
||||
integrity sha512-voJB4xbAjS/qYPboV+e+gmg3jfvHJJY4CagFWBOM9dQKtlaiTjcpD2tWwla84Z7PtXSQPeIpXY0qksA9Dum29A==
|
||||
dependencies:
|
||||
"@jest/console" "^27.0.2"
|
||||
"@jest/environment" "^27.0.3"
|
||||
|
@ -8851,8 +8846,8 @@ jest-runtime@^27.0.3:
|
|||
jest-message-util "^27.0.2"
|
||||
jest-mock "^27.0.3"
|
||||
jest-regex-util "^27.0.1"
|
||||
jest-resolve "^27.0.2"
|
||||
jest-snapshot "^27.0.2"
|
||||
jest-resolve "^27.0.4"
|
||||
jest-snapshot "^27.0.4"
|
||||
jest-util "^27.0.2"
|
||||
jest-validate "^27.0.2"
|
||||
slash "^3.0.0"
|
||||
|
@ -8867,10 +8862,10 @@ jest-serializer@^27.0.1:
|
|||
"@types/node" "*"
|
||||
graceful-fs "^4.2.4"
|
||||
|
||||
jest-snapshot@^27.0.2:
|
||||
version "27.0.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.0.2.tgz#40c48dc6afd3cbc5d3d07c061f20fc10d94ca0cd"
|
||||
integrity sha512-4RcgvZbPrrbEE/hT6XQ4hr+NVVLNrmsgUnYSnZRT6UAvW9Q2yzGMS+tfJh+xlQJAapnnkNJzsMn6vUa+yfiVHA==
|
||||
jest-snapshot@^27.0.4:
|
||||
version "27.0.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.0.4.tgz#2b96e22ca90382b3e93bd0aae2ce4c78bf51fb5b"
|
||||
integrity sha512-hnjrvpKGdSMvKfbHyaG5Kul7pDJGZvjVy0CKpzhu28MmAssDXS6GpynhXzgst1wBQoKD8c9b2VS2a5yhDLQRCA==
|
||||
dependencies:
|
||||
"@babel/core" "^7.7.2"
|
||||
"@babel/generator" "^7.7.2"
|
||||
|
@ -8891,7 +8886,7 @@ jest-snapshot@^27.0.2:
|
|||
jest-haste-map "^27.0.2"
|
||||
jest-matcher-utils "^27.0.2"
|
||||
jest-message-util "^27.0.2"
|
||||
jest-resolve "^27.0.2"
|
||||
jest-resolve "^27.0.4"
|
||||
jest-util "^27.0.2"
|
||||
natural-compare "^1.4.0"
|
||||
pretty-format "^27.0.2"
|
||||
|
@ -8943,14 +8938,14 @@ jest-worker@^27.0.2:
|
|||
merge-stream "^2.0.0"
|
||||
supports-color "^8.0.0"
|
||||
|
||||
jest@27.0.3:
|
||||
version "27.0.3"
|
||||
resolved "https://registry.yarnpkg.com/jest/-/jest-27.0.3.tgz#0b4ac738c93612f778d58250aee026220487e5a4"
|
||||
integrity sha512-0G9+QqXFIZWgf5rs3yllpaA+13ZawVHfyuhuCV1EnoFbX++rVMRrYWCAnk+dfhwyv9/VTQvn+XG969u8aPRsBg==
|
||||
jest@27.0.4:
|
||||
version "27.0.4"
|
||||
resolved "https://registry.yarnpkg.com/jest/-/jest-27.0.4.tgz#91d4d564b36bcf93b98dac1ab19f07089e670f53"
|
||||
integrity sha512-Px1iKFooXgGSkk1H8dJxxBIrM3tsc5SIuI4kfKYK2J+4rvCvPGr/cXktxh0e9zIPQ5g09kOMNfHQEmusBUf/ZA==
|
||||
dependencies:
|
||||
"@jest/core" "^27.0.3"
|
||||
"@jest/core" "^27.0.4"
|
||||
import-local "^3.0.2"
|
||||
jest-cli "^27.0.3"
|
||||
jest-cli "^27.0.4"
|
||||
|
||||
joi@^17.3.0:
|
||||
version "17.3.0"
|
||||
|
@ -11976,10 +11971,10 @@ sass-loader@10.2.0:
|
|||
schema-utils "^3.0.0"
|
||||
semver "^7.3.2"
|
||||
|
||||
sass@1.34.0:
|
||||
version "1.34.0"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.34.0.tgz#e46d5932d8b0ecc4feb846d861f26a578f7f7172"
|
||||
integrity sha512-rHEN0BscqjUYuomUEaqq3BMgsXqQfkcMVR7UhscsAVub0/spUrZGBMxQXFS2kfiDsPLZw5yuU9iJEFNC2x38Qw==
|
||||
sass@1.34.1:
|
||||
version "1.34.1"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.34.1.tgz#30f45c606c483d47b634f1e7371e13ff773c96ef"
|
||||
integrity sha512-scLA7EIZM+MmYlej6sdVr0HRbZX5caX5ofDT9asWnUJj21oqgsC+1LuNfm0eg+vM0fCTZHhwImTiCU0sx9h9CQ==
|
||||
dependencies:
|
||||
chokidar ">=3.0.0 <4.0.0"
|
||||
|
||||
|
@ -13690,10 +13685,10 @@ vue-style-loader@^4.1.0, vue-style-loader@^4.1.2:
|
|||
hash-sum "^1.0.2"
|
||||
loader-utils "^1.0.2"
|
||||
|
||||
vue-template-compiler@2.6.13:
|
||||
version "2.6.13"
|
||||
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.13.tgz#a735b8974e013ce829e7f77e08e4ee5aecbd3005"
|
||||
integrity sha512-latKAqpUjCkovB8XppW5gnZbSdYQzkf8pavsMBZYZrQcG6lAnj0EH4Ty7jMwAwFw5Cf4mybKBHlp1UTjnLPOWw==
|
||||
vue-template-compiler@2.6.14:
|
||||
version "2.6.14"
|
||||
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz#a2f0e7d985670d42c9c9ee0d044fed7690f4f763"
|
||||
integrity sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==
|
||||
dependencies:
|
||||
de-indent "^1.0.2"
|
||||
he "^1.1.0"
|
||||
|
@ -13703,10 +13698,10 @@ vue-template-es2015-compiler@^1.9.0:
|
|||
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
||||
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
|
||||
|
||||
vue@2.6.13:
|
||||
version "2.6.13"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.13.tgz#94b2c1b31fddf1dfcc34f28ec848ba8f01ea4c5b"
|
||||
integrity sha512-O+pAdJkce1ooYS1XyoQtpBQr9An+Oys3w39rkqxukVO3ZD1ilYJkWBGoRuadiQEm2LLJnCL2utV4TMSf52ubjw==
|
||||
vue@2.6.14:
|
||||
version "2.6.14"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"
|
||||
integrity sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==
|
||||
|
||||
vue@^2.6.11:
|
||||
version "2.6.11"
|
||||
|
|
Loading…
Reference in New Issue
Block a user