From 8d0b66bf1523949cdda736fd5ede12035338bb0e Mon Sep 17 00:00:00 2001 From: Dan Zanfirescu Date: Mon, 9 Jul 2018 23:58:16 +0300 Subject: [PATCH] 1. Create items; 2. Create items type --- README.md | 2 + doc/03-client-app.md | 16 ++ package-lock.json | 300 +++++++++++++++++++++++++++++ package.json | 4 +- src/re-intro/async-calls.js | 84 +++++++- src/ui/addTodo.mjs | 10 + src/ui/clearTodos.mjs | 10 + src/ui/cli.mjs | 54 ++++++ src/ui/deleteTodos.mjs | 10 + src/ui/doneTodos.mjs | 10 + src/ui/index.mjs | 1 + src/ui/showTodos.mjs | 10 + src/ui/todo.mjs | 14 ++ src/ui/todoStore.mjs | 138 +++++++++++++ src/ui/updateTodos.mjs | 13 ++ src/ui/utils/genereateUniqueId.mjs | 8 + 16 files changed, 682 insertions(+), 2 deletions(-) create mode 100644 doc/03-client-app.md create mode 100644 package-lock.json create mode 100644 src/ui/addTodo.mjs create mode 100644 src/ui/clearTodos.mjs create mode 100644 src/ui/cli.mjs create mode 100644 src/ui/deleteTodos.mjs create mode 100644 src/ui/doneTodos.mjs create mode 100644 src/ui/index.mjs create mode 100644 src/ui/showTodos.mjs create mode 100644 src/ui/todo.mjs create mode 100644 src/ui/todoStore.mjs create mode 100644 src/ui/updateTodos.mjs create mode 100644 src/ui/utils/genereateUniqueId.mjs diff --git a/README.md b/README.md index 2a3e62f..8fd01ae 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,5 @@ Branches - [01-create-project](./doc/01-create-project.md) - [02-re-intro](./doc/02-re-intro.md) +- [03-client-app](./doc/03-client-app.md) + diff --git a/doc/03-client-app.md b/doc/03-client-app.md new file mode 100644 index 0000000..0cb75c3 --- /dev/null +++ b/doc/03-client-app.md @@ -0,0 +1,16 @@ +## Description +A lightweight CLI(Command-Line Interface) that helps to manage a todo list application. + +## Run +Navigate to the /src/ui path and run the following command: +```node --experimental-modules cli.mjs``` + +## Usage + +* ```todo add ``` - Create a new todo. Example: ```todo add 'Read Game of Thornes book.'``` +* ```todo clear <'active'|'all'|'completed'>``` - Deletes all todos or a subset of them. Example: ```todo delete 'all'``` +* ```todo delete [ids...]``` - Deletes a todo(s). Example: ```todo delete 'id1' 'id2'``` +* ```todo done [ids...]``` - Marks a todo(s) as done. Example: ```todo done 'id1' 'id2'``` +* ```todo show <'active'|'all'|'completed'>``` - Lists all todo or a subset of them. Example: ```todo show 'active'``` +* ```todo update ``` - Allows to update the text of a specific todo. Example: ```todo update 'id1', 'Read Lord of the Rings book.'``` +* ```help``` - Displays all availables commands \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d25d9e4 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,300 @@ +{ + "name": "xnote-cli", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "babel-polyfill": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", + "requires": { + "babel-runtime": "6.26.0", + "core-js": "2.5.7", + "regenerator-runtime": "0.10.5" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=" + } + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "2.5.7", + "regenerator-runtime": "0.11.1" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + } + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "requires": { + "restore-cursor": "1.0.1" + } + }, + "cli-width": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-1.1.1.tgz", + "integrity": "sha1-pNKT72frt7iNSk1CwMzwDE0eNm0=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "core-js": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=" + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "requires": { + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=" + }, + "inquirer": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.11.0.tgz", + "integrity": "sha1-dEi/qSQJKvMR1HFzu6uZDK4rsCc=", + "requires": { + "ansi-escapes": "1.4.0", + "ansi-regex": "2.1.1", + "chalk": "1.1.3", + "cli-cursor": "1.0.2", + "cli-width": "1.1.1", + "figures": "1.7.0", + "lodash": "3.10.1", + "readline2": "1.0.1", + "run-async": "0.1.0", + "rx-lite": "3.1.2", + "strip-ansi": "3.0.1", + "through": "2.3.8" + }, + "dependencies": { + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + } + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "1.0.1" + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "log-update": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz", + "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=", + "requires": { + "ansi-escapes": "1.4.0", + "cli-cursor": "1.0.2" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mute-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", + "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=" + }, + "node-localstorage": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-0.6.0.tgz", + "integrity": "sha1-RaBgHGky395mRKIzYfG+Fzx1068=" + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" + }, + "readline2": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", + "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "mute-stream": "0.0.5" + } + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "requires": { + "exit-hook": "1.1.1", + "onetime": "1.1.0" + } + }, + "run-async": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", + "requires": { + "once": "1.4.0" + } + }, + "rx-lite": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "vorpal": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/vorpal/-/vorpal-1.12.0.tgz", + "integrity": "sha1-S+eypOSPj8/JzzZIxBnTEcUiFZ0=", + "requires": { + "babel-polyfill": "6.26.0", + "chalk": "1.1.3", + "in-publish": "2.0.0", + "inquirer": "0.11.0", + "lodash": "4.17.10", + "log-update": "1.0.2", + "minimist": "1.2.0", + "node-localstorage": "0.6.0", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/package.json b/package.json index bd1fe8b..2a8e03e 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "keywords": [], "author": "", "license": "ISC", - "dependencies": {}, + "dependencies": { + "vorpal": "^1.12.0" + }, "devDependencies": { "babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-preset-env": "^1.7.0", diff --git a/src/re-intro/async-calls.js b/src/re-intro/async-calls.js index 96545b7..4c2d47e 100644 --- a/src/re-intro/async-calls.js +++ b/src/re-intro/async-calls.js @@ -1,5 +1,87 @@ +// see https://developer.mozilla.org/en-US/docs/Glossary/Callback_function // callbacks +// success callback function +function successCallback(number) { + return `Even number: ${number}`; +} + +// failure callback function +function failureCallback(number) { + return `Odd number: ${number}`; +} + +// pass callback functions as arguments +function calculateEvenNumber(number, successCallback, failureCallback) { + if (number % 2 === 0) { + // invoke success callback function + return successCallback(number); + } else { + // invoke failure callback function + return failureCallback(number); + } +} + +console.assert(calculateEvenNumber(8, successCallback, failureCallback) === 'Even number: 8'); +console.assert(calculateEvenNumber(13, successCallback, failureCallback) === 'Odd number: 13'); + +// see https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise // promises -// async-await \ No newline at end of file +function calculateEvenNumber(number) { + return new Promise((resolve, reject) => { + if (number % 2 === 0) { + resolve(number); + } else { + reject(number); + } + }); +} + +function onResolve(number) { + return `Even number: ${number}`; +} + +function onReject(number) { + throw new Error(`Odd number: ${number}`); +} + +// promise is resolved +calculateEvenNumber(8) + .then(onResolve, onReject) + .then(value => console.assert(value === 'Even number: 8')); + +// promise is rejected +calculateEvenNumber(13) + .then(onResolve, onReject) + .catch(error => console.assert(error.message === 'Odd number: 13')); + +// Promise.all: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all +// promise will resolve only if all the promise passed are resolved +// if one of the promise fails then the promise is rejected +const promise1 = Promise.resolve(1); +const promise2 = 2; +const promise3 = new Promise(resolve => resolve(3)); + +Promise.all([promise1, promise2, promise3]).then(values => console.assert(values.toString() === '1,2,3')); + + +// async-await +// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function +async function calculateEvenNumber(number) { + return new Promise((resolve, reject) => { + if (number % 2 === 0) { + setTimeout(() => resolve(`Even number: ${number}`), 100); + } else { + reject(new Error(`Odd number: ${number}`)); + } + }); +} + +async function foo(number) { + const result = await calculateEvenNumber(number); + return result; +} + +foo(8).then(value => console.assert(value === 'Even number: 8')); +foo(13).catch(error => console.assert(error.message === 'Odd number: 13')); diff --git a/src/ui/addTodo.mjs b/src/ui/addTodo.mjs new file mode 100644 index 0000000..1514c88 --- /dev/null +++ b/src/ui/addTodo.mjs @@ -0,0 +1,10 @@ +import TodoStore from './todoStore'; + +const addTodo = (data, callback) => { + const store = new TodoStore(); + const { todoText } = data; + store.add(todoText); + callback(); +} + +export default addTodo; \ No newline at end of file diff --git a/src/ui/clearTodos.mjs b/src/ui/clearTodos.mjs new file mode 100644 index 0000000..d5db142 --- /dev/null +++ b/src/ui/clearTodos.mjs @@ -0,0 +1,10 @@ +import TodoStore from './todoStore'; + +const clearTodos = (data, callback) => { + const store = new TodoStore(); + const { status } = data; + store.clear(status); + callback(); +} + +export default clearTodos; \ No newline at end of file diff --git a/src/ui/cli.mjs b/src/ui/cli.mjs new file mode 100644 index 0000000..23eb357 --- /dev/null +++ b/src/ui/cli.mjs @@ -0,0 +1,54 @@ +import vorpalModule from 'vorpal'; +import addTodo from './addTodo.mjs'; +import clearTodos from './clearTodos.mjs'; +import deleteTodos from './deleteTodos.mjs'; +import doneTodos from './doneTodos.mjs'; +import showTodos from './showTodos.mjs'; +import updateTodos from './updateTodos.mjs'; + +const vorpal = vorpalModule(); + +vorpal + .command('todo add ', 'Create a new todo. Example: `todo add "Read Game of Thornes book."`') + .action(function(args, callback) { + this.log('todo add:', args.todoText); + addTodo(args, callback); + }); + +vorpal + .command('todo clear ', 'Deletes all todos or a subset of them. Example: `todo delete "all"') + .action(function(args, callback) { + this.log('todo clear:', args.status); + clearTodos(args, callback); + }); + +vorpal + .command('todo delete [ids...]', 'Deletes a todo(s). Example: `todo delete "id1" "id2"`') + .action(function(args, callback) { + this.log('todo delete:', args.ids); + deleteTodos(args, callback); + }); + +vorpal + .command('todo done [ids...]', 'Marks a todo(s) as done. Example: `todo done "id1" "id2"`') + .action(function(args, callback) { + this.log('todo done:', args.ids); + doneTodos(args, callback); + }); +vorpal + .command('todo show ', 'Lists all todo or a subset of them. Example: `todo show "active"`') + .action(function(args, callback) { + this.log('todo show:', args.status); + showTodos(args, callback); + }); + +vorpal + .command('todo update ', 'Allows to update the text of a specific todo. Example: `todo update "id1" "Read Lord of the Rings book."') + .action(function(args, callback) { + this.log('todo update:', { ...args }); + updateTodos(args, callback) + }); + +vorpal + .delimiter('todo-cli$') + .show(); \ No newline at end of file diff --git a/src/ui/deleteTodos.mjs b/src/ui/deleteTodos.mjs new file mode 100644 index 0000000..8f2414e --- /dev/null +++ b/src/ui/deleteTodos.mjs @@ -0,0 +1,10 @@ +import TodoStore from './todoStore'; + +const deleteTodos = (data, callback) => { + const store = new TodoStore(); + const { ids } = data; + store.delete(ids); + callback(); +} + +export default deleteTodos; \ No newline at end of file diff --git a/src/ui/doneTodos.mjs b/src/ui/doneTodos.mjs new file mode 100644 index 0000000..d7e0325 --- /dev/null +++ b/src/ui/doneTodos.mjs @@ -0,0 +1,10 @@ +import TodoStore from './todoStore'; + +const doneTodos = (data, callback) => { + const store = new TodoStore(); + const { ids } = data; + store.done(ids); + callback(); +} + +export default doneTodos; \ No newline at end of file diff --git a/src/ui/index.mjs b/src/ui/index.mjs new file mode 100644 index 0000000..0eec6ba --- /dev/null +++ b/src/ui/index.mjs @@ -0,0 +1 @@ +import TodoList from './todoStore'; diff --git a/src/ui/showTodos.mjs b/src/ui/showTodos.mjs new file mode 100644 index 0000000..2f59ff1 --- /dev/null +++ b/src/ui/showTodos.mjs @@ -0,0 +1,10 @@ +import TodoStore from './todoStore'; + +const showTodos = (data, callback) => { + const store = new TodoStore(); + const { status } = data; + store.show(status); + callback(); +} + +export default showTodos; \ No newline at end of file diff --git a/src/ui/todo.mjs b/src/ui/todo.mjs new file mode 100644 index 0000000..4c138b0 --- /dev/null +++ b/src/ui/todo.mjs @@ -0,0 +1,14 @@ +class Todo { + constructor(text, completed = false, updated = Todo.timestamp(), version = 0) { + this.completed = completed; + this.text = text; + this.updated = updated; + this.version = version; + } + + static timestamp() { + return Date.now(); + } +} + +export default Todo; \ No newline at end of file diff --git a/src/ui/todoStore.mjs b/src/ui/todoStore.mjs new file mode 100644 index 0000000..7c8c2bf --- /dev/null +++ b/src/ui/todoStore.mjs @@ -0,0 +1,138 @@ +import Todo from './todo'; +import genereateUniqueId from './utils/genereateUniqueId'; + +const SUPPORTED_TODO_STATUS = ['active', 'all', 'completed']; +let singletonInstance; + +class TodoStore { + constructor() { + if (!singletonInstance) { + singletonInstance = this; + } + this.todos = []; + return singletonInstance; + } + + /** + * Function to add a todo item + * @param {string} text - the todo text + */ + add(text) { + const todo = new Todo(text); + const id = genereateUniqueId(); + const todoItem = { + id, + ...todo, + }; + this.todos.push(todoItem); + console.log('Todos list:', this.todos); + } + + /** + * Function to clear todo items + * @param {string} status - one of 'active' | 'all' | 'completed' + */ + clear(status) { + let todos; + const statusIndex = SUPPORTED_TODO_STATUS.findIndex(supportedStatus => supportedStatus === status); + const isSupportedStatus = statusIndex >= 0; + + if (!status || !isSupportedStatus) { + console.log('Todo status unknown:', status); + return; + } + + if (status === 'active') { + todos = this.todos.filter(todo => todo.completed); + } + + if (status === 'all') { + todos = []; + } + + if (status === 'completed') { + todos = this.todos.filter(todo => !todo.completed); + } + this.todos = todos; + console.log('Todos list:', todos); + } + + /** + * Function to mark todo as done + * @param {Array} ids - a list of todo id to be marked as done + */ + done(ids) { + ids.map(id => { + const todo = this.todos.find(todo => todo.id === id); + const index = this.todos.findIndex(todo => todo.id === id); + const updatedTodo = { + ...todo, + completed: true, + }; + this.todos.splice(index, 1, updatedTodo); + }); + console.log('Todos list:', this.todos); + } + + /** + * Function to delete todos + * @param {Array} ids - a list of todo id to be deleted + */ + delete(ids) { + ids.map(id => { + const index = this.todos.findIndex(todo => todo.id === id); + const removedTodo = this.todos.splice(index, 1); + console.log('Removed todo:', removedTodo); + }); + console.log('Todos list:', this.todos); + } + + /** + * Function to display todo items + * @param {string} status - one of 'active' | 'all' | 'completed' + */ + show(status) { + let todos; + const statusIndex = SUPPORTED_TODO_STATUS.findIndex(supportedStatus => supportedStatus === status); + const isSupportedStatus = statusIndex >= 0; + + if (!status || !isSupportedStatus) { + console.log('Todo status unknown:', status); + return; + } + + if (status === 'active') { + todos = this.todos.filter(todo => !todo.completed); + } + + if (status === 'all') { + todos = this.todos; + } + + if (status === 'completed') { + todos = this.todos.filter(todo => todo.completed); + } + console.log(`${status} todos items:`, todos); + } + + /** + * Function to update a todo item + * @param {string} id - the id of todo item + * @param {string} text - the text to be update + */ + update(id, text) { + const todo = this.todos.find(todo => todo.id === id); + const index = this.todos.findIndex(todo => todo.id === id); + const updatedTodo = { + ...todo, + text, + updated: Date.now(), + version: todo.version + 1, + }; + this.todos.splice(index, 1, updatedTodo); + console.log('Todos list:', this.todos); + } + +}; + +export default TodoStore; \ No newline at end of file diff --git a/src/ui/updateTodos.mjs b/src/ui/updateTodos.mjs new file mode 100644 index 0000000..85f2e66 --- /dev/null +++ b/src/ui/updateTodos.mjs @@ -0,0 +1,13 @@ +import TodoStore from './todoStore'; + +const doneTodos = (data, callback) => { + const store = new TodoStore(); + const { + todoId, + todoText, + } = data; + store.update(todoId, todoText); + callback(); +} + +export default doneTodos; \ No newline at end of file diff --git a/src/ui/utils/genereateUniqueId.mjs b/src/ui/utils/genereateUniqueId.mjs new file mode 100644 index 0000000..afe070f --- /dev/null +++ b/src/ui/utils/genereateUniqueId.mjs @@ -0,0 +1,8 @@ +/** + * Creates a string that can be used for dynamic id attributes + * Example: "id-so7567s1pcpojemi" + * @returns {string} + */ +const genereateUniqueId = () => `id-${Math.random().toString(36).substr(2, 16)}`; + +export default genereateUniqueId; \ No newline at end of file