Merge pull request #13 from paoloar77/Categories

TodoList
This commit is contained in:
Paolo Arena
2019-01-30 02:07:17 +01:00
committed by GitHub
32 changed files with 1560 additions and 222 deletions

View File

@@ -13,9 +13,9 @@ const extendTypescriptToWebpack = (config) => {
config.resolve config.resolve
.alias .alias
.set('@components', path.resolve(__dirname, 'src/components/index.ts')) .set('@components', path.resolve(__dirname, 'src/components/index.ts'))
.set('@components', path.resolve(__dirname, 'src/components')) // .set('@components', path.resolve(__dirname, 'src/components'))
.set('@views', path.resolve(__dirname, 'src/components/views/index.ts')) .set('@views', path.resolve(__dirname, 'src/components/views/index.ts'))
.set('@views', path.resolve(__dirname, 'src/components/views')) // .set('@views', path.resolve(__dirname, 'src/components/views'))
.set('@src', path.resolve(__dirname, 'src')) .set('@src', path.resolve(__dirname, 'src'))
.set('@icons', path.resolve(__dirname, 'src/assets/icons')) .set('@icons', path.resolve(__dirname, 'src/assets/icons'))
.set('@images', path.resolve(__dirname, 'src/assets/images')) .set('@images', path.resolve(__dirname, 'src/assets/images'))
@@ -149,13 +149,17 @@ module.exports = function (ctx) {
'QDatetime', 'QDatetime',
'QSlideTransition', 'QSlideTransition',
'QTable', 'QTable',
'QContextMenu',
'QProgress',
'QSlider',
], ],
directives: [ directives: [
'Ripple', 'Ripple',
'CloseOverlay' 'CloseOverlay',
], ],
// Quasar plugins // Quasar plugins
plugins: [ plugins: [
'Dialog',
'Notify', 'Notify',
'Meta', 'Meta',
'Cookies', 'Cookies',

42
src/classes/debounce.ts Normal file
View File

@@ -0,0 +1,42 @@
/**
* A function that emits a side effect and does not return anything.
*/
export type Procedure = (...args: any[]) => void
export type Options = {
isImmediate: boolean
}
export function debounce<F extends Procedure>(
func: F,
waitMilliseconds: number = 50,
options: Options = {
isImmediate: false
}
): F {
let timeoutId: NodeJS.Timeout | undefined
return function(this: any, ...args: any[]) {
const context = this
const doLater = function() {
timeoutId = undefined
if (!options.isImmediate) {
func.apply(context, args)
}
}
const shouldCallNow = options.isImmediate && timeoutId === undefined
if (timeoutId !== undefined) {
clearTimeout(timeoutId)
}
timeoutId = <any>setTimeout(doLater, waitMilliseconds)
if (shouldCallNow) {
func.apply(context, args)
}
} as any
}

18
src/classes/routinestd.ts Normal file
View File

@@ -0,0 +1,18 @@
export async function askConfirm($q: any, mytitle, mytext, ok, cancel) {
try {
return await $q.dialog({
title: mytitle,
message: mytext,
ok: ok,
cancel: cancel
}).then((ris) => {
return true
// this.$q.notify('Agreed!')
}).catch(() => {
return false
// this.$q.notify('Disagreed...')
})
} catch (e) {
return false
}
}

View File

@@ -1,6 +1,13 @@
<template> <template>
<div> <div>
<!--
<router-link :to="'/'" v-if="$router.currentRoute.meta.backButton">
<button>
<i>arrow_back</i>
</button>
</router-link>
-->
<q-layout-header> <q-layout-header>
<q-toolbar <q-toolbar
color="primary" color="primary"
@@ -130,6 +137,10 @@
} }
} }
.fixed-left:hover{
cursor: ew-resize;
}
/* /*
@-webkit-keyframes moveFromLeftFade { @-webkit-keyframes moveFromLeftFade {
from { from {

View File

@@ -1,7 +1,7 @@
import Vue from 'vue' import Vue from 'vue'
import { Component, Watch } from 'vue-property-decorator' import { Component, Watch } from 'vue-property-decorator'
import { SingleCat } from '@components' import { SingleCat } from '../SingleCat'
import { ICategory } from '@src/model' import { ICategory } from '@src/model'

View File

@@ -1,17 +1,43 @@
$colcompleted: #a2a2a2;
$heightitem: 19px;
.flex-item{ .flex-item{
background-color: #d5e2eb; // background-color: #d5e2eb;
padding: 1px; padding: 0px;
margin: 1px; margin: 0px;
color: #000; color: #000;
font-size: 0.85rem; font-size: 0.85rem;
flex: 1 1 auto; //flex: 0 0 100%;
} }
.flex-container2 { .flex-container2 {
flex-flow: row nowrap; flex-flow: row wrap;
justify-content: space-between; justify-content: space-between;
margin: 2px 5px 2px 5px; // top right bottom left
} }
// Set visibility: visible to the icon menu of pos-item-popover
.flex-container2:hover .pos-item-popover, .flex-container2:hover .priority-item-popover{
//.flex-container2:hover .pos-item-popover {
background-color: rgba(230, 230, 230, 0.8);
display: inline-block;
}
.flex-container2:hover{
background-color: rgba(230, 230, 230, 0.8);
}
.rowselected {
border-width: 1px 0px 1px 0px;
border-style: solid;
border-color: rgba(49, 68, 240, 0.6);
//background-color: rgba(166, 153, 240, 0.7) !important;
}
.btn-item { .btn-item {
max-width: 24px; max-width: 24px;
} }
@@ -20,16 +46,77 @@
.pos-item { .pos-item {
max-width: 24px; max-width: 24px;
min-width: 24px; min-width: 24px;
margin: 0 auto; margin-left: 1px;
height: 36px; margin-right: 1px;
line-height: 36px; padding-left: 1px;
padding-right: 1px;
height: $heightitem;
line-height: $heightitem;
text-align: center; text-align: center;
// background-color: #ff4081;
font-size: 0.75rem;
}
.todo-menu {
min-width: 202px;
}
.item-menu{
font-size: 1rem;
}
.titleLista-item {
max-width: 92px;
min-width: 92px;
margin: 0 auto;
height: $heightitem;
line-height: $heightitem;
text-align: center;
// background-color: #ff4081;
font-size: 1rem;
font-weight: bold;
}
.pos-item-popover{
max-width: 24px;
min-width: 24px;
padding: 0px;
text-align: center;
vertical-align: middle;
display: none;
color: #777;
height: 100%;
//visibility: hidden;
}
.pos-item:hover, .pos-item-popover:hover {
cursor: grab;
} }
.priority-item-popover { .priority-item-popover {
max-width: 24px; max-width: 24px;
min-width: 24px; min-width: 24px;
padding: 0px; padding: 0px;
text-align: center;
vertical-align: middle;
display: none;
height: 100%;
color: #777;
}
.completed-item-popover {
max-width: 24px;
min-width: 24px;
padding: 0px;
text-align: center;
vertical-align: middle;
display: inline-block;
height: 100%;
color: #777;
} }
.priority-item { .priority-item {
@@ -42,6 +129,42 @@
min-width: 24px; min-width: 24px;
} }
.progress-item {
max-width: 32px;
min-width: 24px;
flex: 1;
order: 2;
text-align: right;
}
.percProgress {
height: $heightitem;
line-height: $heightitem;
color: #888;
}
.menuProgress {
}
.colProgress {
}
.lowperc {
color: red;
}
.medperc {
color: blue;
}
.highperc {
color: green;
}
.percompleted {
color: $colcompleted
}
.myexpired { .myexpired {
padding-top: 0px; padding-top: 0px;
padding-bottom: 0px; padding-bottom: 0px;
@@ -50,11 +173,22 @@
.data-item { .data-item {
max-width: 100px; max-width: 100px;
min-width: 100px; //min-width: 100px;
display: block; //display: flex;
visibility: initial; //visibility: initial;
margin-right: 3px;
color: #ccc;
order: 1;
flex: 1;
} }
.data-item .q-input-target{
color:red !important;
}
/*
@media screen and (max-width: 600px) { @media screen and (max-width: 600px) {
.data-item { .data-item {
display: none; display: none;
@@ -62,11 +196,35 @@
content: ""; content: "";
} }
} }
*/
.div_descr, .div_descr_edit {
margin: 2px;
padding: 2px;
min-width: 200px;
vertical-align: middle;
text-align: left;
flex: 1;
//order: 2;
//height: 24px;
//line-height: 24px; /* same as height! */
&.hide {
display: none !important;
visibility: hidden;
}
&.show {
visibility: visible;
}
}
.div_descr {
// background-color: green; .div_descr:hover {
flex: 2 1 auto; border-width: 1px 0px 1px 0px;
border-color: rgba(125, 255, 125, 0.5);
padding: 1px;
} }
@@ -75,8 +233,8 @@
font-size: 1.0rem; font-size: 1.0rem;
// display: flex; // display: flex;
margin: 1px; margin: 1px;
padding: 2px; padding: 1px;
border: 1px; border: 0px;
background-color: #e1ebed; background-color: #e1ebed;
} }
@@ -110,6 +268,61 @@
} }
.icon_completed:hover { .icon_completed {
color: darkgreen; color: rgb(178, 235, 225);
} }
.status_completed {
color: $colcompleted
}
.menuTitlePriority {
background-color: blue;
color:white;
padding: 2px;
margin: 2px;
font-weight: bold;
}
.test{
color: fuchsia;
&.mio1{
background-color: red;
}
}
.after_textarea {
display: none;
visibility: hidden;
}
.after_textarea:hover {
visibility: visible;
}
/*
.container {
background-color: #ccc;
padding: 10px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.item {
background-color: red;
padding: 10px;
color: white;
}
.i3{
order: 1;
flex: 1;
}
*/

View File

@@ -6,29 +6,270 @@ import { UserStore } from '@modules'
import { ITodo } from '../../../model/index' import { ITodo } from '../../../model/index'
import { SubMenus } from '../SubMenus'
import $ from 'jquery'
// import { debounce } from '../../../classes/debounce'
import { askConfirm } from '../../../classes/routinestd'
@Component({ @Component({
name: 'SingleTodo' name: 'SingleTodo',
components: { SubMenus }
}) })
export default class SingleTodo extends Vue { export default class SingleTodo extends Vue {
public selectPriority: [] = [] public selectPriority: [] = []
public menuPopupTodo: any[] = []
public iconCompleted: string = '' public iconCompleted: string = ''
public classCompleted: string = ''
public classDescr: string = ''
public classDescrEdit: string = ''
public classExpiring: string = ''
public classExpiringEx: string = ''
public iconPriority: string = '' public iconPriority: string = ''
public popover: boolean = false public popover: boolean = false
public popover_menu: boolean = false // Serve
public classRow: string = ''
public sel: boolean = false
public inEdit: boolean = false
public precDescr: string = ''
public menuProgress: string = 'menuprogress'
public percProgress: string = 'percProgress'
public colProgress: string = 'blue'
public togglemenu: boolean = false
public percentageProgress: number = 0
$q: any $q: any
@Prop({required: true}) itemtodo: ITodo @Prop({ required: true }) itemtodo: ITodo
@Watch('itemtodo.completed') valueChanged() { @Watch('itemtodo.completed') valueChanged() {
this.$emit('eventupdate', this.itemtodo) this.watchupdate()
}
@Watch('itemtodo.expiring_at') valueChanged2() {
this.$emit('eventupdate', this.itemtodo)
}
@Watch('itemtodo.priority') valueChanged3() {
this.$emit('eventupdate', this.itemtodo)
} }
setCompleted () { @Watch('itemtodo.enableExpiring') valueChanged4() {
this.watchupdate()
}
@Watch('itemtodo.expiring_at') valueChanged2() {
this.watchupdate()
}
@Watch('itemtodo.priority') valueChanged3() {
this.watchupdate()
}
@Watch('itemtodo.descr') valueChanged5() {
this.precDescr = this.itemtodo.descr
}
@Watch('itemtodo.progress') valueChanged6() {
this.updateClasses()
}
isTodo() {
return this.isTodoByElem(this.itemtodo)
}
isTodoByElem(elem) {
return elem.descr.slice(-1) !== ':'
}
watchupdate() {
this.$emit('eventupdate', this.itemtodo)
this.updateicon()
}
updateClasses() {
// this.classCompleted = 'completed-item'
this.classCompleted = 'completed-item-popover'
this.classDescr = 'flex-item div_descr show'
this.classDescrEdit = 'flex-item div_descr_edit'
if (!this.isTodo())
this.classDescr += ' titleLista-item'
this.classExpiring = 'flex-item data-item'
this.classExpiringEx = ''
if (this.itemtodo.completed) {
this.percentageProgress = 100
this.classCompleted += ' icon_completed'
this.classDescr += ' status_completed'
this.classExpiring += ' status_completed'
this.classExpiringEx += ' status_completed'
} else {
this.percentageProgress = this.itemtodo.progress
}
this.menuProgress = 'menuProgress'
this.percProgress = 'percProgress'
let mycolcl = ''
if (this.itemtodo.progress < 33) {
mycolcl = ' lowperc'
} else if (this.itemtodo.progress < 66) {
mycolcl = ' medperc'
} else {
mycolcl = ' highperc'
}
if (this.itemtodo.completed)
mycolcl = ' percompleted'
this.colProgress = mycolcl
this.menuProgress += mycolcl
this.percProgress += mycolcl
// if (this.inEdit) {
// this.classDescr += ' hide'
// this.classDescrEdit += ' show'
// } else {
// this.classDescrEdit += ' hide'
// this.classDescr += ' show'
// }
// this.getinputdescr = 'inputdescr' + this.itemtodo.id
// console.log('classDescrEdit = ', this.classDescrEdit)
// console.log('classDescr', this.classDescr)
if (this.isTodo())
this.menuPopupTodo = rescodes.menuPopupTodo[UserStore.state.lang]
else {
this.menuPopupTodo = []
this.menuPopupTodo.push(rescodes.menuPopupTodo[UserStore.state.lang][rescodes.INDEX_MENU_DELETE])
}
}
created() {
this.precDescr = this.itemtodo.descr
this.updateicon()
this.updateClasses()
this.selectPriority = rescodes.selectPriority[UserStore.state.lang]
}
getClassRow() {
return 'row flex-container2 ' + this.classRow
}
clickRiga() {
// console.log('CLICK RIGA ************')
if (!this.inEdit) {
this.$emit('deselectAllRows', this.itemtodo, true)
if (!this.sel) {
this.selectRiga()
} else {
this.deselectRiga()
}
}
}
selectRiga() {
// console.log('selectRiga', this.itemtodo.descr)
this.sel = true
this.classRow = 'rowselected'
this.updateClasses()
// console.log('FINE selectRiga', this.itemtodo.descr)
}
deselectRiga() {
// console.log('DeselectRiga', this.itemtodo.descr)
this.sel = false
this.classRow = ''
this.inEdit = false
this.updateClasses()
}
deselectAndExitEdit() {
this.deselectRiga()
this.exitEdit()
}
mouseUp() {
if (!this.inEdit) {
if (this.sel) {
this.selectRiga()
} else {
this.deselectRiga()
}
}
}
editTodo() {
// console.log('INIZIO - editTodo')
this.$emit('click')
this.precDescr = this.itemtodo.descr
this.inEdit = true
if (!this.sel)
this.selectRiga()
else
this.updateClasses()
this.faiFocus('inputdescr')
// console.log('FINE - editTodo')
}
faiFocus(elem, isparent: boolean = false) {
let mythis = this
setTimeout(() => {
let theField = null
if (isparent)
theField = <HTMLInputElement>mythis.$parent.$parent.$parent.$parent.$refs[elem]
else
theField = <HTMLInputElement>mythis.$refs[elem]
theField.focus()
// console.log('focus()')
}, 100)
}
exitEdit(singola: boolean = false) {
if (this.inEdit) {
// console.log('exitEdit')
this.inEdit = false
this.updateClasses
this.$emit('deselectAllRows', this.itemtodo, false, singola)
}
}
keyDownArea(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
this.updateTodo()
this.deselectRiga()
this.faiFocus('insertTask', true)
}
// console.log('keyDownArea', e)
if (e.key === 'Escape') {
this.deselectRiga()
this.faiFocus('insertTask', true)
console.log('LOAD this.precDescr', this.precDescr)
this.precDescr = this.itemtodo.descr
}
}
updateTodo() {
this.itemtodo.descr = this.precDescr
console.log('updateTodo', this.precDescr, this.itemtodo.descr)
this.watchupdate()
this.inEdit = false
// this.precDescr = this.itemtodo.descr
this.updateClasses()
}
setCompleted() {
// console.log('setCompleted') // console.log('setCompleted')
this.itemtodo.completed = !this.itemtodo.completed this.itemtodo.completed = !this.itemtodo.completed
@@ -38,10 +279,12 @@ export default class SingleTodo extends Vue {
} }
updatedata() { updatedata() {
console.log('calling this.$emit(eventupdate)')
this.$emit('eventupdate', this.itemtodo) this.$emit('eventupdate', this.itemtodo)
} }
updateicon () { updateicon() {
console.log('updateicon')
if (this.itemtodo.completed) if (this.itemtodo.completed)
this.iconCompleted = 'check_circle' this.iconCompleted = 'check_circle'
else else
@@ -55,25 +298,56 @@ export default class SingleTodo extends Vue {
else if (this.itemtodo.priority === rescodes.Todos.PRIORITY_LOW) else if (this.itemtodo.priority === rescodes.Todos.PRIORITY_LOW)
this.iconPriority = 'expand_more' // expand_more this.iconPriority = 'expand_more' // expand_more
this.updateClasses()
} }
created() {
this.updateicon()
this.selectPriority = rescodes.selectPriority[UserStore.state.lang] removeitem(id) {
this.$emit('deleteitem', id)
}
enableExpiring() {
this.itemtodo.enableExpiring = !this.itemtodo.enableExpiring
} }
remove(id) { clickMenu(action) {
this.$emit('event', id) console.log('click menu: ', action)
if (action === rescodes.MenuAction.DELETE) {
this.askConfirmDelete()
} else if (action === rescodes.MenuAction.TOGGLE_EXPIRING) {
this.enableExpiring()
} else if (action === rescodes.MenuAction.COMPLETED) {
this.setCompleted()
} else if (action === rescodes.MenuAction.PROGRESS_BAR) {
this.updatedata()
}
} }
setPriority (newpriority) { setPriority(newpriority) {
this.itemtodo.priority = newpriority this.itemtodo.priority = newpriority
this.updatedata() this.updatedata()
this.updateicon()
// this.$q.notify('setPriority: ' + elem) // this.$q.notify('setPriority: ' + elem)
} }
askConfirmDelete() {
const deletestr = this.$t('dialog.delete')
const cancelstr = this.$t('dialog.cancel')
askConfirm(this.$q, this.$t('dialog.msg.titledeleteTask'), this.$t('dialog.msg.deleteTask').toString(), deletestr, cancelstr)
.then(ris => {
console.log('ris', ris)
if (ris)
this.removeitem(this.itemtodo.id)
}).catch(err => {
})
}
} }

View File

@@ -1,48 +1,69 @@
<template> <template>
<div class="row flex-container2"> <div :class="getClassRow()">
<div class="flex-item pos-item">{{ itemtodo.pos }}ª</div> <q-context-menu ref="contextMenu">
<div class="flex-item priority-item"> <SubMenus :menuPopupTodo="menuPopupTodo" :itemtodo="itemtodo" @clickMenu="clickMenu" @setPriority="setPriority"></SubMenus>
<q-btn push </q-context-menu>
class="priority-item-popover"
:icon="iconPriority">
<q-popover
v-model="popover"
self="top left"
> <div v-if="isTodo()" class="flex-item pos-item" @mouseup.left="mouseUp" @mousedown="clickRiga">
<q-list link> <q-btn flat
<q-item v-for="field in selectPriority" :key="field.value" class="pos-item-popover"
@click.native="setPriority(field.value), popover = false"> icon="menu" >
<q-item-side :icon="field.icon" inverted color="primary"/> <q-popover self="top right">
<q-item-main> <SubMenus :menuPopupTodo="menuPopupTodo" :itemtodo="itemtodo" @clickMenu="clickMenu" @setPriority="setPriority"></SubMenus>
<q-item-tile label>{{field.label}}</q-item-tile>
</q-item-main>
</q-item>
</q-list>
</q-popover> </q-popover>
</q-btn> </q-btn>
</div> </div>
<div class="flex-item completed-item">
<q-btn push <div v-if="isTodo()" class="flex-item completed-item">
class="priority-item-popover" <q-btn push flat
:class="classCompleted"
:icon="iconCompleted" :icon="iconCompleted"
@click.native="setCompleted"> @click.native="setCompleted">
</q-btn> </q-btn>
<!--<q-icon class=" mycols allleft icon_completed ScheduleStatus" :name="iconCompleted"
@click.native="setCompleted"/>-->
</div>
<div class="flex-item div_descr">
{{ itemtodo.descr }}
</div> </div>
<div class="flex-item data-item"> <q-input type="textarea" ref="inputdescr" v-model="precDescr"
<q-datetime :class="classDescr" :max-height="50"
v-model="itemtodo.expiring_at" @keydown="keyDownArea" v-on:keydown.esc="exitEdit" @blur="exitEdit(true)" @click="editTodo()"/>
class="myexpired"/>
<!--:after="[{icon: 'arrow_forward', content: true, handler () {}}]"-->
<!--<div :class="classDescr" @mousedown.left="editTodo()">-->
<!--<q-field>{{ itemtodo.descr }}</q-field>-->
<!--</div>-->
<div v-if="isTodo()" class="flex-item progress-item">
<q-progress
:percentage="percentageProgress"
class="progress-item"
:color="colProgress"
>
</q-progress>
<div :class="percProgress">
{{percentageProgress}}%
</div>
</div> </div>
<div class="flex-item btn-item">
<q-btn class="mybtn" round color="" icon="delete" @click="remove(itemtodo.id)"></q-btn>
<div v-if="itemtodo.enableExpiring">
<div :class="classExpiring">
<q-datetime
:class="classExpiringEx"
v-model="itemtodo.expiring_at"
class="myexpired">
</q-datetime>
</div>
</div> </div>
<!--<div class="flex-item btn-item">-->
<!--{{classPosItemPopup}}-->
<!--</div>-->
<!--<div class="flex-item btn-item">-->
<!--<q-btn class="mybtn" round color="" icon="delete" @click.native="removeitem(itemtodo.id)"></q-btn>-->
<!--</div>-->
<!--<div class="flex-item">-->
<!--[{{ itemtodo.id_prev}} - {{ itemtodo.id_next}}]-->
<!--</div>-->
</div> </div>
</template> </template>

View File

@@ -0,0 +1,33 @@
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
import { ITodo } from '../../../model/index'
import { rescodes } from "@src/store/Modules/rescodes"
import { UserStore } from "@store"
@Component({
name: 'SubMenus'
})
export default class SubMenus extends Vue {
public selectPriority: [] = rescodes.selectPriority[UserStore.state.lang]
@Prop({ required: false }) menuPopupTodo: any[]
@Prop({ required: false }) itemtodo: ITodo[]
$q: any
clickMenu (field) {
this.$emit('clickMenu', field)
}
setPriority (field) {
this.$emit('setPriority', field)
}
create () {
this.selectPriority = rescodes.selectPriority[UserStore.state.lang]
console.log('CREAZIONE')
}
}

View File

@@ -0,0 +1,62 @@
<template>
<div>
<q-list link separator no-border class="todo-menu">
<div v-for="field in menuPopupTodo" :key="field.value">
<q-item v-if="(field.value !== 130) && (field.value !== 100)" :icon="field.icon"
@click.native="clickMenu(field.value)">
<q-item-side :icon="field.icon"/>
<q-item-main v-if="field.value !== 120">
<q-item-tile label class="item-menu">{{field.label}}</q-item-tile>
</q-item-main>
<q-item-side v-if="field.value === 101">
<q-checkbox v-model="itemtodo.enableExpiring"/>
</q-item-side>
<q-item-side v-if="field.value === 110">
<q-checkbox v-model="itemtodo.completed"/>
</q-item-side>
<q-item-main v-if="field.value === 120">
<q-slider :class="$parent.menuProgress" v-model="itemtodo.progress" :min="0" :max="100"/>
</q-item-main>
<q-item-side v-if="field.value === 120">
<div :class="$parent.percProgress">
{{$parent.percentageProgress}}%
</div>
</q-item-side>
</q-item>
<q-item v-if="(field.value === 100)" :icon="field.icon" v-close-overlay
@click.native="clickMenu(field.value)">
<q-item-side :icon="field.icon"/>
<q-item-tile label class="item-menu">{{field.label}}</q-item-tile>
</q-item>
<q-item v-if="(field.value === 130)" :icon="field.icon"
@click.native="clickMenu(field.value)">
<q-item-side :icon="$parent.iconPriority"/>
<q-item-main>
<q-btn-dropdown ref="dropdown_priority" flat :label="field.label"
>
<q-list link>
<q-item v-close-overlay v-for="field in selectPriority" :key="field.value"
@click.native="setPriority(field.value)">
<q-item-side :icon="field.icon" inverted color="primary"/>
<q-item-main>
<q-item-tile label>{{field.label}}</q-item-tile>
</q-item-main>
</q-item>
</q-list>
</q-btn-dropdown>
</q-item-main>
</q-item>
</div>
</q-list>
</div>
</template>
<script lang="ts" src="./SubMenus.ts">
</script>

View File

@@ -0,0 +1 @@
export {default as SubMenus} from './SubMenus.vue'

View File

@@ -1,2 +1,3 @@
export * from './SingleTodo' export * from './SingleTodo'
export * from './SubMenus'
export * from './todo' export * from './todo'

View File

@@ -1,8 +1,8 @@
.flex-container{ .flex-container{
background-color: #ccc; background-color: rgb(250, 250, 250);
padding: 5px; padding: 5px;
display: flex; display: flex;
flex-flow: row nowrap; flex-direction: row;
justify-content: space-between; justify-content: space-between;
} }
@@ -11,3 +11,68 @@
.mycard { .mycard {
visibility: hidden; visibility: hidden;
} }
.myitemdrag {
padding: 2px;
margin-top: 4px;
border-width: 1px 0px 0px 0px;
//border: solid 1px #ccc;
border-style: solid;
border-color: #ccc;
transition: all .4s;
}
.titlePriority, .titleCompleted{
border-width: 0px 0px 1px 0px;
border-style: solid;
border-color: #ccc;
color:white;
}
.titleCompleted {
background-color: #ccc;
}
.high_priority {
background-color: #4caf50;
}
.medium_priority {
background-color: #3846af;
}
.low_priority {
background-color: #af2218;
}
.myitemdrag-enter, .myitemdrag-leave-active {
opacity: 0;
}
.drag {
//background-color: green;
}
.dragArea {
min-height: 10px;
}
.divtitlecat {
margin: 5px;
padding: 5px;
}
.categorytitle{
color:blue;
background-color: lightblue;
font-size: 1.25rem;
font-weight: bold;
text-align: center;
}
.titleSubMenu {
font-size: 0.7rem;
font-weight: 350;
}

View File

@@ -1,7 +1,7 @@
import Vue from 'vue' import Vue from 'vue'
import { Component, Watch } from 'vue-property-decorator' import { Component, Prop, Watch } from 'vue-property-decorator'
import { SingleTodo } from '@components' import { SingleTodo } from '../SingleTodo'
import { ITodo } from '@src/model' import { ITodo } from '@src/model'
import { rescodes } from '../../../store/Modules/rescodes' import { rescodes } from '../../../store/Modules/rescodes'
@@ -12,7 +12,10 @@ import _ from 'lodash'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import $ from 'jquery'
@Component({ @Component({
components: { SingleTodo, draggable } components: { SingleTodo, draggable }
}) })
export default class Todo extends Vue { export default class Todo extends Vue {
@@ -21,67 +24,243 @@ export default class Todo extends Vue {
filter: boolean = false filter: boolean = false
title: string = '' title: string = ''
todo: string = '' todo: string = ''
todos_arr: any[] = [{}] todos_arr: ITodo[] = []
drag: boolean = true drag: boolean = true
startpos: number = 0 startpos: number = 0
endpos: number = 0 listPriorityLabel: number[] = []
arrPrior: number[] = []
prioritySel: number = 0
itemDragStart: any = null
itemDragEnd: any = null
selrowid: number = 0
// @Prop({ required: false }) category: string
$refs: {
single: SingleTodo[]
}
@Watch('drag') changedrag() { @Watch('drag') changedrag() {
console.log('drag = ' + this.drag) console.log('drag = ' + this.drag)
} }
@Watch('$route.params.category') changecat() {
console.log('changecat')
this.load()
}
getCategory() {
return this.$route.params.category
// return this.category
}
change(param) { change(param) {
console.log('Change... ' + param) console.log('Change... ' + param)
} }
getmyid(id) {
return 'row' + id
}
getrefbyid(id) {
return 'single' + id
}
getelem(indelem, arr: ITodo[] = this.todos_arr) {
return (indelem >= 0) && (indelem < arr.length) ? arr[indelem] : null
}
getLastElem() {
if (this.todos_arr.length > 0)
return this.todos_arr[this.todos_arr.length - 1]
else
return null
}
getFirstelem() {
return this.todos_arr[this.todos_arr.length - 1]
}
onStart() { onStart() {
this.startpos = 0 this.startpos = 0
this.itemDragStart = null
} }
getpos(indelem) { async updateLinkedList(init: boolean, arr: ITodo[] = this.todos_arr) {
return this.todos_arr[indelem].pos
console.log('updateLinkedList', this.todos_arr)
let idprev = -1
let idnext = -1
let pos = 1
if (arr.length > 0) {
idprev = arr[0].id_prev
idnext = arr[0].id_next
}
await arr.forEach((elem, index) => {
if (index === 0) {
idprev = rescodes.LIST_START
} else {
const elemprev = this.getelem(index - 1, arr)
idprev = elemprev.id
}
if (index === arr.length - 1) {
idnext = rescodes.LIST_END
} else {
const elemnext = this.getelem(index + 1, arr)
idnext = elemnext.id
}
elem.modified = ((elem.id_prev !== idprev) || (elem.id_next !== idnext) || (elem.pos !== pos)) ? true : elem.modified
elem.id_prev = idprev
elem.id_next = idnext
elem.pos = pos
if (init) {
elem.modified = false
}
pos++
this.logelem('updateLinked', elem)
})
} }
onEnd(myvar) { logelem(mystr, elem) {
let oldpos = this.getpos(myvar.oldIndex) console.log(mystr, 'elem [', elem.id, '] ', elem.descr, ' Pr(', this.getPriorityByInd(elem.priority), ') [', elem.id_prev, '-', elem.id_next, '] modif=', elem.modified)
let newpos = this.getpos(myvar.newIndex) }
console.log('onEnd old = ' + oldpos + ' new = ' + newpos) getPriorityToSet(ind1, ind2) {
if (myvar.oldIndex < myvar.newIndex) { let elem1 = this.getelem(ind1)
// c'è spazio let elem2 = this.getelem(ind2)
newpos = oldpos - 1
if (newpos <= 0) if ((elem1 !== null) && (elem2 !== null)) {
newpos = 1 if (elem1.priority === elem2.priority) {
return elem1.priority
} else {
// if different priority then take the first
return elem1.priority
}
} else { } else {
newpos = newpos + 1 return (elem1 != null) ? elem1.priority : ((elem2 != null) ? elem2.priority : null)
}
}
getCompleted(ind1, ind2) {
let elem1 = this.getelem(ind1)
let elem2 = this.getelem(ind2)
if ((elem1 !== null) && (elem2 !== null)) {
if (elem1.completed === elem2.completed) {
return elem1.completed
} else {
return elem1.completed
}
} else {
return (elem1 != null) ? elem1.completed : ((elem2 != null) ? elem2.completed : null)
} }
console.log('newpos = ' + newpos) }
if (newpos >= 0) { getTitlePriority (priority) {
let myobj = this.todos_arr[myvar.oldIndex] let cl = ''
console.log('posprec = ' + myobj.pos)
myobj.pos = newpos
this.modify(myobj)
if (priority === rescodes.Todos.PRIORITY_HIGH)
cl = 'high_priority'
else if (priority === rescodes.Todos.PRIORITY_NORMAL)
cl = 'medium_priority'
else if (priority === rescodes.Todos.PRIORITY_LOW)
cl = 'low_priority'
return cl + ' titlePriority'
}
async onEnd(itemdragend) {
console.log('newindex=', itemdragend.newIndex, 'oldindex=', itemdragend.oldIndex)
if (itemdragend.newIndex === itemdragend.oldIndex)
return // If nothing change, exit
let myobj = this.getelem(itemdragend.newIndex)
const indini = itemdragend.newIndex - 1
const indfine = itemdragend.newIndex + 1
console.log('indini', indini, 'indfine', indfine)
// If the newIndex is between another priority, then change priority
let completed = this.getCompleted(indini, indfine)
let changecompleted = false
if (completed != null) {
myobj.modified = (myobj.completed !== completed) ? true : myobj.modified
myobj.completed = completed
changecompleted = true
console.log('Newcompleted: ', completed)
} }
if (!changecompleted) {
// if I changed the completed, I don't have to put in other list priority
let newpriority = this.getPriorityToSet(indini, indfine)
if (newpriority != null && newpriority >= 0) {
myobj.modified = (myobj.priority !== newpriority) ? true : myobj.modified
myobj.priority = newpriority
console.log('NewPriority: ', newpriority)
}
}
await this.updateLinkedList(false)
// Updated only elements modified
await this.updateModifyRecords(true)
}
async updateModifyRecords(refresh: boolean = false) {
let update = false
await this.todos_arr.forEach((elem: ITodo) => {
if (elem.modified) {
this.modify(elem, false)
update = true
}
})
if (update)
await this.updatetable(refresh)
} }
created() { created() {
this.loadCat() this.load()
} }
showlistaTodos(nomelista = '') { setarrPriority() {
this.arrPrior = []
// console.log('todos_arr: ') const arr = rescodes.selectPriority[UserStore.state.lang]
this.todos_arr.forEach((todo, key) => { arr.forEach(rec => {
console.log('Todo n"' + key + ': ' + todo) this.arrPrior.push(rec.value)
}) })
console.log('Array PRIOR:', this.arrPrior)
} }
loadCat() {
this.updatetable() async load() {
// Set last category selected
localStorage.setItem(rescodes.localStorage.categorySel, this.getCategory())
for (let todosKey in rescodes.Todos) {
this.listPriorityLabel.push(rescodes.Todos[todosKey])
}
console.log('Priority:' + this.listPriorityLabel)
this.setarrPriority()
this.clearArr()
await this.updatetable()
this.todos_arr.forEach((elem, index) => {
this.logelem('LOAD ' + index, elem)
})
} }
initcat() { initcat() {
@@ -91,40 +270,56 @@ export default class Todo extends Vue {
const objtodo: ITodo = { const objtodo: ITodo = {
userId: UserStore.state.userId, userId: UserStore.state.userId,
descr: '', descr: '',
pos: -1,
priority: rescodes.Todos.PRIORITY_NORMAL, priority: rescodes.Todos.PRIORITY_NORMAL,
completed: false, completed: false,
created_at: new Date(), created_at: new Date(),
category: '',
modify_at: new Date(), modify_at: new Date(),
expiring_at: mydateexp expiring_at: mydateexp,
enableExpiring: false,
id_prev: 0,
id_next: 0,
pos: 0,
modified: true,
progress: 0
} }
return objtodo return objtodo
} }
getLastPos() { getPriorityByInd(index) {
let max = 0 const arr = rescodes.selectPriority[UserStore.state.lang]
this.todos_arr.forEach(myobj => { for (let rec of arr) {
if (myobj.pos > max) if (rec.value === index)
max = myobj.pos return rec.label
}) }
return ''
return max + 1
} }
async insertTodo() { async insertTodo() {
if (this.todo.trim() === '')
return
const objtodo = this.initcat() const objtodo = this.initcat()
let myid = 0
objtodo.descr = this.todo objtodo.descr = this.todo
objtodo.pos = this.getLastPos() objtodo.category = this.getCategory()
const lastelem = this.getLastList()
objtodo.id_prev = (lastelem !== null) ? lastelem.id : rescodes.LIST_START
objtodo.id_next = rescodes.LIST_END
objtodo.pos = (lastelem !== null) ? lastelem.pos + 1 : 1
objtodo.modified = true
// Add to Indexdb // Add to Indexdb
await this.$db.todos.add(objtodo await this.$db.todos.add(objtodo
).then(() => { ).then((id) => {
this.updatetable() console.log('*** IDNEW = ', id)
if (lastelem !== null) {
lastelem.id_next = id
lastelem.modified = true
this.modify(lastelem, false)
}
this.modify(objtodo, true)
}).catch(err => { }).catch(err => {
console.log('Errore: ' + err.message) console.log('Errore: ' + err.message)
}) })
@@ -133,86 +328,259 @@ export default class Todo extends Vue {
this.todo = '' this.todo = ''
} }
getobjbyid(id) { getElemById(id, lista = this.todos_arr) {
let myobjtrov = null let myobj: ITodo
this.todos_arr.forEach(myobj => { for (myobj of lista) {
if (myobj.id === id) if (myobj.id === id) {
myobjtrov = myobj return myobj
}) }
}
return myobjtrov return null
} }
deleteitem(id) { async deleteitem(id) {
// console.log('deleteitem: KEY = ', id) console.log('deleteitem: KEY = ', id)
let myobjtrov = this.getobjbyid(id) let myobjtrov = this.getElemById(id)
if (myobjtrov !== null) { if (myobjtrov !== null) {
let myobjprev = this.getElemById(myobjtrov.id_prev)
let myobjnext = this.getElemById(myobjtrov.id_next)
if (myobjprev !== null) {
myobjprev.id_next = myobjtrov.id_next
myobjprev.modified = true
this.modify(myobjprev, false)
}
if (myobjnext !== null) {
myobjnext.id_prev = myobjtrov.id_prev
myobjnext.modified = true
this.modify(myobjnext, false)
}
console.log('ENTRATO')
const mythis = this
// Delete item // Delete item
this.$db.todos await this.$db.todos
.where('id').equals(id) .where('id').equals(id)
.delete() .delete()
.then(() => { .then(() => {
this.updatetable() console.log('UpdateTable')
mythis.updatetable()
}).catch((error) => {
console.log('err: ', error)
}) })
} }
console.log('FINE deleteitem')
} }
async updatetable() { async updatetable(refresh: boolean = false) {
await this.filtertodos() await this.filtertodos(refresh)
} }
async filtertodos() { clearArr() {
this.todos_arr = []
}
existArr(x) {
return x = (typeof x !== 'undefined' && x instanceof Array) ? x : []
}
getFirstList(arrlist) {
let elem: ITodo
for (elem of arrlist) {
if (elem.id_prev === rescodes.LIST_START) {
return elem
}
}
return null
}
getLastList(arrlist = this.todos_arr) {
let elem: ITodo
for (elem of arrlist) {
if (elem.id_next === rescodes.LIST_END) {
return elem
}
}
return null
}
setArrayFinale(arrris) {
// Sort List:
let myarr = []
let current = this.getFirstList(arrris)
let currentprec = current
if (current !== null)
myarr.push(current)
let index = -1
while (current !== null && current.id_next !== rescodes.LIST_END && index < arrris.length) {
this.logelem('current : ', current)
console.log('id_next', current.id_next)
// let changed = (prior !== elem.priority) ? true : false
current = this.getElemById(current.id_next, arrris)
if (current === null)
break
if (current.id === currentprec.id)
break
myarr.push(current)
currentprec = current
this.logelem('current AFTER : ', current)
index++
}
return myarr
}
async filtertodos(refresh: boolean = false) {
console.log('filtertodos')
let arrtemp = []
if (this.filter) { if (this.filter) {
// #Todo If need to filter the output database ... // #Todo If need to filter the output database ...
await this.$db.todos await this.$db.todos
.where('userId').equals(UserStore.state.userId) .where('userId').equals(UserStore.state.userId)
.and(todo => todo.category === this.getCategory())
.toArray() .toArray()
.then((response) => { .then((response) => {
Promise.all(response.map(key => key)) Promise.all(response.map(key => key))
.then((ristodos) => { .then((ristodos) => {
this.todos_arr = ristodos arrtemp = ristodos
}) })
}) })
} else { } else {
await this.$db.todos await this.$db.todos
.where('userId').equals(UserStore.state.userId) .where('userId').equals(UserStore.state.userId)
.and(todo => todo.category === this.getCategory())
.toArray().then(ristodos => { .toArray().then(ristodos => {
this.todos_arr = ristodos arrtemp = ristodos
}) })
this.todos_arr = _.orderBy(this.todos_arr, ['completed', 'priority', 'pos'], ['asc', 'desc', 'asc']) arrtemp = _.orderBy(arrtemp, ['completed', 'priority', 'pos'], ['asc', 'desc', 'asc'])
} }
this.todos_arr.map((item, index) => { this.updateLinkedList(true, arrtemp)
item.pos = (index * 2) + 1
}) // set array
// arrtemp = this.setArrayFinale(arrtemp)
this.todos_arr = [...arrtemp] // make copy
return [] return []
} }
sortarr(arr, field) {
return arr.slice().sort(function(a, b) {
return a[field] - b[field]
})
// let ind1 = -1
// let val1 = -1
// for (let x = 0; x <= arr.length; x++) {
// if (x[field] < ind1) {
// val11 = x[field]
// ind1 = x
// }
// for (let y: ITodo = null of arr) {
//
// }
// }
}
updateitem(myobj) { updateitem(myobj) {
console.log('updateitem') console.log('updateitem')
this.modify(myobj) this.modify(myobj, true)
}
// inactiveAllButtons() {
// let divs = this.$children.filter(function (child) {
// return child.$attrs['component-type'] === 'my-custom-button'
// })
// divs.forEach(i => {
// divs[i].isActive = false
// })
// }
//
deselectAllRows(item, check, onlythis: boolean = false) {
console.log('deselectAllRows : ', item)
for (let i = 0; i < this.$refs.single.length; i++) {
let contr = <SingleTodo>this.$refs.single[i]
// @ts-ignore
let id = contr.itemtodo.id
// Don't deselect the actual clicked!
let des = false
if (onlythis) {
des = item.id === id
}else {
des = ((check && (item.id !== id)) || (!check))
}
if (des) {
// @ts-ignore
contr.deselectAndExitEdit()
}
}
}
// updateRow(rec: ITodo) {
// let index = -1
// // get index
// this.$refs.single.forEach( (singletodo: SingleTodo) => {
// if (singletodo.itemtodo.id === rec.id)
// index = -1
// })
//
// }
modifyField(recOut, recIn, field) {
if (recOut[field] !== recIn[field]) {
recOut.modified = true
recOut[field] = recIn[field]
}
} }
async modify(myobj) { async modify(myobj: ITodo, update: boolean) {
await this.$db.transaction('rw', [this.$db.todos], async () => { await this.$db.transaction('rw', [this.$db.todos], async () => {
const miorec = await this.$db.todos.get(myobj.id) const miorec = await this.$db.todos.get(myobj.id)
miorec.modify_at = new Date() this.modifyField(miorec, myobj, 'descr')
miorec.completed = myobj.completed this.modifyField(miorec, myobj, 'completed')
miorec.expiring_at = myobj.expiring_at this.modifyField(miorec, myobj, 'category')
miorec.priority = myobj.priority this.modifyField(miorec, myobj, 'expiring_at')
miorec.pos = myobj.pos this.modifyField(miorec, myobj, 'priority')
this.modifyField(miorec, myobj, 'id_prev')
this.modifyField(miorec, myobj, 'id_next')
this.modifyField(miorec, myobj, 'pos')
this.modifyField(miorec, myobj, 'enableExpiring')
this.modifyField(miorec, myobj, 'progress')
await this.$db.todos.put(miorec)
this.updatetable() if (miorec.modified) {
miorec.modify_at = new Date()
this.logelem('modify', miorec)
await this.$db.todos.put(miorec)
if (update)
await this.updatetable(false)
}
}) })
} }

View File

@@ -3,22 +3,37 @@
<div class="panel"> <div class="panel">
<p class="caption"></p> <p class="caption"></p>
<q-input v-model="todo" inverted float-label="Inserisci il Todo" <div class="divtitlecat">
:after="[{icon: 'arrow_forward', content: true, handler () {}}]" <div class="categorytitle">{{ getCategory() }}</div>
v-on:keyup.enter="insertTodo"/> </div>
<div class="flex-container"> <div style="display: none">{{ prior = 0, priorcomplet = false }}</div>
<draggable v-model="todos_arr" :options="{draggable:'.myitemdrag'}" @start="onStart" @end="onEnd"> <div class="drag">
<draggable v-model="todos_arr" :options="{draggable:'.myitemdrag'}"
@start="onStart" @end="onEnd" class="dragArea">
<transition-group> <transition-group>
<SingleTodo @event="deleteitem" @eventupdate="updateitem" :itemtodo='mytodo' <div :id="getmyid(mytodo.id)" :key="mytodo.id" v-for="mytodo in todos_arr" class="myitemdrag">
v-for="mytodo of todos_arr" :key="mytodo.id" class="myitemdrag">
</SingleTodo> <div v-if="(prior !== mytodo.priority) && !mytodo.completed" :class="getTitlePriority(mytodo.priority)">
<label>{{getPriorityByInd(mytodo.priority)}}</label>
</div>
<div v-if="(!priorcomplet && mytodo.completed)" class="titleCompleted">
<label>{{$t('todo.completed')}}</label>
<div style="display: none">{{ priorcomplet = true }}</div>
</div>
<SingleTodo ref="single" @deleteitem="deleteitem" @eventupdate="updateitem"
@deselectAllRows="deselectAllRows"
:itemtodo='mytodo' />
<div style="display: none">{{ prior = mytodo.priority, priorcomplet = mytodo.completed }}</div>
</div>
</transition-group> </transition-group>
</draggable> </draggable>
</div> </div>
<q-input ref="insertTask" v-model="todo" inverted :float-label="$t('todo.insert')"
<!--<div v-for="element in todos_arr" :key="element.id">{{element.descr}}</div>--> :after="[{icon: 'arrow_forward', content: true, handler () {}}]"
v-on:keyup.enter="insertTodo"/>
</div> </div>
</q-page> </q-page>

View File

@@ -1,5 +1,15 @@
const messages = { const messages = {
it: { it: {
dialog: {
yes: 'Si',
no: 'No',
delete: 'Elimina',
cancel: 'Annulla',
msg: {
titledeleteTask: 'Cancella Task',
deleteTask: 'Vuoi cancellare questo Task?'
}
},
comp:{ comp:{
Conta: "Conta", Conta: "Conta",
}, },
@@ -16,6 +26,9 @@ const messages = {
Test: 'Test', Test: 'Test',
Category: 'Categorie', Category: 'Categorie',
Todo: 'Todo', Todo: 'Todo',
personal: 'Personale',
work: 'Lavoro',
shopping: 'Spesa',
}, },
components: { components: {
authentication:{ authentication:{
@@ -84,8 +97,24 @@ const messages = {
undefined: 'non definito' undefined: 'non definito'
} }
}, },
todo: {
titleprioritymenu: 'Priorità:',
insert: 'Inserisci il Task',
edit: 'Descrizione Task:',
completed: 'Completati'
}
}, },
enUk: { enUk: {
dialog: {
yes: 'Yes',
no: 'No',
delete: 'Delete',
cancel: 'Cancel',
msg: {
titledeleteTask: 'Delete Task',
deleteTask: 'Delete this Task?'
}
},
comp:{ comp:{
Conta: "Count", Conta: "Count",
}, },
@@ -102,6 +131,9 @@ const messages = {
Test: 'Test', Test: 'Test',
Category: 'Category', Category: 'Category',
Todo: 'Todo', Todo: 'Todo',
personal: 'Personal',
work: 'Work',
shopping: 'Shopping',
}, },
components: { components: {
authentication:{ authentication:{
@@ -170,6 +202,12 @@ const messages = {
undefined: 'undefined' undefined: 'undefined'
} }
}, },
todo: {
titleprioritymenu: 'Priority:',
insert: 'Insert Task',
edit: 'Task Description:',
completed: 'Completed'
}
}, },
}; };

View File

@@ -38,6 +38,7 @@
import { Store } from 'vuex' import { Store } from 'vuex'
import { UserStore } from '@modules' import { UserStore } from '@modules'
import { GlobalStore } from '@modules' import { GlobalStore } from '@modules'
import { ITodoList } from "../../model";
@Component({ @Component({
@@ -48,32 +49,48 @@
export default class Drawer extends Vue { export default class Drawer extends Vue {
public $q public $q
$t: any $t: any
public arrlista = GlobalStore.state.listatodo
photo = ''
user = null
links
created() { created() {
console.log('Drawer created...') console.log('Drawer created...')
let listatodo = []
this.arrlista.forEach((elem: ITodoList) => {
let item = { route: '/todo/' + elem.namecat, faIcon: 'fa fa-list-alt', materialIcon: 'todo', name: 'pages.' + elem.description }
listatodo.push(item)
})
this.links = {
Dashboard: {
routes: [
{ route: '/', faIcon: 'fa fa-home', materialIcon: 'home', name: 'pages.home' },
{
route: '/todo', faIcon: 'fa fa-list-alt', materialIcon: 'todo', name: 'pages.Todo',
routes2: listatodo
},
{ route: '/category', faIcon: 'fa fa-list-alt', materialIcon: 'category', name: 'pages.Category' },
{ route: '/signup', faIcon: 'fa fa-registered', materialIcon: 'home', name: 'pages.SignUp' },
{ route: '/signin', faIcon: 'fa fa-anchor', materialIcon: 'home', name: 'pages.SignIn' },
/* {route: '/vreg?idlink=aaa', faIcon: 'fa fa-login', materialIcon: 'login', name: 'pages.vreg'},*/
],
show: true,
},
Forms: {
routes: [
{ route: '/prec', faIcon: 'fa fa-search', materialIcon: 'search', name: 'pages.Test' },
],
show: false
},
}
} }
photo = ''
user = null
links = {
Dashboard: {
routes: [
{ route: '/', faIcon: 'fa fa-home', materialIcon: 'home', name: 'pages.home' },
{ route: '/todo', faIcon: 'fa fa-list-alt', materialIcon: 'todo', name: 'pages.Todo' },
{ route: '/category', faIcon: 'fa fa-list-alt', materialIcon: 'category', name: 'pages.Category' },
{ route: '/signup', faIcon: 'fa fa-registered', materialIcon: 'home', name: 'pages.SignUp' },
{ route: '/signin', faIcon: 'fa fa-anchor', materialIcon: 'home', name: 'pages.SignIn' },
/* {route: '/vreg?idlink=aaa', faIcon: 'fa fa-login', materialIcon: 'login', name: 'pages.vreg'},*/
],
show: true
},
Forms: {
routes: [
{ route: '/prec', faIcon: 'fa fa-search', materialIcon: 'search', name: 'pages.Test' },
],
show: false
},
}
get MenuCollapse() { get MenuCollapse() {
return GlobalStore.state.menuCollapse return GlobalStore.state.menuCollapse
@@ -140,6 +157,10 @@
margin-top: 5%; margin-top: 5%;
} }
.fixed-left:hover {
cursor: ew-resize;
}
footer { footer {
small { small {
color: red; color: red;

View File

@@ -1,15 +1,26 @@
<template> <template>
<div class="list no-border platform-delimiter light-paragraph"> <div class="list no-border platform-delimiter light-paragraph">
<q-icon name="action"/> <q-icon name="action"/>
<template v-for="(parent, index) in links"> <template v-for="(parent, index) in links">
<!-- <!--
<div class="list-label cursor-pointer" @click="parent.show = !parent.show"> <div class="list-label cursor-pointer" @click="parent.show = !parent.show">
{{replaceUnderlineToSpace(index)}} <div class="menu_freccina"><i aria-hidden="true" class="v-icon material-icons theme--light">keyboard_arrow_down</i></div> {{replaceUnderlineToSpace(index)}} <div class="menu_freccina"><i aria-hidden="true" class="v-icon material-icons theme--light">keyboard_arrow_down</i></div>
</div>
-->
<div class="q-list-header">{{replaceUnderlineToSpace(index)}}</div>
<template v-for="child in parent.routes">
<div v-if="child.routes2">
<q-collapsible menu :label="$t(child.name)" icon="format_list_bulleted" class="titleSubMenu">
<div v-for="child2 in child.routes2">
<q-item link :to="child2.route" exact
class="item item-link drawer-closer cursor-pointer">
<i :class="child2.faIcon" class="item-primary"></i>
<div class="item-content">{{$t(child2.name)}}</div>
</q-item>
</div>
</q-collapsible>
</div> </div>
--> <div v-else>
<div class="q-list-header">{{replaceUnderlineToSpace(index)}}</div>
<template v-for="child in parent.routes">
<q-slide-transition :duration=200> <q-slide-transition :duration=200>
<div v-show="true"> <div v-show="true">
<q-item link :to="child.route" exact <q-item link :to="child.route" exact
@@ -19,7 +30,8 @@
</q-item> </q-item>
</div> </div>
</q-slide-transition> </q-slide-transition>
</template> </div>
</template>
</template> </template>
</div> </div>
</template> </template>
@@ -67,7 +79,7 @@
padding: 5px 8px; padding: 5px 8px;
} }
.menu-hr{ .menu-hr {
border-color: #dedede; border-color: #dedede;
height: 0.5px; height: 0.5px;
} }

View File

@@ -9,6 +9,14 @@ export interface IGlobalState {
mobileMode: boolean mobileMode: boolean
menuCollapse: boolean menuCollapse: boolean
leftDrawerOpen: boolean leftDrawerOpen: boolean
category: string
posts: IPost[] posts: IPost[]
listatodo: ITodoList[]
}
export interface ITodoList {
namecat: string
description: string
} }

View File

@@ -1,13 +1,19 @@
export interface ITodo { export interface ITodo {
id?: number, id?: number,
userId: string userId: string
pos: number, category?: string
descr?: string, descr?: string,
priority: number, priority: number,
completed: boolean, completed: boolean,
created_at: any, created_at: any,
modify_at: any, modify_at: any,
expiring_at: any expiring_at: any,
enableExpiring?: boolean,
id_prev?: number,
id_next?: number,
modified?: boolean,
pos?: number,
progress?: number
} }
export interface ITodosState { export interface ITodosState {

View File

@@ -22,6 +22,7 @@ export interface IUserState {
tokens?: IToken[] tokens?: IToken[]
verifiedEmail?: boolean verifiedEmail?: boolean
categorySel?: string
tokenforgot?: string tokenforgot?: string

5
src/plugins/dialog.js Normal file
View File

@@ -0,0 +1,5 @@
import Dialog from 'quasar'
export default ({ Vue }) => {
Vue.use(Dialog)
}

View File

@@ -9,7 +9,7 @@ export default ({ Vue }) => {
database: 'test', database: 'test',
schemas: [ schemas: [
{ categories: '++id, sub_categ_id, descr_it, campo2bool, campo3bool' }, { categories: '++id, sub_categ_id, descr_it, campo2bool, campo3bool' },
{ todos: '++id, userId, pos, descr, priority, completed, created_at, modify_at, expiring_at' } { todos: '++id, userId, category, pos, descr, priority, completed, created_at, modify_at, expiring_at, progress, enableExpiring' }
], ],
options: { options: {
todos: { type: 'list', primary: 'pos', label: 'label', updated_at: 'updated_at' }, todos: { type: 'list', primary: 'pos', label: 'label', updated_at: 'updated_at' },

View File

@@ -2,7 +2,7 @@ import Vue from 'vue'
import { Component } from 'vue-property-decorator' import { Component } from 'vue-property-decorator'
import { GlobalStore } from '@store' import { GlobalStore } from '@store'
import { Logo } from '@components' import { Logo } from '../../components/logo'
@Component({ @Component({
components: { Logo } components: { Logo }

View File

@@ -25,8 +25,9 @@ export const RouteConfig: VueRouteConfig[] = [
meta: { name: 'Verify Reg' } meta: { name: 'Verify Reg' }
}, },
{ {
path: '/todo', path: '/todo/:category',
component: () => import('@/components/todos/todo/todo.vue'), component: () => import('@/components/todos/todo/todo.vue'),
// props: { category: 'personal' },
meta: { name: 'Todos' } meta: { name: 'Todos' }
}, },
{ {

View File

@@ -452,31 +452,31 @@
style="fill:none;stroke:#ffffff;stroke-miterlimit:1;stroke-opacity:0" /> style="fill:none;stroke:#ffffff;stroke-miterlimit:1;stroke-opacity:0" />
</g> </g>
<!-- SUN --> <!-- SUN -->
<animate xlink:href="#sun" attributeName="r" from="3.6" to="4.1" dur="5s" begin="2s" fill="freeze" /> <animate xlink:href="#sun" attributeName="r" from="3.6" to="4.1" dur="3s" begin="2s" fill="freeze" />
<animate xlink:href="#sun" attributeName="r" from="4.1" to="3.6" dur="5s" begin="7s" fill="freeze" /> <animate xlink:href="#sun" attributeName="r" from="4.1" to="3.6" dur="3s" begin="3s" fill="freeze" />
<animate xlink:href="#sun" attributeName="r" from="3.6" to="3.1" dur="5s" begin="12s" fill="freeze" /> <animate xlink:href="#sun" attributeName="r" from="3.6" to="3.1" dur="3s" begin="10s" fill="freeze" />
<animate xlink:href="#sun" attributeName="r" from="3.1" to="3.6" dur="5s" begin="17s" fill="freeze" /> <animate xlink:href="#sun" attributeName="r" from="3.1" to="3.6" dur="3s" begin="15s" fill="freeze" />
<!-- MOON --> <!-- MOON -->
<animate xlink:href="#moon" attributeName="r" from="3.6" to="3.1" dur="5s" begin="2s" fill="freeze" /> <animate xlink:href="#moon" attributeName="r" from="3.6" to="3.1" dur="3s" begin="2s" fill="freeze" />
<animate xlink:href="#moon" attributeName="r" from="3.1" to="3.6" dur="5s" begin="7s" fill="freeze" /> <animate xlink:href="#moon" attributeName="r" from="3.1" to="3.6" dur="3s" begin="3s" fill="freeze" />
<animate xlink:href="#moon" attributeName="r" from="3.6" to="4.1" dur="5s" begin="12s" fill="freeze" /> <animate xlink:href="#moon" attributeName="r" from="3.6" to="4.1" dur="3s" begin="10s" fill="freeze" />
<animate xlink:href="#moon" attributeName="r" from="4.1" to="3.6" dur="5s" begin="17s" fill="freeze" /> <animate xlink:href="#moon" attributeName="r" from="4.1" to="3.6" dur="3s" begin="15s" fill="freeze" />
<animateMotion id="anim1" xlink:href="#sun" dur="20s" begin="2s" fill="freeze"> <animateMotion id="anim1" xlink:href="#sun" dur="15s" begin="2s" fill="freeze">
<mpath xlink:href="#motionPathSun"/> <mpath xlink:href="#motionPathSun"/>
</animateMotion> </animateMotion>
<animateMotion id="anim2" xlink:href="#moon" dur="20s" begin="2s" fill="freeze"> <animateMotion id="anim2" xlink:href="#moon" dur="15s" begin="2s" fill="freeze">
<mpath xlink:href="#motionPathMoon"/> <mpath xlink:href="#motionPathMoon"/>
</animateMotion> </animateMotion>
<animateMotion id="anim3" xlink:href="#sunrays" dur="20s" begin="2s" fill="freeze"> <animateMotion id="anim3" xlink:href="#sunrays" dur="15s" begin="2s" fill="freeze">
<mpath xlink:href="#motionPathSun"/> <mpath xlink:href="#motionPathSun"/>
</animateMotion> </animateMotion>
<animateMotion id="anim4" xlink:href="#moonlight" dur="20s" begin="2s" fill="freeze"> <animateMotion id="anim4" xlink:href="#moonlight" dur="15s" begin="2s" fill="freeze">
<mpath xlink:href="#motionPathMoon"/> <mpath xlink:href="#motionPathMoon"/>
</animateMotion> </animateMotion>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 190 KiB

View File

@@ -9,7 +9,13 @@ const state: IGlobalState = {
mobileMode: false, mobileMode: false,
menuCollapse: true, menuCollapse: true,
leftDrawerOpen: true, leftDrawerOpen: true,
posts: [] category: 'personal',
posts: [],
listatodo: [
{namecat: 'personal', description: 'personal'},
{namecat: 'work', description: 'work'},
{namecat: 'shopping', description: 'shopping'}
]
} }
const b = storeBuilder.module<IGlobalState>('GlobalModule', state) const b = storeBuilder.module<IGlobalState>('GlobalModule', state)
@@ -18,10 +24,20 @@ const b = storeBuilder.module<IGlobalState>('GlobalModule', state)
namespace Getters { namespace Getters {
const conta = b.read(state => state.conta, 'conta') const conta = b.read(state => state.conta, 'conta')
const listatodo = b.read(state => state.listatodo, 'listatodo')
const category = b.read(state => state.category, 'category')
export const getters = { export const getters = {
get conta() { get conta() {
return conta() return conta()
},
get listaTodo() {
return listatodo()
},
get category() {
return category()
} }
} }
} }
@@ -36,9 +52,15 @@ namespace Mutations {
state.leftDrawerOpen = bool state.leftDrawerOpen = bool
} }
function setCategorySel(state: IGlobalState, cat: string) {
state.category = cat
}
export const mutations = { export const mutations = {
setConta: b.commit(setConta), setConta: b.commit(setConta),
setleftDrawerOpen: b.commit(setleftDrawerOpen) setleftDrawerOpen: b.commit(setleftDrawerOpen),
setCategorySel: b.commit(setCategorySel)
} }
} }

View File

@@ -22,7 +22,8 @@ const state: IUserState = {
repeatPassword: '', repeatPassword: '',
idToken: '', idToken: '',
tokens: [], tokens: [],
verifiedEmail: false verifiedEmail: false,
categorySel: 'personal'
} }
@@ -69,6 +70,7 @@ namespace Mutations {
state.username = data.username state.username = data.username
state.idToken = data.idToken state.idToken = data.idToken
state.verifiedEmail = data.verifiedEmail state.verifiedEmail = data.verifiedEmail
state.category = data.categorySel
// @ts-ignore // @ts-ignore
state.tokens = [ state.tokens = [
{ access: 'auth', token: data.idToken } { access: 'auth', token: data.idToken }
@@ -106,6 +108,7 @@ namespace Mutations {
state.tokens = [] state.tokens = []
state.idToken = '' state.idToken = ''
state.verifiedEmail = false state.verifiedEmail = false
state.categorySel = 'personal'
} }
export const mutations = { export const mutations = {
@@ -484,12 +487,14 @@ namespace Actions {
localStorage.removeItem(rescodes.localStorage.isLogged) localStorage.removeItem(rescodes.localStorage.isLogged)
// localStorage.removeItem(rescodes.localStorage.leftDrawerOpen) // localStorage.removeItem(rescodes.localStorage.leftDrawerOpen)
localStorage.removeItem(rescodes.localStorage.verifiedEmail) localStorage.removeItem(rescodes.localStorage.verifiedEmail)
localStorage.removeItem(rescodes.localStorage.categorySel)
router.push('/signin') router.push('/signin')
} }
function setGlobal() { function setGlobal() {
GlobalStore.mutations.setleftDrawerOpen(localStorage.getItem(rescodes.localStorage.leftDrawerOpen) === 'true') GlobalStore.mutations.setleftDrawerOpen(localStorage.getItem(rescodes.localStorage.leftDrawerOpen) === 'true')
GlobalStore.mutations.setCategorySel(localStorage.getItem(rescodes.localStorage.categorySel))
} }
async function autologin (context) { async function autologin (context) {

View File

@@ -5,8 +5,12 @@ export const rescodes = {
DUPLICATE_EMAIL_ID: 11000, DUPLICATE_EMAIL_ID: 11000,
DUPLICATE_USERNAME_ID: 11100, DUPLICATE_USERNAME_ID: 11100,
LIST_END: 10000000,
LIST_START: 0,
localStorage: { localStorage: {
verifiedEmail: 'vf', verifiedEmail: 'vf',
categorySel: 'cs',
isLogged: 'ilog', isLogged: 'ilog',
expirationDate: 'expdate', expirationDate: 'expdate',
leftDrawerOpen: 'ldo', leftDrawerOpen: 'ldo',
@@ -16,9 +20,17 @@ export const rescodes = {
}, },
Todos: { Todos: {
PRIORITY_NORMAL: 0, PRIORITY_HIGH: 2,
PRIORITY_HIGH: 1, PRIORITY_NORMAL: 1,
PRIORITY_LOW: -1 PRIORITY_LOW: 0
},
MenuAction: {
DELETE: 100,
TOGGLE_EXPIRING: 101,
COMPLETED: 110,
PROGRESS_BAR: 120,
PRIORITY: 130
}, },
@@ -27,41 +39,120 @@ export const rescodes = {
{ {
id: 1, id: 1,
label: 'Alta', label: 'Alta',
value: 1, value: 2,
icon: 'expand_less' icon: 'expand_less'
}, },
{ {
id: 2, id: 2,
label: 'Normale', label: 'Normale',
value: 0, value: 1,
icon: 'remove' icon: 'remove'
}, },
{ {
id: 3, id: 3,
label: 'Bassa', label: 'Bassa',
value: -1, value: 0,
icon: 'expand_more' icon: 'expand_more'
}], }],
'enUk': [ 'enUk': [
{ {
id: 1, id: 1,
label: 'High', label: 'High',
value: 1, value: 2,
icon: 'expand_less' icon: 'expand_less'
}, },
{ {
id: 2, id: 2,
label: 'Normal', label: 'Normal',
value: 0, value: 1,
icon: 'remove' icon: 'remove'
}, },
{ {
id: 3, id: 3,
label: 'Low', label: 'Low',
value: -1, value: 0,
icon: 'expand_more' icon: 'expand_more'
}] }]
},
INDEX_MENU_DELETE: 3,
menuPopupTodo: {
'it': [
{
id: 10,
label: '',
value: 120, // PROGRESS_BAR
icon: 'rowing',
checked: true
},
{
id: 20,
label: 'Imposta Priorità',
value: 130, // PRIORITY
icon: 'rowing',
checked: false
},
{
id: 30,
label: 'Completato',
value: 110, // COMPLETED
icon: 'check_circle',
checked: true
},
{
id: 40,
label: 'Imposta Scadenza',
value: 101, // TOGGLE_EXPIRING
icon: 'date_range',
checked: true
},
{
id: 50,
label: 'Cancella',
value: 100, // DELETE
icon: 'delete',
checked: false
}
],
'enUk': [
{
id: 10,
label: '',
value: 120, // PROGRESS_BAR
icon: 'check_circle',
checked: true
},
{
id: 20,
label: 'Set Priority',
value: 130, // PRIORITY
icon: 'high_priority',
checked: false
},
{
id: 30,
label: 'Completed',
value: 110, // COMPLETED
icon: 'check_circle',
checked: true
},
{
id: 40,
label: 'Set Expiring',
value: 101, // TOGGLE_EXPIRING
icon: 'date_range',
checked: true
},
{
id: 50,
label: 'Delete',
value: 100, // DELETE
icon: 'trash',
checked: false
}
]
} }

View File

@@ -10,7 +10,7 @@ import { validations, TSignin } from './signin-validate'
import { validationMixin } from 'vuelidate' import { validationMixin } from 'vuelidate'
import { Logo } from '@components' import { Logo } from '../../../components/logo'
import router from '@router' import router from '@router'

View File

@@ -8,7 +8,7 @@ import { validations, TSignup } from './signup-validate'
import { validationMixin } from 'vuelidate' import { validationMixin } from 'vuelidate'
import { Logo } from '@components' import { Logo } from '../../../components/logo'
// import {Loading, QSpinnerFacebook, QSpinnerGears} from 'quasar' // import {Loading, QSpinnerFacebook, QSpinnerGears} from 'quasar'