Corrected some logic problems...

4 storing:
  - Array in Memory Global state.todos
  - Array temporary filtered arr_todos
  - IndexedDb
  - Database
This commit is contained in:
Paolo Arena
2019-02-15 01:25:44 +01:00
parent 1af2c86067
commit cd0ec40441
11 changed files with 198 additions and 132 deletions

View File

@@ -137,7 +137,7 @@ if (workbox) {
if (data) { if (data) {
if (data.todos) { if (data.todos) {
console.log('***********************+++++++++++++++++++++++++++++++++++++++++++++++++++********** Records TODOS Received from Server [', data.todos.length, 'record]', data.todos) console.log('***********************+++++++++++++++++++++++++++++++++++++++++++++++++++********** Records TODOS Received from Server [', data.todos.length, 'record]', data.todos)
for (const key in data.todos) { for (let key in data.todos) {
await writeData('todos', data.todos[key]) await writeData('todos', data.todos[key])
} }
} }
@@ -506,7 +506,9 @@ self.addEventListener('push', function (event) {
badge: '/statics/icons/android-chrome-192x192.png', badge: '/statics/icons/android-chrome-192x192.png',
data: { data: {
url: data.url url: data.url
} },
tag: 'received',
renitify: true, // vibrate also with others messages.
}; };
event.waitUntil( event.waitUntil(

View File

@@ -26,6 +26,10 @@ $heightitem: 19px;
display: inline-block; display: inline-block;
} }
.comp_selected {
display: inline-block !important;
}
.flex-container2:hover{ .flex-container2:hover{
background-color: rgba(230, 230, 230, 0.8); background-color: rgba(230, 230, 230, 0.8);
} }
@@ -87,6 +91,9 @@ $heightitem: 19px;
//visibility: hidden; //visibility: hidden;
} }
//.q-popover.animate-scale {
// animation: none;
//}
.pos-item:hover, .pos-item-popover:hover { .pos-item:hover, .pos-item-popover:hover {
cursor: grab; cursor: grab;

View File

@@ -40,6 +40,8 @@ export default class SingleTodo extends Vue {
public togglemenu: boolean = false public togglemenu: boolean = false
public percentageProgress: number = 0 public percentageProgress: number = 0
public itemtodoPrec: ITodo public itemtodoPrec: ITodo
public clButtPopover: string = 'pos-item-popover'
$q: any $q: any
@Prop({ required: true }) itemtodo: ITodo @Prop({ required: true }) itemtodo: ITodo
@@ -128,6 +130,9 @@ export default class SingleTodo extends Vue {
this.menuProgress += mycolcl this.menuProgress += mycolcl
this.percProgress += mycolcl this.percProgress += mycolcl
this.clButtPopover = this.sel ? 'pos-item-popover comp_selected' : 'pos-item-popover'
// if (this.inEdit) { // if (this.inEdit) {
// this.classDescr += ' hide' // this.classDescr += ' hide'
// this.classDescrEdit += ' show' // this.classDescrEdit += ' show'
@@ -264,6 +269,7 @@ export default class SingleTodo extends Vue {
} }
} }
*/ */
// Delete Key or Backspage
if (((e.keyCode === 8) || (e.keyCode === 46)) && (this.precDescr === '') && !e.shiftKey) { if (((e.keyCode === 8) || (e.keyCode === 46)) && (this.precDescr === '') && !e.shiftKey) {
e.preventDefault() e.preventDefault()
this.deselectRiga() this.deselectRiga()
@@ -382,11 +388,13 @@ export default class SingleTodo extends Vue {
const deletestr = this.$t('dialog.delete') const deletestr = this.$t('dialog.delete')
const cancelstr = this.$t('dialog.cancel') const cancelstr = this.$t('dialog.cancel')
await askConfirm(this.$q, this.$t('dialog.msg.titledeleteTask'), this.$t('dialog.msg.deleteTask').toString(), deletestr, cancelstr) let msg = this.$t('dialog.msg.deleteTask', {'mytodo' : this.itemtodo.descr })
await askConfirm(this.$q, this.$t('dialog.msg.titledeleteTask'), msg, deletestr, cancelstr)
.then(ris => { .then(ris => {
console.log('ris', ris) console.log('ris', ris)
if (ris) if (ris) {
this.removeitem(this.itemtodo._id) this.removeitem(this.itemtodo._id)
}
}).catch(err => { }).catch(err => {
}) })

View File

@@ -13,7 +13,7 @@
</q-btn> </q-btn>
</div> </div>
<q-input type="textarea" ref="inputdescr" v-model.trim="precDescr" <q-input hide-underline type="textarea" ref="inputdescr" v-model.trim="precDescr"
:class="classDescr" :max-height="50" :class="classDescr" :max-height="50"
@keydown="keyDownArea" v-on:keydown.esc="exitEdit" @blur="exitEdit(true)" @click="editTodo()"/> @keydown="keyDownArea" v-on:keydown.esc="exitEdit" @blur="exitEdit(true)" @click="editTodo()"/>
@@ -50,8 +50,8 @@
</div> </div>
</div> </div>
<div v-if="isTodo()" class="flex-item pos-item" @mouseup.left="mouseUp" @mousedown="clickRiga"> <div v-if="isTodo()" class="flex-item pos-item" @mouseup.left="mouseUp" @mousedown="clickRiga">
<q-btn flat <q-btn push
class="pos-item-popover" :class="clButtPopover"
icon="menu" > icon="menu" >
<q-popover self="top right"> <q-popover self="top right">
<SubMenus :menuPopupTodo="menuPopupTodo" :itemtodo="itemtodo" @clickMenu="clickMenu" @setPriority="setPriority"></SubMenus> <SubMenus :menuPopupTodo="menuPopupTodo" :itemtodo="itemtodo" @clickMenu="clickMenu" @setPriority="setPriority"></SubMenus>
@@ -59,6 +59,8 @@
</q-btn> </q-btn>
</div> </div>
<!--clButtPopover: {{ clButtPopover }}-->
<!--Sel: {{ sel }}-->
<!--<div class="flex-item btn-item">--> <!--<div class="flex-item btn-item">-->
<!--{{itemtodo.expiring_at}}--> <!--{{itemtodo.expiring_at}}-->
<!--</div>--> <!--</div>-->

View File

@@ -105,7 +105,7 @@ export default class Todo extends Vue {
// if (value) { // if (value) {
Todos.actions.dbLoadTodo(false) Todos.actions.dbLoadTodo(false)
.then(() => { .then(() => {
return Todos.actions.updateArrayInMemory() // return Todos.actions.updatefromIndexedDbToStateTodo()
}) })
} }
// } // }
@@ -462,6 +462,7 @@ export default class Todo extends Vue {
return return
} }
// 1) Insert into the IndexedDb
const id = await globalroutines(this, 'write', 'todos', objtodo) const id = await globalroutines(this, 'write', 'todos', objtodo)
// update also the last elem // update also the last elem
if (lastelem !== null) { if (lastelem !== null) {
@@ -470,15 +471,21 @@ export default class Todo extends Vue {
// console.log('calling MODIFY 4', lastelem) // console.log('calling MODIFY 4', lastelem)
} }
// Create record in Memory
Todos.mutations.createNewItem(objtodo)
// Modify the record above to the new last
await this.modify(lastelem, false) await this.modify(lastelem, false)
await this.saveItemToSyncAndDb(rescodes.DB.TABLE_SYNC_TODOS, 'POST', objtodo, true)
await this.updatetable(false, 'insertTodo')
// console.log('ESCO.........')
// empty the field // empty the field
this.todo = '' this.todo = ''
this.updatetable(false, 'insertTodo')
this.saveItemToSyncAndDb(rescodes.DB.TABLE_SYNC_TODOS, 'POST', objtodo, true)
// console.log('ESCO.........')
} }
@@ -525,8 +532,10 @@ export default class Todo extends Vue {
// } // }
}) })
.then(function () { .then(function () {
let data = { message: msg, position: 'bottom', timeout: 3000 } if (msg !== '') {
mythis.$q.notify(data) let data = { message: msg, position: 'bottom', timeout: 3000 }
mythis.$q.notify(data)
}
}) })
.catch(function (err) { .catch(function (err) {
console.error('Errore in globalroutines', table, err) console.error('Errore in globalroutines', table, err)
@@ -534,32 +543,36 @@ export default class Todo extends Vue {
}) })
}) })
if (update) { // if (update) {
// // Update the array in memory, from todos table from IndexedDb // // // Update the array in memory, from IndexedDb update the todos array
await Todos.actions.updateArrayInMemory() // await Todos.actions.updatefromIndexedDbToStateTodo()
.then((ris) => { // .then((ris) => {
return ris // return ris
}) // })
} // }
} else { }
if (cmd === rescodes.DB.CMD_SYNC_NEW_TODOS) {
if (method === 'POST') if (cmd === rescodes.DB.CMD_SYNC_NEW_TODOS) {
await Todos.actions.dbInsertTodo(item) if (method === 'POST')
else if (method === 'PATCH') await Todos.actions.dbInsertTodo(item)
await Todos.actions.dbSaveTodo(item) else if (method === 'PATCH')
} else if (cmd === rescodes.DB.CMD_DELETE_TODOS) await Todos.actions.dbSaveTodo(item)
await Todos.actions.dbDeleteTodo(item) } else if (cmd === rescodes.DB.CMD_DELETE_TODOS) {
await Todos.actions.dbDeleteTodo(item)
} }
} }
async saveItemToSyncAndDb(table: String, method, item: ITodo, update: boolean) { async saveItemToSyncAndDb(table: String, method, item: ITodo, update: boolean) {
// let msg = (method === 'PATCH') ? 'Modif: ' : '++Create: '
// msg = msg + item.descr
return await this.cmdToSyncAndDb(rescodes.DB.CMD_SYNC_NEW_TODOS, table, method, item, 0, '', update) return await this.cmdToSyncAndDb(rescodes.DB.CMD_SYNC_NEW_TODOS, table, method, item, 0, '', update)
} }
async deleteItemToSyncAndDb(table: String, item: ITodo, id, update: boolean) { deleteItemToSyncAndDb(table: String, item: ITodo, id, update: boolean) {
return await this.cmdToSyncAndDb(rescodes.DB.CMD_DELETE_TODOS, table, 'DELETE', item, id, '', update) // let msg = 'Delete: ' + item.descr
this.cmdToSyncAndDb(rescodes.DB.CMD_DELETE_TODOS, table, 'DELETE', item, id, '', update)
} }
/* /*
@@ -591,6 +604,8 @@ export default class Todo extends Vue {
async deleteitem(id) { async deleteitem(id) {
console.log('deleteitem: KEY = ', id) console.log('deleteitem: KEY = ', id)
let myobjtrov = this.getElemById(id) let myobjtrov = this.getElemById(id)
if (myobjtrov !== null) { if (myobjtrov !== null) {
@@ -611,16 +626,23 @@ export default class Todo extends Vue {
await this.modify(myobjnext, false) await this.modify(myobjnext, false)
} }
await this.deleteItemToSyncAndDb(rescodes.DB.TABLE_DELETE_TODOS, myobjtrov, id, true) // 1) Delete from the Todos Array
Todos.mutations.deletemyitem(myobjtrov)
Todos.mutations.setTodos_changed()
const mythis = this // 2) Delete from the IndexedDb
// Delete item globalroutines(this, 'delete', 'todos', null, id)
await globalroutines(this, 'delete', 'todos', null, id)
.then((ris) => { .then((ris) => {
mythis.updatetable(false, 'deleteitem') // Update in to the UI
this.updatetable(true, 'deleteitem')
}).catch((error) => { }).catch((error) => {
console.log('err: ', error) console.log('err: ', error)
}) })
// 3) Delete from the Server (call)
this.deleteItemToSyncAndDb(rescodes.DB.TABLE_DELETE_TODOS, myobjtrov, id, true)
} }
// console.log('FINE deleteitem') // console.log('FINE deleteitem')
@@ -646,7 +668,7 @@ export default class Todo extends Vue {
return await Todos.actions.getTodosByCategory(this.getCategory()) return await Todos.actions.getTodosByCategory(this.getCategory())
.then(arrris => { .then(arrris => {
this.todos_arr = [] // this.todos_arr = []
let arrtemp = [...arrris] let arrtemp = [...arrris]
@@ -663,7 +685,7 @@ export default class Todo extends Vue {
this.todos_arr = [...arrtemp] // make copy this.todos_arr = [...arrtemp] // make copy
console.log('AGGIORNA todos_arr') console.log('AGGIORNA todos_arr [', this.todos_arr.length, ']')
}) })
} }
@@ -797,9 +819,9 @@ export default class Todo extends Vue {
modifyField(recOut, recIn, field) { modifyField(recOut, recIn, field) {
if (String(recOut[field]) !== String(recIn[field])) { if (String(recOut[field]) !== String(recIn[field])) {
console.log('*************** CAMPO ', field, 'MODIFICATO!') // console.log('*************** CAMPO ', field, 'MODIFICATO!')
console.log(recOut[field]) // console.log(recOut[field])
console.log(recIn[field]) // console.log(recIn[field])
recOut.modified = true recOut.modified = true
recOut[field] = recIn[field] recOut[field] = recIn[field]
return true return true
@@ -833,19 +855,22 @@ export default class Todo extends Vue {
miorec.modify_at = new Date().getDate() miorec.modify_at = new Date().getDate()
miorec.modified = false miorec.modified = false
// this.logelem('modify', miorec) // 1) Modify on Global Memory
Todos.mutations.modifymyItem(miorec)
return globalroutines(this, 'write', 'todos', miorec) if (update) {
// 4) Update the filter in Memory
this.updatetable(false, 'modify')
}
// this.logelem('modify', miorec)
// 2) Modify on IndexedDb
globalroutines(this, 'write', 'todos', miorec)
.then(ris => { .then(ris => {
return this.saveItemToSyncAndDb(rescodes.DB.TABLE_SYNC_TODOS_PATCH, 'PATCH', miorec, update) // 3) Modify on the Server (call)
.then(() => { this.saveItemToSyncAndDb(rescodes.DB.TABLE_SYNC_TODOS_PATCH, 'PATCH', miorec, update)
// console.log('SET MODIFIED FALSE')
if (update)
return this.updatetable(false, 'modify')
})
}) })
} }
}) })

View File

@@ -63,7 +63,7 @@ export default async (context, cmd, table, datakey = null, id = '') => {
} else if (cmd === 'write') { } else if (cmd === 'write') {
return await storage.setdata(table, datakey) return await storage.setdata(table, datakey)
} else if (cmd === 'updateinMemory') { } else if (cmd === 'updatefromIndexedDbToStateTodo') {
return await readfromIndexDbToStateTodos(context, table) return await readfromIndexDbToStateTodos(context, table)
} else if (cmd === 'readall') { } else if (cmd === 'readall') {
return await storage.getalldata(table) return await storage.getalldata(table)

View File

@@ -43,10 +43,10 @@ export default class Home extends Vue {
set conta(valore) { set conta(valore) {
GlobalStore.actions.setConta(valore) GlobalStore.actions.setConta(valore)
let my = this.$q.i18n.lang let my = this.$q.i18n.lang
this.showNotification(String(my)) this.showNotif(String(my))
} }
showNotification(message: string, color = 'primary', icon = '') { showNotif(message: string, color = 'primary', icon = '') {
this.$q.notify({ this.$q.notify({
color, color,
icon, icon,
@@ -158,15 +158,15 @@ export default class Home extends Vue {
} }
askfornotification() { askfornotification() {
this.showNotification(this.$t('notification.waitingconfirm'), 'positive', 'notifications') this.showNotif(this.$t('notification.waitingconfirm'), 'positive', 'notifications')
let mythis = this let mythis = this
Notification.requestPermission(function (result) { Notification.requestPermission(function (result) {
console.log('User Choice', result) console.log('User Choice', result)
if (result === 'granted') { if (result === 'granted') {
mythis.showNotification(mythis.$t('notification.confirmed'), 'positive', 'notifications') mythis.showNotif(mythis.$t('notification.confirmed'), 'positive', 'notifications')
} else { } else {
mythis.showNotification(mythis.$t('notification.denied'), 'negative', 'notifications') mythis.showNotif(mythis.$t('notification.denied'), 'negative', 'notifications')
// displayConfirmNotification(); // displayConfirmNotification();
} }

View File

@@ -8,7 +8,7 @@ const messages = {
cancel: 'Annulla', cancel: 'Annulla',
msg: { msg: {
titledeleteTask: 'Elimina Task', titledeleteTask: 'Elimina Task',
deleteTask: 'Vuoi Eliminare questo Task?' deleteTask: "Vuoi Eliminare {mytodo}?"
} }
}, },
comp:{ comp:{
@@ -134,7 +134,7 @@ const messages = {
cancel: 'Cancelar', cancel: 'Cancelar',
msg: { msg: {
titledeleteTask: 'Borrar Tarea', titledeleteTask: 'Borrar Tarea',
deleteTask: 'Quieres borrar este tarea?' deleteTask: 'Quieres borrar %s?'
} }
}, },
comp:{ comp:{
@@ -260,7 +260,7 @@ const messages = {
cancel: 'Cancel', cancel: 'Cancel',
msg: { msg: {
titledeleteTask: 'Delete Task', titledeleteTask: 'Delete Task',
deleteTask: 'Delete this Task?' deleteTask: 'Delete %s?'
} }
}, },
comp:{ comp:{

View File

@@ -132,7 +132,7 @@ export namespace ApiTool {
} }
export async function syncAlternative(mystrparam) { export async function syncAlternative(mystrparam) {
console.log('[ALTERNATIVE Background syncing', mystrparam) // console.log('[ALTERNATIVE Background syncing', mystrparam)
let multiparams = mystrparam.split('|') let multiparams = mystrparam.split('|')
if (multiparams) { if (multiparams) {

View File

@@ -46,9 +46,45 @@ namespace Mutations {
// console.log('******* state.todos_changed', state.todos_changed) // console.log('******* state.todos_changed', state.todos_changed)
} }
function findTodoById(state: ITodosState, id: string) {
for (let i = 0; i < state.todos.length; i++) {
if (state.todos[i]._id === id)
return i
}
return -1
}
function createNewItem(state: ITodosState, myitem: ITodo) {
state.todos.push(myitem)
Todos.mutations.setTodos_changed()
}
function modifymyItem(state: ITodosState, myitem: ITodo) {
// Find record
const ind = findTodoById(state, myitem._id)
if (ind >= 0)
state.todos[ind] = rescodes.jsonCopy(myitem)
}
function deletemyitem(state: ITodosState, myitem: ITodo) {
// Find record
const ind = findTodoById(state, myitem._id)
// Delete Item in to Array
if (ind >= 0)
state.todos.splice(ind, 1)
}
export const mutations = { export const mutations = {
setTestpao: b.commit(setTestpao), setTestpao: b.commit(setTestpao),
setTodos_changed: b.commit(setTodos_changed) setTodos_changed: b.commit(setTodos_changed),
modifymyItem: b.commit(modifymyItem),
deletemyitem: b.commit(deletemyitem),
createNewItem: b.commit(createNewItem)
} }
} }
@@ -226,8 +262,10 @@ namespace Actions {
} else { } else {
consolelogpao('NETWORK UNREACHABLE ! (Error in fetch)', UserStore.getters.getServerCode, ris.status) consolelogpao('NETWORK UNREACHABLE ! (Error in fetch)', UserStore.getters.getServerCode, ris.status)
} }
// Read all data from IndexedDB Store into Memory if ('serviceWorker' in navigator) {
await updateArrayInMemory(context) // Read all data from IndexedDB Store into Memory
await updatefromIndexedDbToStateTodo(context)
}
} else { } else {
if (ris.status === rescodes.OK && checkPending) { if (ris.status === rescodes.OK && checkPending) {
waitAndcheckPendingMsg(context) waitAndcheckPendingMsg(context)
@@ -235,11 +273,11 @@ namespace Actions {
} }
} }
async function updateArrayInMemory(context) { async function updatefromIndexedDbToStateTodo(context) {
// console.log('Update the array in memory, from todos table from IndexedDb') // console.log('Update the array in memory, from todos table from IndexedDb')
await globalroutines(null, 'updateinMemory', 'todos', null) await globalroutines(null, 'updatefromIndexedDbToStateTodo', 'todos', null)
.then(() => { .then(() => {
// console.log('updateArrayInMemory! ') console.log('updatefromIndexedDbToStateTodo! ')
return true return true
}) })
} }
@@ -269,78 +307,57 @@ namespace Actions {
return await dbInsertSaveTodo(context, itemtodo, 'POST') return await dbInsertSaveTodo(context, itemtodo, 'POST')
} }
function UpdateNewIdFromDB(oldItem, newItem, method) { async function dbInsertSaveTodo(context, itemtodo: ITodo, method) {
// console.log('PRIMA state.todos', state.todos)
// console.log('ITEM', newItem) if (!('serviceWorker' in navigator)) {
if (method === 'POST') {
state.todos.push(newItem) console.log('dbInsertSaveTodo', itemtodo, method)
Todos.mutations.setTodos_changed() let call = process.env.MONGODB_HOST + '/todos'
// } else if (method === 'PATCH') {
// state.todos.map(item => { if (UserStore.state.userId === '')
// if (item._id === newItem._id) { return false // Login not made
// return newItem
// } if (method !== 'POST')
// }) call += '/' + itemtodo._id
console.log('TODO TO SAVE: ', itemtodo)
let res = await Api.SendReq(call, method, itemtodo)
.then(({ res, newItem }) => {
console.log('dbInsertSaveTodo to the Server', newItem)
return (res.status === 200)
})
.catch((error) => {
UserStore.mutations.setErrorCatch(error)
// return UserStore.getters.getServerCode
return false
})
} }
return true
// console.log('DOPO state.todos', state.todos)
}
async function dbInsertSaveTodo(context, itemtodo: ITodo, method) {
console.log('dbInsertSaveTodo', itemtodo, method)
let call = process.env.MONGODB_HOST + '/todos'
if (UserStore.state.userId === '')
return false // Login not made
if (method !== 'POST')
call += '/' + itemtodo._id
console.log('TODO TO SAVE: ', itemtodo)
let res = await Api.SendReq(call, method, itemtodo)
.then(({ res, newItem }) => {
console.log('dbInsertSaveTodo RIS =', newItem)
if (newItem) {
// Update ID on local
UpdateNewIdFromDB(itemtodo, newItem, method)
}
})
.catch((error) => {
UserStore.mutations.setErrorCatch(error)
return UserStore.getters.getServerCode
})
return res
} }
async function dbDeleteTodo(context, item: ITodo) { async function dbDeleteTodo(context, item: ITodo) {
// console.log('dbDeleteTodo', item)
let call = process.env.MONGODB_HOST + '/todos/' + item._id
if (UserStore.state.userId === '') if (!('serviceWorker' in navigator)) {
return false // Login not made // console.log('dbDeleteTodo', item)
let call = process.env.MONGODB_HOST + '/todos/' + item._id
let res = await Api.SendReq(call, 'DELETE', item) if (UserStore.state.userId === '')
.then(function ({ res, itemris }) { return false // Login not made
if (res.status === 200) { let res = await Api.SendReq(call, 'DELETE', item)
// Delete Item in to Array .then(function ({ res, itemris }) {
state.todos.splice(state.todos.indexOf(item), 1) console.log('dbDeleteTodo to the Server')
})
.catch((error) => {
UserStore.mutations.setErrorCatch(error)
return UserStore.getters.getServerCode
})
Todos.mutations.setTodos_changed() return res
} }
return rescodes.OK
})
.catch((error) => {
UserStore.mutations.setErrorCatch(error)
return UserStore.getters.getServerCode
})
return res
} }
async function getTodosByCategory(context, category: string) { async function getTodosByCategory(context, category: string) {
@@ -356,7 +373,7 @@ namespace Actions {
dbSaveTodo: b.dispatch(dbSaveTodo), dbSaveTodo: b.dispatch(dbSaveTodo),
dbLoadTodo: b.dispatch(dbLoadTodo), dbLoadTodo: b.dispatch(dbLoadTodo),
dbDeleteTodo: b.dispatch(dbDeleteTodo), dbDeleteTodo: b.dispatch(dbDeleteTodo),
updateArrayInMemory: b.dispatch(updateArrayInMemory), updatefromIndexedDbToStateTodo: b.dispatch(updatefromIndexedDbToStateTodo),
getTodosByCategory: b.dispatch(getTodosByCategory), getTodosByCategory: b.dispatch(getTodosByCategory),
checkPendingMsg: b.dispatch(checkPendingMsg), checkPendingMsg: b.dispatch(checkPendingMsg),
waitAndcheckPendingMsg: b.dispatch(waitAndcheckPendingMsg) waitAndcheckPendingMsg: b.dispatch(waitAndcheckPendingMsg)

View File

@@ -89,7 +89,7 @@ export const rescodes = {
}, },
INDEX_MENU_DELETE: 3, INDEX_MENU_DELETE: 4,
menuPopupTodo: { menuPopupTodo: {
'it': [ 'it': [
@@ -168,6 +168,11 @@ export const rescodes = {
] ]
}, },
jsonCopy(src) {
return JSON.parse(JSON.stringify(src))
}
} }