├── .gitignore ├── screenshot.png ├── babel.config.js ├── jest.config.js ├── postcss.config.js ├── tailwind.config.js ├── src ├── components │ └── alert-component.js ├── tailwind.css ├── interface │ ├── new-item.js │ ├── list-item.js │ ├── list-projects.js │ └── new-project.js ├── test │ ├── projects.test.js │ ├── to-dos.test.js │ ├── list-items.test.js │ └── new_project.test.js ├── classes │ ├── projects.js │ ├── utils.js │ ├── localstorage.js │ └── to-dos.js └── index.js ├── .stylelintrc.json ├── .eslintrc.json ├── webpack.config.js ├── dist └── index.html ├── .github └── workflows │ └── linters.yml ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore 2 | node_modules/ -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahalrazika/to-do-list/HEAD/screenshot.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [['@babel/preset-env', { targets: { node: 'current' } }]], 3 | }; -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleNameMapper: { 3 | '\\.(css|scss)$': 'identity-obj-proxy', 4 | }, 5 | }; -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | purge: [], 3 | darkMode: false, // or 'media' or 'class' 4 | theme: { 5 | extend: {}, 6 | }, 7 | variants: { 8 | extend: {}, 9 | }, 10 | plugins: [], 11 | }; 12 | -------------------------------------------------------------------------------- /src/components/alert-component.js: -------------------------------------------------------------------------------- 1 | const customAlert = (message) => { 2 | const html = ` 3 |
4 | ${message} 5 |
6 | `; 7 | 8 | return html; 9 | }; 10 | 11 | export default customAlert; -------------------------------------------------------------------------------- /src/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | 6 | .input-wrapper { 7 | @apply mt-1 relative rounded-md shadow-sm border-red-800; 8 | } 9 | 10 | input[type="text"] { 11 | @apply focus:ring-indigo-500 focus:border-indigo-500 block w-full pl-7 pr-12 sm:text-sm rounded-md h-12; 12 | } 13 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["stylelint-config-standard"], 3 | "plugins": ["stylelint-scss", "stylelint-csstree-validator"], 4 | "rules": { 5 | "at-rule-no-unknown": null, 6 | "scss/at-rule-no-unknown": true, 7 | "csstree/validator": true 8 | 9 | }, 10 | "ignoreFiles": ["build/**", "dist/**", "**/reset*.css", "**/bootstrap*.css", "**/tailwind*.css"] 11 | } 12 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "jest": true 6 | }, 7 | "parser": "babel-eslint", 8 | "parserOptions": { 9 | "ecmaVersion": 2018, 10 | "sourceType": "module" 11 | }, 12 | "extends": ["airbnb-base"], 13 | "rules": { 14 | "no-shadow": "off", 15 | "no-param-reassign": "off", 16 | "eol-last": "off", 17 | "arrow-parens": "off" 18 | }, 19 | "ignorePatterns": [ 20 | "dist/", 21 | "build/" 22 | ] 23 | } -------------------------------------------------------------------------------- /src/interface/new-item.js: -------------------------------------------------------------------------------- 1 | import utils from '../classes/utils'; 2 | import ListToDo from './list-item'; 3 | 4 | const NewItem = () => { 5 | const project = utils.getProjectIdFromUrl(); 6 | 7 | const html = ` 8 |

${project.title}

9 | 11 |
12 | Add new item 13 |
14 |
15 |
19 | 20 | 21 | `; 22 | 23 | return html; 24 | }; 25 | 26 | export default NewItem; -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: `${path.resolve(__dirname, 'src')}/index.js`, 5 | output: { 6 | filename: 'main.js', 7 | path: path.resolve(__dirname, 'dist'), 8 | }, 9 | resolve: { 10 | fallback: { 11 | util: require.resolve('util/'), 12 | }, 13 | }, 14 | 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.css$/, 19 | use: [ 20 | 'style-loader', 21 | { 22 | loader: 'css-loader', options: { importLoaders: 1 }, 23 | }, 24 | 'postcss-loader', 25 | ], 26 | }, 27 | { 28 | test: /\.(png|jpg|gif)$/i, 29 | use: [ 30 | { 31 | loader: 'url-loader', 32 | options: { 33 | limit: 8192, 34 | fallback: require.resolve('file-loader'), 35 | }, 36 | }, 37 | ], 38 | }, 39 | ], 40 | }, 41 | 42 | }; -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 24 | 25 |
26 | 27 |
28 | 29 |
30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /src/test/projects.test.js: -------------------------------------------------------------------------------- 1 | const { Project } = require('../classes/projects'); 2 | 3 | describe(' new project ', () => { 4 | const newProject = Project([ 5 | 'Test', 6 | 'Test project', 7 | '02/28/2021', 8 | 'High', 9 | '02/18/2021', 10 | 11 | ]); 12 | 13 | it('expect to create a new project', () => { 14 | expect(Project).toBeDefined(); 15 | }); 16 | 17 | it('expect a project title ', () => { 18 | expect(newProject.title).toEqual('Test'); 19 | }); 20 | 21 | it('expect a project description ', () => { 22 | expect(newProject.description).toEqual('Test project'); 23 | }); 24 | 25 | it('expect a project priority ', () => { 26 | expect(newProject.priority).toEqual('High'); 27 | }); 28 | 29 | it('expect a project dueDate ', () => { 30 | expect(newProject.dueDate).toEqual('02/28/2021'); 31 | }); 32 | 33 | it('expect a project createdDate ', () => { 34 | expect(newProject.createdDate).toEqual('02/18/2021'); 35 | }); 36 | it('expect a project createdDate to qual the same date as creation date ', () => { 37 | expect(newProject.createdDate).not.toEqual('02/29/2021'); 38 | }); 39 | }); -------------------------------------------------------------------------------- /src/interface/list-item.js: -------------------------------------------------------------------------------- 1 | import utils from '../classes/utils'; 2 | 3 | const ListToDo = () => { 4 | const project = utils.getProjectIdFromUrl(); 5 | const items = project.todos; 6 | const toDoList = []; 7 | for (let i = 0; i < items.length; i += 1) { 8 | const toDoHtml = ` 9 |
10 |
11 | ${items[i].itemDescription} 12 |
13 |
14 | ${items[i].completed ? '' : ``} 15 |
16 |
17 | Delete 18 |
19 |
20 | `; 21 | toDoList.push(toDoHtml); 22 | } 23 | 24 | const html = ` 25 | 26 |
27 |

These are your toDo

28 | ${toDoList.length > 0 ? toDoList.join(' ') : '
No todos yet.
'} 29 |
30 | `; 31 | 32 | return html; 33 | }; 34 | 35 | export default ListToDo; -------------------------------------------------------------------------------- /src/classes/projects.js: -------------------------------------------------------------------------------- 1 | import { format } from 'date-fns'; 2 | import DB from './localstorage'; 3 | import utils from './utils'; 4 | 5 | const Project = ([title, description, dueDate, priority, todos = [], createdDate = format(new Date(), 'MM/dd/yyyy')]) => ({ 6 | title, 7 | description, 8 | priority, 9 | dueDate, 10 | createdDate, 11 | todos, 12 | }); 13 | 14 | const createProject = () => { 15 | const values = utils.getFormValues(); 16 | if (utils.validateForms(values)) { 17 | const p = Project(values); 18 | DB.saveProject(p); 19 | window.location.hash = '#complete'; 20 | } 21 | }; 22 | 23 | const addSelectProjectLink = () => { 24 | const projects = document.querySelectorAll('.project-card'); 25 | for (let i = 0; i < projects.length; i += 1) { 26 | const el = projects[i]; 27 | el.addEventListener('click', (event) => { event.preventDefault(); window.location.hash = `#add-item/${i}`; }); 28 | } 29 | }; 30 | 31 | const addCreateProjectLink = () => { 32 | const el = document.getElementById('button'); 33 | el.addEventListener('click', () => createProject()); 34 | }; 35 | 36 | export { 37 | Project, createProject, addCreateProjectLink, addSelectProjectLink, 38 | }; -------------------------------------------------------------------------------- /src/test/to-dos.test.js: -------------------------------------------------------------------------------- 1 | const { Todo, ToDoItem } = require('../classes/to-dos'); 2 | 3 | describe(' new To do ', () => { 4 | const ToDoTest = ToDoItem('itemDescription', true, '02/18/2021'); 5 | it('expect to create a new todo', () => { 6 | expect(Todo).toBeDefined(); 7 | }); 8 | 9 | it('expect To do description ', () => { 10 | expect(ToDoTest.itemDescription).toEqual('itemDescription'); 11 | }); 12 | it('expect To do completed ', () => { 13 | expect(ToDoTest.completed).toEqual(true); 14 | }); 15 | it('expect To do not completed ', () => { 16 | expect(ToDoTest.completed).not.toEqual(false); 17 | }); 18 | it('expect To do Date ', () => { 19 | expect(ToDoTest.date).toEqual('02/18/2021'); 20 | }); 21 | it('expect to add new to do ', () => { 22 | const project = { 23 | title: 'Test', 24 | description: 'Test project', 25 | dueDate: '02/28/2021', 26 | priority: 'High', 27 | createdDate: '02/16/2021', 28 | todos: [], 29 | }; 30 | project.todos.push(ToDoTest); 31 | 32 | expect(project.todos).toContain(ToDoTest); 33 | }); 34 | it('Clicking to add new item ', () => { 35 | expect(Todo).toBeDefined(); 36 | }); 37 | }); -------------------------------------------------------------------------------- /src/classes/utils.js: -------------------------------------------------------------------------------- 1 | import customAlert from '../components/alert-component'; 2 | import DB from './localstorage'; 3 | 4 | const utils = { 5 | getProjectIdFromUrl() { 6 | const projects = window.location.hash.split('/'); 7 | const projectId = projects[1]; 8 | const project = DB.getSingleProject(projectId); 9 | project.id = projectId; 10 | return project; 11 | }, 12 | getFormValues() { 13 | const valuesForm = document.querySelectorAll('input'); 14 | const values = []; 15 | for (let i = 0; i < valuesForm.length; i += 1) { 16 | values.push(valuesForm[i].value); 17 | } 18 | values.push(document.querySelector('select').value); 19 | return values; 20 | }, 21 | validateForms([...args]) { 22 | for (let i = 0; i < args.length; i += 1) { 23 | if (args[i] === '') { 24 | document.querySelector('body').innerHTML += customAlert('Please fill all fields.'); 25 | return false; 26 | } 27 | } 28 | return true; 29 | }, 30 | checkForHashInUrl() { 31 | let url; 32 | if (window.location.hash) { 33 | url = new URL(window.location.hash); 34 | } else { 35 | url = '#index'; 36 | } 37 | 38 | return url; 39 | }, 40 | }; 41 | 42 | export default utils; -------------------------------------------------------------------------------- /src/classes/localstorage.js: -------------------------------------------------------------------------------- 1 | const DB = { 2 | initialize() { 3 | const defaultProjct = [{ 4 | title: 'Restaurant js app ', 5 | description: 'an app use js vanilla ', 6 | priority: 'High', 7 | dueDate: '2021-01-28', 8 | createdDate: '15-01-2021', 9 | todos: [{ 10 | itemDescription: 'setup webpack ', 11 | completed: false, 12 | date: '01/13/2021', 13 | }], 14 | }]; 15 | if (window.localStorage.getItem('projects') == null) { 16 | window.localStorage.setItem('projects', JSON.stringify(defaultProjct)); 17 | } 18 | }, 19 | 20 | getProjects() { 21 | const projects = JSON.parse(window.localStorage.getItem('projects')); 22 | return projects; 23 | }, 24 | saveProject(project) { 25 | const b = this.getProjects(); 26 | project.id = b.length + 1; 27 | b.push(project); 28 | window.localStorage.setItem('projects', [JSON.stringify(b)]); 29 | }, 30 | getSingleProject(projectId) { 31 | const project = this.getProjects(); 32 | return project[projectId]; 33 | }, 34 | updateProject(updatedProject) { 35 | const projects = this.getProjects(); 36 | projects[updatedProject.id] = updatedProject; 37 | window.localStorage.setItem('projects', JSON.stringify(projects)); 38 | }, 39 | }; 40 | 41 | export default DB; -------------------------------------------------------------------------------- /.github/workflows/linters.yml: -------------------------------------------------------------------------------- 1 | name: Linters 2 | 3 | on: pull_request 4 | 5 | env: 6 | FORCE_COLOR: 1 7 | 8 | jobs: 9 | eslint: 10 | name: ESLint 11 | runs-on: ubuntu-18.04 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: "12.x" 17 | - name: Setup ESLint 18 | run: | 19 | npm install --save-dev eslint@6.8.x eslint-config-airbnb-base@14.1.x eslint-plugin-import@2.20.x babel-eslint@10.1.x 20 | [ -f .eslintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/javascript/.eslintrc.json 21 | - name: ESLint Report 22 | run: npx eslint . 23 | stylelint: 24 | name: Stylelint 25 | runs-on: ubuntu-18.04 26 | steps: 27 | - uses: actions/checkout@v2 28 | - uses: actions/setup-node@v1 29 | with: 30 | node-version: "12.x" 31 | - name: Setup Stylelint 32 | run: | 33 | npm install --save-dev stylelint@13.3.x stylelint-scss@3.17.x stylelint-config-standard@20.0.x stylelint-csstree-validator 34 | [ -f .stylelintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/javascript/.stylelintrc.json 35 | - name: Stylelint Report 36 | run: npx stylelint "**/*.{css,scss}" -------------------------------------------------------------------------------- /src/test/list-items.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | import ListToDo from '../interface/list-item'; 3 | 4 | describe('list to do ', () => { 5 | const project = { 6 | title: 'Test', 7 | description: 'Test project', 8 | dueDate: '02/28/2021', 9 | priority: 'High', 10 | createdDate: '02/16/2021', 11 | todos: { 12 | itemDescription: 'test', 13 | completed: true, 14 | }, 15 | }; 16 | 17 | const items = project.todos; 18 | const toDoList = []; 19 | test('add to do to the list ', () => { 20 | toDoList.push(items); 21 | 22 | expect(toDoList).toContain(items); 23 | }); 24 | 25 | test('to test the completed case equal to true ', () => { 26 | const checkboxInput = items.completed; 27 | expect(checkboxInput).toEqual(true); 28 | }); 29 | test('to test the completed case not equal to true ', () => { 30 | const checkboxInput = items.completed; 31 | expect(checkboxInput).not.toEqual(false); 32 | }); 33 | 34 | test('to list of all todos', () => { 35 | document.body.innerHTML = ` 36 |
37 |

These are your toDo

38 | ${toDoList.length > 0 ? toDoList.join(' ') : '
No todos yet.
'} 39 |
40 | `; 41 | 42 | const todoList = document.querySelectorAll('#todo-list'); 43 | expect(todoList.length).toBe(1); 44 | expect(todoList.length > 1).toBe(false); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "to-do-list", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "webpack --watch --mode development", 8 | "build": "webpack --mode production", 9 | "test": "jest" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/rahalrazika/to-do-list.git" 14 | }, 15 | "author": "", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/rahalrazika/to-do-list/issues" 19 | }, 20 | "homepage": "https://github.com/rahalrazika/to-do-list#readme", 21 | "devDependencies": { 22 | "@babel/core": "^7.12.16", 23 | "@babel/preset-env": "^7.12.16", 24 | "babel-eslint": "^10.1.0", 25 | "babel-jest": "^26.6.3", 26 | "css-loader": "^5.0.1", 27 | "eslint": "^6.8.0", 28 | "eslint-config-airbnb-base": "^14.1.0", 29 | "eslint-plugin-import": "^2.20.2", 30 | "file-loader": "^6.2.0", 31 | "jest": "^26.6.3", 32 | "postcss": "^8.2.1", 33 | "postcss-loader": "^4.1.0", 34 | "style-loader": "^2.0.0", 35 | "stylelint": "^13.3.3", 36 | "stylelint-config-standard": "^20.0.0", 37 | "stylelint-csstree-validator": "^1.9.0", 38 | "stylelint-scss": "^3.17.2", 39 | "url-loader": "^4.1.1", 40 | "webpack": "^5.10.0", 41 | "webpack-cli": "^4.2.0" 42 | }, 43 | "dependencies": { 44 | "autoprefixer": "^10.1.0", 45 | "date-fns": "^2.16.1", 46 | "postcss-import": "^13.0.0", 47 | "tailwindcss": "^2.0.1", 48 | "util": "^0.12.3" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/interface/list-projects.js: -------------------------------------------------------------------------------- 1 | import { format } from 'date-fns'; 2 | import DB from '../classes/localstorage'; 3 | 4 | const ListProjects = (projects) => { 5 | const projectsList = []; 6 | for (let i = 0; i < projects.length; i += 1) { 7 | const projectCardHtml = ` 8 |
9 |
10 |
11 |
${format(new Date(projects[i].dueDate), 'dd/MM')}
12 |
13 |
14 |
15 | ${projects[i].title} 16 |
17 |
18 | ${projects[i].todos.length > 0 ? `${projects[i].todos.length} todos` : 'No todos'} 19 |
20 |
21 | ${projects[i].priority} 22 |
23 |
24 |
25 | `; 26 | 27 | projectsList.push(projectCardHtml); 28 | } 29 | 30 | const html = ` 31 |

These are your projects

32 |
33 | 34 | ${projectsList.length > 0 ? projectsList.join(' ') : DB.defaultProjct()} 35 |
36 | `; 37 | 38 | return html; 39 | }; 40 | 41 | export default ListProjects; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css'; 2 | import newProject from './interface/new-project'; 3 | import NewItem from './interface/new-item'; 4 | import ListProjects from './interface/list-projects'; 5 | import DB from './classes/localstorage'; 6 | import { addCreateProjectLink, addSelectProjectLink } from './classes/projects'; 7 | import Todo from './classes/to-dos'; 8 | import utils from './classes/utils'; 9 | 10 | const routes = () => { 11 | const url = utils.checkForHashInUrl(); 12 | 13 | switch (true) { 14 | case /#new-project/.test(url.hash): 15 | document.querySelector('#content').innerHTML = newProject(); 16 | addCreateProjectLink(); 17 | break; 18 | case /.*?add-item(.*?)/g.test(url.hash): 19 | document.querySelector('#content').innerHTML = NewItem(); 20 | Todo.todoUtils.addCreateAddItemLinkListner(); 21 | Todo.todoUtils.addCheckboxListner(); 22 | break; 23 | default: 24 | window.location.hash = '#index'; 25 | document.querySelector('#content').innerHTML = ListProjects(DB.getProjects()); 26 | addSelectProjectLink(); 27 | break; 28 | } 29 | }; 30 | 31 | const urlListner = () => { 32 | window.onhashchange = () => routes(); 33 | }; 34 | 35 | const addEventListener = (id, url) => { 36 | const createProjectButton = document.getElementById(id); 37 | createProjectButton.addEventListener('click', () => { window.location.hash = url; }); 38 | }; 39 | 40 | document.addEventListener('DOMContentLoaded', () => { 41 | addEventListener('#createNewProjectButton', '#new-project'); 42 | addEventListener('#logo', '#index'); 43 | routes(); 44 | urlListner(); 45 | }); 46 | 47 | DB.initialize(); 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # to-do-list 2 | 3 | A todo list app built with javascript vanilla 4 | ## Screenshots 5 | ![Home Page](screenshot.png) 6 | 7 | 8 | 9 | 10 | ## Built With 🔨 11 | - Javascript 12 | - Html 13 | - Webpack 14 | 15 | 16 | ## Live Demo 🚀 17 | [TODO](https://rahalrazika.github.io/to-do-list/) 18 | 19 | ## Getting Started 💻 20 | 21 | ## Installation 22 | 23 | To get a local copy of the repository please run the following commands on your terminal: 24 | 25 | ``` 26 | $ cd 27 | ``` 28 | 29 | ``` 30 | $ git clone git@github.com:rahalrazika/to-do-list.git 31 | ``` 32 | 33 | ## Testing 34 | Below shows the following commands you will need to run tests: 35 | 1- Install Jest using yarn: 36 | ``` 37 | yarn add --dev jest 38 | 39 | ``` 40 | - or npm : 41 | ``` 42 | npm install --save-dev jest 43 | 44 | ``` 45 | 2- Or just run 46 | ``` 47 | npm install 48 | 49 | ``` 50 | to initialize the project 51 | 52 | 3- To run the tests, execute 53 | ``` 54 | npm run test 55 | 56 | ``` 57 | or 58 | ``` 59 | yarn test 60 | 61 | ``` 62 | 63 | ## Authors 64 | 👤 **Diego Lira** 65 | 66 | - Github: [@Diego Lira](https://github.com/lirad) 67 | - Linkedin : [Diego Lira](https://www.linkedin.com/in/diegoalira/) 68 | 69 | 👤 **Razika Rahal** 70 | 71 | - Github: [@rahalrazika](https://github.com/rahalrazika) 72 | - Linkedin : [Razika Rahal](https://www.linkedin.com/in/razika-rahal-85539bbb/) 73 | - Twitter: [@Razika Rahal](https://twitter.com/RahalRazika) 74 | 75 | 76 | ## 🤝 Contributing 77 | 78 | Contributions, issues and feature requests are welcome! 79 | 80 | ## Show your support 81 | 82 | Give a ⭐️ if you like this project! 83 | 84 | ## Acknowledgments 85 | 86 | - Odin projects 87 | - Microverse -------------------------------------------------------------------------------- /src/classes/to-dos.js: -------------------------------------------------------------------------------- 1 | import { format } from 'date-fns'; 2 | import ListToDo from '../interface/list-item'; 3 | import DB from './localstorage'; 4 | import utils from './utils'; 5 | 6 | const ToDoItem = (itemDescription, completed = false, date = format(new Date(), 'MM/dd/yyyy')) => ({ itemDescription, completed, date }); 7 | 8 | const Todo = { 9 | updateTodoList() { 10 | document.getElementById('todo-list').innerHTML = ListToDo(); 11 | this.todoUtils.addCheckboxListner(); 12 | }, 13 | addItem() { 14 | const project = utils.getProjectIdFromUrl(); 15 | const text = document.querySelector('input').value; 16 | const todo = ToDoItem(text, false); 17 | project.todos.push(todo); 18 | DB.updateProject(project); 19 | this.updateTodoList(); 20 | document.querySelector('input').value = ''; 21 | }, 22 | deleteItem(id) { 23 | const project = utils.getProjectIdFromUrl(); 24 | project.todos.splice(id, 1); 25 | DB.updateProject(project); 26 | this.updateTodoList(); 27 | }, 28 | toggleChecked(id) { 29 | const project = utils.getProjectIdFromUrl(); 30 | project.todos[id].completed = !project.todos[id].completed; 31 | DB.updateProject(project); 32 | this.updateTodoList(); 33 | }, 34 | todoUtils: { 35 | 36 | addCreateAddItemLinkListner() { 37 | const el = document.getElementById('button'); 38 | el.addEventListener('click', () => Todo.addItem()); 39 | }, 40 | addCheckboxListner() { 41 | const checkbox = document.querySelectorAll('.checkbox'); 42 | const deleteItem = document.querySelectorAll('.delete'); 43 | for (let i = 0; i < checkbox.length; i += 1) { 44 | checkbox[i].id = i; 45 | deleteItem[i].id = i; 46 | checkbox[i].addEventListener('click', () => Todo.toggleChecked(checkbox[i].id)); 47 | deleteItem[i].addEventListener('click', () => Todo.deleteItem(deleteItem[i].id)); 48 | } 49 | }, 50 | }, 51 | }; 52 | 53 | export { Todo, ToDoItem }; -------------------------------------------------------------------------------- /src/interface/new-project.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | import { Project } from '../classes/projects'; 3 | 4 | const newProject = () => { 5 | const html = ` 6 |
7 | 8 |
9 | 11 |
12 | 13 |
14 | 16 |
17 | 18 |
19 | 21 |
22 |
23 | 24 | 25 | 31 |
32 |
33 | Create project 34 |
35 |
36 | `; 37 | 38 | return html; 39 | }; 40 | 41 | export default newProject; -------------------------------------------------------------------------------- /src/test/new_project.test.js: -------------------------------------------------------------------------------- 1 | import newProject from '../interface/new-project'; 2 | 3 | test('create a new project form', () => { 4 | document.body.innerHTML = ` 5 |
6 | 7 |
8 | 10 |
11 | 12 |
13 | 15 |
16 | 17 |
18 | 20 |
21 |
22 | 23 | 24 | 30 |
31 |
32 | Create project 33 |
34 |
35 | `; 36 | newProject(); 37 | const projectCard = document.querySelectorAll('.project-card'); 38 | expect(projectCard.length).toBe(0); 39 | }); 40 | test('test Create project button', () => { 41 | document.body.innerHTML = '
Create project
'; 42 | const createProject = document.getElementById('button'); 43 | expect(createProject.innerHTML).toBe('Create project'); 44 | }); --------------------------------------------------------------------------------