1
0

Merge branch 'main' into vue3

# Conflicts:
#	src/components/input/editor.vue
#	src/components/list/partials/filters.vue
#	src/components/tasks/partials/editAssignees.vue
#	src/helpers/find.ts
#	src/helpers/time/formatDate.js
#	src/main.ts
#	src/store/modules/attachments.js
#	src/store/modules/kanban.js
#	src/views/list/views/List.vue
#	yarn.lock
This commit is contained in:
Dominik Pschenitschni
2021-10-07 12:20:52 +02:00
120 changed files with 717 additions and 272 deletions

View File

@ -6,7 +6,7 @@
>
<div class="container has-text-centered link-share-view">
<div class="column is-10 is-offset-1">
<img alt="Vikunja" class="logo" src="/images/logo-full.svg"/>
<img alt="Vikunja" class="logo" :src="logoUrl" />
<h1
:style="{ 'opacity': currentList.title === '' ? '0': '1' }"
class="title">
@ -27,8 +27,15 @@
import {mapState} from 'vuex'
import {CURRENT_LIST} from '@/store/mutation-types'
import logoUrl from '@/assets/logo-full.svg'
export default {
name: 'contentLinkShare',
data() {
return {
logoUrl,
}
},
computed: mapState({
currentList: CURRENT_LIST,
background: 'background',

View File

@ -1,7 +1,7 @@
<template>
<div class="no-auth-wrapper">
<div class="noauth-container">
<img alt="Vikunja" src="/images/logo-full.svg" width="400" height="117"/>
<img alt="Vikunja" :src="logoUrl" width="400" height="117" />
<div class="message is-info" v-if="motd !== ''">
<div class="message-header">
<p>{{ $t('misc.info') }}</p>
@ -18,8 +18,15 @@
<script>
import {mapState} from 'vuex'
import logoUrl from '@/assets/logo-full.svg'
export default {
name: 'contentNoAuth',
data() {
return {
logoUrl,
}
},
created() {
this.redirectToHome()
},

View File

@ -2,7 +2,7 @@
<div :class="{'is-active': menuActive}" class="namespace-container">
<div class="menu top-menu">
<router-link :to="{name: 'home'}" class="logo">
<img alt="Vikunja" src="/images/logo-full.svg" width="164" height="48"/>
<img alt="Vikunja" :src="logoUrl" width="164" height="48"/>
</router-link>
<ul class="menu-list">
<li>
@ -162,6 +162,8 @@ import NamespaceSettingsDropdown from '@/components/namespace/namespace-settings
import draggable from 'vuedraggable'
import {calculateItemPosition} from '@/helpers/calculateItemPosition'
import logoUrl from '@/assets/logo-full.svg'
export default {
name: 'navigation',
data() {
@ -173,6 +175,7 @@ export default {
ghostClass: 'ghost',
},
listUpdating: {},
logoUrl,
}
},
components: {

View File

@ -7,8 +7,7 @@
>
<div class="navbar-brand">
<router-link :to="{name: 'home'}" class="navbar-item logo">
<img width="164" height="48" alt="Vikunja" src="/images/logo-full-pride.svg" v-if="(new Date()).getMonth() === 5"/>
<img width="164" height="48" alt="Vikunja" src="/images/logo-full.svg" v-else/>
<img width="164" height="48" alt="Vikunja" :src="logoUrl" />
</router-link>
<a
@click="$store.commit('toggleMenu')"
@ -103,6 +102,9 @@ import ListSettingsDropdown from '@/components/list/list-settings-dropdown.vue'
import Dropdown from '@/components/misc/dropdown.vue'
import Notifications from '@/components/notifications/notifications.vue'
import logoUrl from '@/assets/logo-full.svg'
import logoFullPrideUrl from '@/assets/logo-full-pride.svg'
export default {
name: 'topNavigation',
components: {
@ -111,16 +113,21 @@ export default {
ListSettingsDropdown,
Update,
},
computed: mapState({
userInfo: state => state.auth.info,
userAvatar: state => state.auth.avatarUrl,
userAuthenticated: state => state.auth.authenticated,
currentList: CURRENT_LIST,
background: 'background',
imprintUrl: state => state.config.legal.imprintUrl,
privacyPolicyUrl: state => state.config.legal.privacyPolicyUrl,
canWriteCurrentList: state => state.currentList.maxRight > Rights.READ,
}),
computed: {
logoUrl() {
return (new Date()).getMonth() === 5 ? logoFullPrideUrl : logoUrl
},
...mapState({
userInfo: state => state.auth.info,
userAvatar: state => state.auth.avatarUrl,
userAuthenticated: state => state.auth.authenticated,
currentList: CURRENT_LIST,
background: 'background',
imprintUrl: state => state.config.legal.imprintUrl,
privacyPolicyUrl: state => state.config.legal.privacyPolicyUrl,
canWriteCurrentList: state => state.currentList.maxRight > Rights.READ,
}),
},
mounted() {
this.$nextTick(() => {
if (typeof this.$refs.usernameDropdown === 'undefined' || typeof this.$refs.listTitle === 'undefined') {

View File

@ -13,7 +13,7 @@
<div class="preview content" v-html="preview" v-if="isPreviewActive && text !== ''">
</div>
<p class="has-text-centered has-text-grey is-italic" v-if="showPreviewText">
<p class="has-text-centered has-text-grey is-italic my-5" v-if="showPreviewText">
{{ emptyText }}
<template v-if="isEditEnabled">
<a @click="toggleEdit">{{ $t('input.editor.edit') }}</a>.
@ -22,20 +22,20 @@
<ul class="actions" v-if="bottomActions.length > 0">
<li v-if="isEditEnabled && !showPreviewText && showSave">
<a v-if="!isEditActive" @click="toggleEdit">{{ $t('input.editor.edit') }}</a>
<a v-else @click="toggleEdit" class="done-edit">{{ $t('misc.save') }}</a>
<a v-if="showEditButton" @click="toggleEdit">{{ $t('input.editor.edit') }}</a>
<a v-else-if="isEditActive" @click="toggleEdit" class="done-edit">{{ $t('misc.save') }}</a>
</li>
<li v-for="(action, k) in bottomActions" :key="k">
<a @click="action.action">{{ action.title }}</a>
</li>
</ul>
<template v-else-if="isEditEnabled && showSave">
<ul v-if="!isEditActive" class="actions">
<ul v-if="showEditButton" class="actions">
<li>
<a @click="toggleEdit">{{ $t('input.editor.edit') }}</a>
</li>
</ul>
<x-button v-else @click="toggleEdit" type="secondary" :shadow="false">
<x-button v-else-if="isEditActive" @click="toggleEdit" type="secondary" :shadow="false">
{{ $t('misc.save') }}
</x-button>
</template>
@ -103,6 +103,9 @@ export default {
showPreviewText() {
return this.isPreviewActive && this.text === '' && this.emptyText !== ''
},
showEditButton() {
return !this.isEditActive && this.text !== ''
},
},
data() {
return {

View File

@ -178,9 +178,8 @@ import Fancycheckbox from '../../input/fancycheckbox'
import flatPickr from 'vue-flatpickr-component'
import 'flatpickr/dist/flatpickr.css'
import {includesById} from '@/helpers/utils'
import {formatISO} from 'date-fns'
import differenceWith from 'lodash/differenceWith'
import PrioritySelect from '@/components/tasks/partials/prioritySelect.vue'
import PercentDoneSelect from '@/components/tasks/partials/percentDoneSelect.vue'
import Multiselect from '@/components/input/multiselect.vue'
@ -270,13 +269,7 @@ export default {
},
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
})
return this.$store.getters['labels/filterLabelsByQuery'](this.labels, this.query)
},
flatPickerConfig() {
return {
@ -310,8 +303,13 @@ export default {
this.prepareRelatedObjectFilter('namespace')
this.prepareSingleValue('labels')
const labelIds = (typeof this.filters.labels === 'string' ? this.filters.labels : '').split(',').map(i => parseInt(i))
this.labels = (Object.values(this.$store.state.labels.labels).filter(l => labelIds.includes(l.id)) ?? [])
const labels = typeof this.filters.labels === 'string'
? this.filters.labels
: ''
const labelIds = labels.split(',').map(i => parseInt(i))
this.labels = this.$store.getters['labels/getLabelsByIds'](labelIds)
},
removePropertyFromFilter(propertyName) {
// Because of the way arrays work, we can only ever remove one element at once.
@ -534,10 +532,8 @@ export default {
this[`${kind}Service`].getAll({}, {s: query})
.then(response => {
// Filter the results to not include users who are already assigneid
this[`found${kind}`] = differenceWith(response, this[kind], (first, second) => {
return first.id === second.id
})
// Filter users from the results who are already assigned
this[`found${kind}`] = response.filter(({id}) => !includesById(this[kind], id))
})
.catch(e => {
this.$message.error(e)

View File

@ -34,11 +34,11 @@
class="migration-in-progress-container"
v-else-if="isMigrating === true && message === '' && lastMigrationDate === null">
<div class="migration-in-progress">
<img :alt="name" :src="`/images/migration/${identifier}.png`"/>
<img :alt="name" :src="serviceIconSource"/>
<div class="progress-dots">
<span v-for="i in progressDotsCount" :key="i" />
</div>
<img alt="Vikunja" src="/images/logo.svg">
<img alt="Vikunja" :src="logoUrl">
</div>
<p>{{ $t('migrate.inProgress') }}</p>
</div>
@ -66,6 +66,9 @@
<script>
import AbstractMigrationService from '../../services/migrator/abstractMigration'
import AbstractMigrationFileService from '../../services/migrator/abstractMigrationFile'
import {SERVICE_ICONS} from '../../helpers/migrator'
import logoUrl from '@/assets/logo.svg'
const PROGRESS_DOTS_COUNT = 8
@ -80,6 +83,7 @@ export default {
message: '',
migratorAuthCode: '',
migrationService: null,
logoUrl,
}
},
props: {
@ -96,6 +100,11 @@ export default {
default: false,
},
},
computed: {
serviceIconSource() {
return SERVICE_ICONS[this.identifier]()
},
},
created() {
this.message = ''

View File

@ -102,7 +102,7 @@
>
{{ t.title }}
</span>
<priority-label :priority="t.priority"/>
<priority-label :priority="t.priority" :done="t.done"/>
<!-- using the key here forces vue to use the updated version model and not the response returned by the api -->
<a @click="editTask(theTasks[k])" class="edit-toggle">
<icon icon="pen"/>
@ -192,7 +192,6 @@ import TaskCollectionService from '../../services/taskCollection'
import {mapState} from 'vuex'
import Rights from '../../models/constants/rights.json'
import FilterPopup from '@/components/list/partials/filter-popup.vue'
import {format} from 'date-fns'
export default {
name: 'GanttChart',
@ -466,7 +465,7 @@ export default {
})
},
formatYear(date) {
return format(date, 'MMMM, yyyy')
return this.format(date, 'MMMM, yyyy')
},
},
}

View File

@ -1,5 +1,4 @@
import TaskCollectionService from '@/services/taskCollection'
import cloneDeep from 'lodash/cloneDeep'
// FIXME: merge with DEFAULT_PARAMS in filters.vue
const DEFAULT_PARAMS = {
@ -83,7 +82,7 @@ export default {
this.tasks = r
this.currentPage = page
this.loadedList = cloneDeep(currentList)
this.loadedList = JSON.parse(JSON.stringify(currentList))
})
.catch(e => {
this.$message.error(e)

View File

@ -29,8 +29,7 @@
</template>
<script>
import differenceWith from 'lodash/differenceWith'
import {includesById} from '@/helpers/utils'
import UserModel from '../../../models/user'
import ListUserService from '../../../services/listUsers'
import TaskAssigneeService from '../../../services/taskAssignee'
@ -112,9 +111,7 @@ export default {
this.listUserService.getAll({listId: this.listId}, {s: query})
.then(response => {
// Filter the results to not include users who are already assigned
this.foundUsers = differenceWith(response, this.assignees, (first, second) => {
return first.id === second.id
})
this.foundUsers = response.filter(({id}) => !includesById(this.assignees, id))
})
.catch(e => {
this.$message.error(e)

View File

@ -38,8 +38,6 @@
</template>
<script>
import differenceWith from 'lodash/differenceWith'
import LabelModel from '../../../models/label'
import LabelTaskService from '../../../services/labelTask'
@ -84,13 +82,7 @@ export default {
},
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
})
return this.$store.getters['labels/filterLabelsByQuery'](this.labels, this.query)
},
loading() {
return this.labelTaskService.loading || (this.$store.state[LOADING] && this.$store.state[LOADING_MODULE] === 'labels')
@ -157,7 +149,7 @@ export default {
.then(r => {
this.addLabel(r, false)
this.labels.push(r)
this.$message.success({message: this.$t('task.label.removeSuccess')})
this.$message.success({message: this.$t('task.label.addCreateSuccess')})
})
.catch(e => {
this.$message.error(e)

View File

@ -41,7 +41,7 @@
</progress>
<div class="footer">
<labels :labels="task.labels"/>
<priority-label :priority="task.priority"/>
<priority-label :priority="task.priority" :done="task.done"/>
<div class="assignees" v-if="task.assignees.length > 0">
<user
:avatar-size="24"
@ -51,6 +51,7 @@
v-for="u in task.assignees"
/>
</div>
<checklist-summary :task="task"/>
<span class="icon" v-if="task.attachments.length > 0">
<icon icon="paperclip"/>
</span>
@ -66,10 +67,12 @@ import {playPop} from '../../../helpers/playPop'
import PriorityLabel from '../../../components/tasks/partials/priorityLabel'
import User from '../../../components/misc/user'
import Labels from '../../../components/tasks/partials/labels'
import ChecklistSummary from './checklist-summary'
export default {
name: 'kanban-card',
components: {
ChecklistSummary,
PriorityLabel,
User,
Labels,

View File

@ -2,7 +2,7 @@
<span
:class="{'not-so-high': priority === priorities.HIGH, 'high-priority': priority >= priorities.HIGH}"
class="priority-label"
v-if="showAll || priority >= priorities.HIGH">
v-if="!done && (showAll || priority >= priorities.HIGH)">
<span class="icon" v-if="priority >= priorities.HIGH">
<icon icon="exclamation"/>
</span>
@ -39,6 +39,10 @@ export default {
type: Boolean,
default: false,
},
done: {
type: Boolean,
default: false,
},
},
}
</script>

View File

@ -56,9 +56,9 @@
<div class="control is-expanded">
<div class="select is-fullwidth has-defaults">
<select v-model="newTaskRelationKind">
<option value="unset">Select a relation kind</option>
<option :key="rk" :value="rk" v-for="(label, rk) in relationKinds">
{{ label[0] }}
<option value="unset">{{ $t('task.relation.select') }}</option>
<option :key="rk" :value="rk" v-for="rk in relationKinds">
{{ $tc(`task.relation.kinds.${rk}`, 1) }}
</option>
</select>
</div>
@ -256,10 +256,7 @@ export default {
})
},
relationKindTitle(kind, length) {
if (length > 1) {
return relationKinds[kind][1]
}
return relationKinds[kind][0]
return this.$tc(`task.relation.kinds.${kind}`, length)
},
},
}

View File

@ -50,7 +50,7 @@
<transition name="fade">
<defer-task v-if="+new Date(task.dueDate) > 0 && showDefer" v-model="task" ref="deferDueDate"/>
</transition>
<priority-label :priority="task.priority"/>
<priority-label :priority="task.priority" :done="task.done"/>
<span>
<span class="list-task-icon" v-if="task.attachments.length > 0">
<icon icon="paperclip"/>