forked from vikunja/frontend
Compare commits
6 Commits
master
...
0a5c0869cf
Author | SHA1 | Date | |
---|---|---|---|
0a5c0869cf | |||
158e697988 | |||
bb2800ec40 | |||
8fe362c267 | |||
624e4e6d27 | |||
60c21cc36a |
@ -165,7 +165,7 @@ describe('Task', () => {
|
||||
cy.get('.task-view .details.content.description .editor .vue-easymde .EasyMDEContainer .CodeMirror-scroll')
|
||||
.type('{selectall}New Description')
|
||||
cy.get('.task-view .details.content.description .editor a')
|
||||
.contains('Preview')
|
||||
.contains('Done')
|
||||
.click()
|
||||
|
||||
cy.get('.task-view .details.content.description h3 span.is-small.has-text-success')
|
||||
|
@ -73,14 +73,14 @@ export default {
|
||||
window.addEventListener('offline', () => this.$store.commit(ONLINE, navigator.onLine))
|
||||
},
|
||||
setupPasswortResetRedirect() {
|
||||
if (this.$route.query.userPasswordReset !== undefined) {
|
||||
if (typeof this.$route.query.userPasswordReset !== 'undefined') {
|
||||
localStorage.removeItem('passwordResetToken') // Delete an eventually preexisting old token
|
||||
localStorage.setItem('passwordResetToken', this.$route.query.userPasswordReset)
|
||||
this.$router.push({name: 'user.password-reset.reset'})
|
||||
}
|
||||
},
|
||||
setupEmailVerificationRedirect() {
|
||||
if (this.$route.query.userEmailConfirm !== undefined) {
|
||||
if (typeof this.$route.query.userEmailConfirm !== 'undefined') {
|
||||
localStorage.removeItem('emailConfirmToken') // Delete an eventually preexisting old token
|
||||
localStorage.setItem('emailConfirmToken', this.$route.query.userEmailConfirm)
|
||||
this.$router.push({name: 'user.login'})
|
||||
|
@ -33,7 +33,9 @@ export default {
|
||||
this.$route.name !== 'user.password-reset.reset' &&
|
||||
this.$route.name !== 'user.register' &&
|
||||
this.$route.name !== 'link-share.auth' &&
|
||||
this.$route.name !== 'openid.auth'
|
||||
this.$route.name !== 'openid.auth' &&
|
||||
localStorage.getItem('passwordResetToken') === null &&
|
||||
localStorage.getItem('emailConfirmToken') === null
|
||||
) {
|
||||
this.$router.push({name: 'user.login'})
|
||||
}
|
||||
|
@ -2,11 +2,9 @@
|
||||
<div :class="{'is-pulled-up': isEditEnabled}" class="editor">
|
||||
<div class="tabs is-right" v-if="hasPreview && isEditEnabled && !hasEditBottom">
|
||||
<ul>
|
||||
<li :class="{'is-active': isPreviewActive}" v-if="isEditActive">
|
||||
<a @click="showPreview">Preview</a>
|
||||
</li>
|
||||
<li :class="{'is-active': isEditActive}">
|
||||
<a @click="() => {isPreviewActive = false; isEditActive = true}">Edit</a>
|
||||
<li>
|
||||
<a v-if="!isEditActive" @click="toggleEdit">Edit</a>
|
||||
<a v-else @click="toggleEdit">Done</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -23,17 +21,15 @@
|
||||
</div>
|
||||
|
||||
<ul class="actions">
|
||||
<template v-if="hasEditBottom">
|
||||
<li>
|
||||
<a v-if="!isEditActive" @click="toggleEdit">Edit</a>
|
||||
<a v-else @click="toggleEdit">Done</a>
|
||||
</li>
|
||||
</template>
|
||||
<li v-for="(action, k) in bottomActions" :key="k">
|
||||
<a @click="action.action">{{ action.title }}</a>
|
||||
</li>
|
||||
<template v-if="hasEditBottom">
|
||||
<li :class="{'is-active': isPreviewActive}" v-if="isEditActive">
|
||||
<a @click="showPreview">Preview</a>
|
||||
</li>
|
||||
<li :class="{'is-active': isEditActive}">
|
||||
<a @click="() => {isPreviewActive = false; isEditActive = true}">Edit</a>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
@ -394,10 +390,16 @@ export default {
|
||||
this.bubble()
|
||||
this.renderPreview()
|
||||
},
|
||||
showPreview() {
|
||||
this.isPreviewActive = true
|
||||
this.isEditActive = false
|
||||
this.renderPreview()
|
||||
toggleEdit() {
|
||||
if (this.isEditActive) {
|
||||
this.isPreviewActive = true;
|
||||
this.isEditActive = false;
|
||||
this.renderPreview();
|
||||
this.bubble(0); // save instantly
|
||||
} else {
|
||||
this.isPreviewActive = false;
|
||||
this.isEditActive = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -47,7 +47,21 @@
|
||||
<defer-task v-if="+new Date(task.dueDate) > 0 && showDefer" v-model="task"/>
|
||||
</transition>
|
||||
<priority-label :priority="task.priority"/>
|
||||
<span>
|
||||
<span class="list-task-icon" v-if="task.attachments.length > 0">
|
||||
<icon icon="paperclip"/>
|
||||
</span>
|
||||
<span class="list-task-icon" v-if="task.description">
|
||||
<icon icon="align-left"/>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<progress
|
||||
class="progress is-small"
|
||||
v-if="task.percentDone > 0"
|
||||
:value="task.percentDone * 100" max="100">
|
||||
{{ task.percentDone * 100 }}%
|
||||
</progress>
|
||||
<router-link
|
||||
:to="{ name: 'list.list', params: { listId: task.listId } }"
|
||||
class="task-list"
|
||||
|
@ -19,6 +19,7 @@ import ListNamespaces from '../views/namespaces/ListNamespaces'
|
||||
import ListTeamsComponent from '../views/teams/ListTeams'
|
||||
// Label Handling
|
||||
import ListLabelsComponent from '../views/labels/ListLabels'
|
||||
import NewLabelComponent from '../views/labels/NewLabel'
|
||||
// Migration
|
||||
import MigrationComponent from '../views/migrator/Migrate'
|
||||
import MigrateServiceComponent from '../views/migrator/MigrateService'
|
||||
@ -253,6 +254,11 @@ export default new Router({
|
||||
name: 'labels.index',
|
||||
component: ListLabelsComponent,
|
||||
},
|
||||
{
|
||||
path: '/labels/new',
|
||||
name: 'labels.create',
|
||||
component: NewLabelComponent,
|
||||
},
|
||||
{
|
||||
path: '/migrate',
|
||||
name: 'migrate.start',
|
||||
|
@ -72,6 +72,12 @@ $crazy-height-calculation: '100vh - 4.5rem - 1.5rem - 1em - 1.5em - 8px';
|
||||
font-size: .85rem;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin: 8px 0 0 0;
|
||||
width: 100%;
|
||||
height: 0.5rem;
|
||||
}
|
||||
|
||||
.due-date {
|
||||
float: right;
|
||||
@ -97,6 +103,7 @@ $crazy-height-calculation: '100vh - 4.5rem - 1.5rem - 1em - 1.5em - 8px';
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 8px;
|
||||
|
||||
.items {
|
||||
display: flex;
|
||||
@ -120,6 +127,20 @@ $crazy-height-calculation: '100vh - 4.5rem - 1.5rem - 1em - 1.5em - 8px';
|
||||
}
|
||||
}
|
||||
|
||||
.icons-container {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
.icon {
|
||||
padding: 0px .4rem;
|
||||
&:not(:first-child) {
|
||||
margin-left: 4px;
|
||||
}
|
||||
svg {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
svg {
|
||||
margin: 4px 0 4px 4px;
|
||||
|
@ -254,3 +254,16 @@
|
||||
.link-share-container .task-view {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.flash-background {
|
||||
animation: flash-background 0.75s ease 1;
|
||||
}
|
||||
|
||||
@keyframes flash-background {
|
||||
0% {
|
||||
background: lighten($primary, 30);
|
||||
}
|
||||
100% {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@
|
||||
|
||||
.task {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 0.5rem 1rem;
|
||||
border-bottom: 1px solid darken(#fff, 10%);
|
||||
transition: background-color $transition;
|
||||
@ -35,7 +36,7 @@
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
flex: 1 0 50%;
|
||||
|
||||
.overdue {
|
||||
color: $red;
|
||||
@ -55,7 +56,7 @@
|
||||
|
||||
.color-bubble {
|
||||
height: 10px;
|
||||
flex: 1 0 10px;
|
||||
flex: 0 0 10px;
|
||||
}
|
||||
|
||||
.tag {
|
||||
@ -70,6 +71,15 @@
|
||||
width: 27px;
|
||||
}
|
||||
|
||||
.list-task-icon {
|
||||
margin-left: 6px;
|
||||
|
||||
&:not(:first-of-type) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
a {
|
||||
color: $text;
|
||||
transition: color ease $transition-duration;
|
||||
@ -142,6 +152,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: 50px;
|
||||
margin: 0 0.5rem 0 0;
|
||||
flex: 3 1 auto;
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
margin: 0.5rem 0 0 0;
|
||||
order: 1;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.task:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
@ -164,7 +164,7 @@
|
||||
|
||||
.color-bubble {
|
||||
height: 12px;
|
||||
flex: 1 0 12px;
|
||||
flex: 0 0 12px;
|
||||
}
|
||||
|
||||
.favorite {
|
||||
|
@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<div :class="{ 'is-loading': labelService.loading}" class="loader-container content">
|
||||
<router-link :to="{name:'labels.create'}" class="button is-success button-right">
|
||||
<span class="icon is-small">
|
||||
<icon icon="plus"/>
|
||||
</span>
|
||||
New label
|
||||
</router-link>
|
||||
<h1>Manage labels</h1>
|
||||
<p>
|
||||
Click on a label to edit it.
|
||||
|
80
src/views/labels/NewLabel.vue
Normal file
80
src/views/labels/NewLabel.vue
Normal file
@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div class="fullpage">
|
||||
<a @click="back()" class="close">
|
||||
<icon :icon="['far', 'times-circle']"/>
|
||||
</a>
|
||||
<h3>Create a new label</h3>
|
||||
<form @keyup.esc="back()" @submit.prevent="newlabel">
|
||||
<div class="field is-grouped">
|
||||
<p class="control is-expanded" v-bind:class="{ 'is-loading': labelService.loading }">
|
||||
<input
|
||||
:class="{ 'disabled': labelService.loading }"
|
||||
class="input"
|
||||
placeholder="The label title goes here..." type="text"
|
||||
v-focus
|
||||
v-model="label.title"/>
|
||||
</p>
|
||||
<p class="control">
|
||||
<button class="button is-success noshadow" type="submit">
|
||||
<span class="icon is-small">
|
||||
<icon icon="plus"/>
|
||||
</span>
|
||||
Add
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="showError && label.title === ''">
|
||||
Please specify a title.
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import labelModel from '../../models/label'
|
||||
import labelService from '../../services/label'
|
||||
import {IS_FULLPAGE} from '@/store/mutation-types'
|
||||
import LabelModel from '../../models/label'
|
||||
import LabelService from '../../services/label'
|
||||
|
||||
export default {
|
||||
name: 'NewLabel',
|
||||
data() {
|
||||
return {
|
||||
labelService: labelService,
|
||||
label: labelModel,
|
||||
showError: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.labelService = new LabelService()
|
||||
this.label = new LabelModel()
|
||||
this.$store.commit(IS_FULLPAGE, true)
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle('Create a new label')
|
||||
},
|
||||
methods: {
|
||||
newlabel() {
|
||||
|
||||
if (this.label.title === '') {
|
||||
this.showError = true
|
||||
return
|
||||
}
|
||||
this.showError = false
|
||||
|
||||
this.labelService.create(this.label)
|
||||
.then(response => {
|
||||
this.$router.push({name: 'labels.index', params: {id: response.id}})
|
||||
this.success({message: 'The label was successfully created.'}, this)
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this)
|
||||
})
|
||||
},
|
||||
back() {
|
||||
this.$router.go(-1)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
@ -139,6 +139,12 @@
|
||||
</span>
|
||||
</span>
|
||||
<h3>{{ task.title }}</h3>
|
||||
<progress
|
||||
class="progress is-small"
|
||||
v-if="task.percentDone > 0"
|
||||
:value="task.percentDone * 100" max="100">
|
||||
{{ task.percentDone * 100 }}%
|
||||
</progress>
|
||||
<labels :labels="task.labels"/>
|
||||
<div class="footer">
|
||||
<div class="items">
|
||||
@ -153,16 +159,13 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="icon" v-if="task.attachments.length > 0">
|
||||
<svg fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="none" rx="0" ry="0"></rect>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M19.86 8.29994C19.8823 8.27664 19.9026 8.25201 19.9207 8.22634C20.5666 7.53541 20.93 6.63567 20.93 5.68001C20.93 4.69001 20.55 3.76001 19.85 3.06001C18.45 1.66001 16.02 1.66001 14.62 3.06001L9.88002 7.80001C9.86705 7.81355 9.85481 7.82753 9.8433 7.8419L4.58 13.1C3.6 14.09 3.06 15.39 3.06 16.78C3.06 18.17 3.6 19.48 4.58 20.46C5.6 21.47 6.93 21.98 8.26 21.98C9.59 21.98 10.92 21.47 11.94 20.46L17.74 14.66C17.97 14.42 17.98 14.04 17.74 13.81C17.5 13.58 17.12 13.58 16.89 13.81L11.09 19.61C10.33 20.36 9.33 20.78 8.26 20.78C7.19 20.78 6.19 20.37 5.43 19.61C4.68 18.85 4.26 17.85 4.26 16.78C4.26 15.72 4.68 14.71 5.43 13.96L15.47 3.91996C15.4962 3.89262 15.5195 3.86346 15.54 3.83292C16.4992 2.95103 18.0927 2.98269 19.01 3.90001C19.48 4.37001 19.74 5.00001 19.74 5.67001C19.74 6.34001 19.48 6.97001 19.01 7.44001L14.27 12.18C14.2571 12.1935 14.2448 12.2075 14.2334 12.2218L8.96 17.4899C8.59 17.8699 7.93 17.8699 7.55 17.4899C7.36 17.2999 7.26 17.0399 7.26 16.7799C7.26 16.5199 7.36 16.2699 7.55 16.0699L15.47 8.14994C15.7 7.90994 15.71 7.52994 15.47 7.29994C15.23 7.06994 14.85 7.06994 14.62 7.29994L6.7 15.2199C6.29 15.6399 6.06 16.1899 6.06 16.7799C6.06 17.3699 6.29 17.9199 6.7 18.3399C7.12 18.7499 7.67 18.9799 8.26 18.9799C8.85 18.9799 9.4 18.7599 9.82 18.3399L19.86 8.29994Z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</span>
|
||||
<div class="icons-container">
|
||||
<span class="icon" v-if="task.attachments.length > 0">
|
||||
<icon icon="paperclip"/>
|
||||
</span>
|
||||
<span v-if="task.description" class="icon">
|
||||
<icon icon="align-left"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -28,7 +28,7 @@
|
||||
v-model="task.assignees"
|
||||
/>
|
||||
</div>
|
||||
<div class="column" v-if="activeFields.priority">
|
||||
<div class="column" ref="priorityParent" v-if="activeFields.priority">
|
||||
<!-- Priority -->
|
||||
<div class="detail-title">
|
||||
<icon :icon="['far', 'star']"/>
|
||||
@ -40,7 +40,7 @@
|
||||
ref="priority"
|
||||
v-model="task.priority"/>
|
||||
</div>
|
||||
<div class="column" v-if="activeFields.dueDate">
|
||||
<div class="column" ref="dueDateParent" v-if="activeFields.dueDate">
|
||||
<!-- Due Date -->
|
||||
<div class="detail-title">
|
||||
<icon icon="calendar"/>
|
||||
@ -61,7 +61,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column" v-if="activeFields.percentDone">
|
||||
<div class="column" ref="percentDoneParent" v-if="activeFields.percentDone">
|
||||
<!-- Percent Done -->
|
||||
<div class="detail-title">
|
||||
<icon icon="percent"/>
|
||||
@ -73,7 +73,7 @@
|
||||
ref="percentDone"
|
||||
v-model="task.percentDone"/>
|
||||
</div>
|
||||
<div class="column" v-if="activeFields.startDate">
|
||||
<div class="column" ref="startDateParent" v-if="activeFields.startDate">
|
||||
<!-- Start Date -->
|
||||
<div class="detail-title">
|
||||
<icon icon="calendar-week"/>
|
||||
@ -94,7 +94,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column" v-if="activeFields.endDate">
|
||||
<div class="column" ref="endDateParent" v-if="activeFields.endDate">
|
||||
<!-- End Date -->
|
||||
<div class="detail-title">
|
||||
<icon icon="calendar-week"/>
|
||||
@ -115,7 +115,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column" v-if="activeFields.reminders">
|
||||
<div class="column" ref="remindersParent" v-if="activeFields.reminders">
|
||||
<!-- Reminders -->
|
||||
<div class="detail-title">
|
||||
<icon icon="history"/>
|
||||
@ -127,7 +127,7 @@
|
||||
ref="reminders"
|
||||
v-model="task.reminderDates"/>
|
||||
</div>
|
||||
<div class="column" v-if="activeFields.repeatAfter">
|
||||
<div class="column" ref="repeatAfterParent" v-if="activeFields.repeatAfter">
|
||||
<!-- Repeat after -->
|
||||
<div class="detail-title">
|
||||
<icon :icon="['far', 'clock']"/>
|
||||
@ -139,7 +139,7 @@
|
||||
ref="repeatAfter"
|
||||
v-model="task"/>
|
||||
</div>
|
||||
<div class="column" v-if="activeFields.color">
|
||||
<div class="column" ref="colorParent" v-if="activeFields.color">
|
||||
<!-- Color -->
|
||||
<div class="detail-title">
|
||||
<icon icon="fill-drip"/>
|
||||
@ -546,7 +546,23 @@ export default {
|
||||
this.activeFields[fieldName] = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs[fieldName]) {
|
||||
this.$refs[fieldName].$el.focus()
|
||||
this.$refs[fieldName].$el.focus();
|
||||
|
||||
// animate background flash with CSS animations (if the field's parent has a ref)
|
||||
if (typeof this.$refs[fieldName + "Parent"] !== 'undefined')
|
||||
this.$refs[fieldName + "Parent"].classList.add('flash-background');
|
||||
|
||||
// remove animation class after animation finishes
|
||||
setTimeout(() => {
|
||||
if (typeof this.$refs[fieldName + "Parent"] !== 'undefined')
|
||||
this.$refs[fieldName + "Parent"].classList.remove('flash-background');
|
||||
}, 750);
|
||||
|
||||
// scroll the field to the center of the screen if not in viewport already
|
||||
const boundingRect = this.$refs[fieldName].$el.getBoundingClientRect();
|
||||
|
||||
if (boundingRect.top > (window.scrollY + window.innerHeight) || boundingRect.top < window.scrollY)
|
||||
this.$refs[fieldName].$el.scrollIntoView({behavior: "smooth", block: "center", inline: "nearest"});
|
||||
}
|
||||
})
|
||||
},
|
||||
|
@ -96,7 +96,7 @@ export default {
|
||||
let passwordReset = new PasswordResetModel({newPassword: this.credentials.password})
|
||||
this.passwordResetService.resetPassword(passwordReset)
|
||||
.then(response => {
|
||||
this.successMessage = response.data.message
|
||||
this.successMessage = response.message
|
||||
localStorage.removeItem('passwordResetToken')
|
||||
})
|
||||
.catch(e => {
|
||||
|
Loading…
Reference in New Issue
Block a user