diff --git a/.gitignore b/.gitignore index 3f8e1d990..aaedb6779 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -.vscode/* \ No newline at end of file +config/.env +.vscode \ No newline at end of file diff --git a/config/.env b/config/.env deleted file mode 100644 index dbe126e0a..000000000 --- a/config/.env +++ /dev/null @@ -1,2 +0,0 @@ -PORT = 2121 -DB_STRING = mongodb+srv://demo:demo@cluster0.hcds1.mongodb.net/todos?retryWrites=true&w=majority \ No newline at end of file diff --git a/controllers/todos.js b/controllers/todos.js index b10950f93..cb597ca77 100644 --- a/controllers/todos.js +++ b/controllers/todos.js @@ -5,39 +5,65 @@ module.exports = { console.log(req.user) try{ const todoItems = await Todo.find({userId:req.user.id}) - const itemsLeft = await Todo.countDocuments({userId:req.user.id,completed: false}) - res.render('todos.ejs', {todos: todoItems, left: itemsLeft, user: req.user}) + const countBacklog = await Todo.countDocuments({userId:req.user.id,backlog: true}) + const countTodo = await Todo.countDocuments({userId:req.user.id,toDo: true}) + const countDoing = await Todo.countDocuments({userId:req.user.id,doing: true}) + const countCompleted = await Todo.countDocuments({userId:req.user.id,completed: true}) + res.render('todos.ejs', {todos: todoItems, leftBacklog: countBacklog, leftTodo: countTodo, leftDoing: countDoing, leftCompleted: countCompleted, user: req.user}) }catch(err){ console.log(err) } }, createTodo: async (req, res)=>{ try{ - await Todo.create({todo: req.body.todoItem, completed: false, userId: req.user.id}) + await Todo.create({todoName: req.body.todoItem, backlog: true, toDo: false, doing: false, completed: false, userId: req.user.id}) console.log('Todo has been added!') res.redirect('/todos') }catch(err){ console.log(err) } }, - markComplete: async (req, res)=>{ + changeStatus: async (req, res)=>{ try{ - await Todo.findOneAndUpdate({_id:req.body.todoIdFromJSFile},{ - completed: true - }) - console.log('Marked Complete') - res.json('Marked Complete') + const taskStatus = await Todo.find({_id:req.body.todoIdFromJSFile}) + if (taskStatus[0].backlog) { + await Todo.findOneAndUpdate({_id:req.body.todoIdFromJSFile},{ + backlog: false, + toDo: true + })} + else if (taskStatus[0].toDo) { + await Todo.findOneAndUpdate({_id:req.body.todoIdFromJSFile},{ + toDo: false, + doing: true + })} + else if (taskStatus[0].doing) { + await Todo.findOneAndUpdate({_id:req.body.todoIdFromJSFile},{ + doing: false, + completed: true + })} + else if (taskStatus[0].completed) { + await Todo.findOneAndUpdate({_id:req.body.todoIdFromJSFile},{ + completed: false, + toDo: true + })} + console.log(taskStatus) + console.log('Changed Status') + res.json('Changed Status') }catch(err){ console.log(err) } }, - markIncomplete: async (req, res)=>{ + markComplete: async (req, res)=>{ try{ await Todo.findOneAndUpdate({_id:req.body.todoIdFromJSFile},{ - completed: false + backlog: false, + toDo: false, + doing: false, + completed: true }) - console.log('Marked Incomplete') - res.json('Marked Incomplete') + console.log('Marked Complete') + console.log({_id:req.body.todoIdFromJSFile}) + res.json('Marked Complete') }catch(err){ console.log(err) } diff --git a/models/Todo.js b/models/Todo.js index 8698f6900..9acb5afc5 100644 --- a/models/Todo.js +++ b/models/Todo.js @@ -1,10 +1,22 @@ const mongoose = require('mongoose') const TodoSchema = new mongoose.Schema({ - todo: { + todoName: { type: String, required: true, }, + backlog: { + type: Boolean, + required: true, + }, + toDo: { + type: Boolean, + required: true, + }, + doing: { + type: Boolean, + required: true, + }, completed: { type: Boolean, required: true, diff --git a/public/css/auth.css b/public/css/auth.css new file mode 100644 index 000000000..2f85897f8 --- /dev/null +++ b/public/css/auth.css @@ -0,0 +1,158 @@ +*, +*:before, +*:after{ + padding: 0; + margin: 0; + box-sizing: border-box; +} +body{ + background-color: #ffffff; +} +.background{ + width: 430px; + height: 520px; + position: absolute; + transform: translate(-50%,-50%); + left: 50%; + top: 50%; +} +.background .shape{ + height: 200px; + width: 200px; + position: absolute; + border-radius: 50%; +} +.shape:first-child{ + background: linear-gradient( + #1641a6, + #1845ad, + #23a2f6 + ); + left: -80px; + top: -80px; +} +.shape:last-child{ + background: linear-gradient( + to right, + #ff512f, + #f09819 + ); + right: -30px; + bottom: -75px; +} + +.alert { + margin-top: 5px; + font-size: 12px; + letter-spacing: 0; + line-height:1.3; +} + +.fa-check-double { + text-align: center; + margin-bottom: 30px; +} + +form{ + display: flex; + flex-direction: column; + justify-content: center; + min-height: 380px; + width: 350px; + background-color: rgba(21, 102, 25, 0.75); + position: absolute; + transform: translate(-50%,-50%); + top: 50%; + left: 50%; + border-radius: 10px; + backdrop-filter: blur(10px); + border: 2px solid rgba(255,255,255,0.1); + box-shadow: 0 0 40px rgba(8,7,16,0.6); + padding: 20px 35px; +} +form *{ + font-family: 'Poppins',sans-serif; + color: #ffffff; + letter-spacing: 0.5px; + outline: none; + border: none; +} +form h3{ + margin-top: 0; + font-size: 24px; + font-weight: 500; + text-align: center; +} + +label{ + display: block; + margin-top: 30px; + font-size: 16px; + font-weight: 500; +} +input{ + display: block; + height: 50px; + width: 100%; + background-color: rgba(255,255,255,0.07); + border-radius: 3px; + padding: 0 10px; + margin-top: 8px; + font-size: 14px; + font-weight: 300; +} +::placeholder{ + color: #e5e5e5; +} +.primary { + display: block; + text-decoration: none; + text-align: center; + margin-top: 15px; + width: 100%; + background-color: #ffffff; + color: #080710; + padding: 10px 0; + font-size: 16px; + font-weight: 600; + border-radius: 5px; + cursor: pointer; +} +.secondary { + display: block; + text-align: center; + text-decoration: none; + margin-top: 15px; + width: 100%; + background-color: #ffffff30; + border: solid 3px rgba(255, 255, 255, 0.624); + color: #ffffff; + padding: 8px 0; + font-size: 16px; + font-weight: 600; + border-radius: 5px; + cursor: pointer; +} + +.social{ + margin-top: 30px; + display: flex; +} +.social div{ + background: red; + width: 150px; + border-radius: 3px; + padding: 5px 10px 10px 5px; + background-color: rgba(255,255,255,0.27); + color: #eaf0fb; + text-align: center; +} +.social div:hover{ + background-color: rgba(255,255,255,0.47); +} +.social .fb{ + margin-left: 25px; +} +.social i{ + margin-right: 4px; +} \ No newline at end of file diff --git a/public/css/style.css b/public/css/style.css index fbd6b929e..f14b7c8f4 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1,10 +1,295 @@ +html { + font-family: 'Poppins', sans-serif; +} + h1{ color: red; + font-size: 22px; +} +h3 { + font-size: 14px; + font-weight: 500; +} +p, span { + font-size: 12px; +} + +nav { + margin: 0 10vw; + display: flex; + justify-content: space-between; + align-items: center; +} + +.background{ + width: 430px; + height: 520px; + position: absolute; + transform: translate(-50%,-50%); + left: 50%; + top: 50%; + z-index: -100; +} +.background .shape{ + height: 200px; + width: 200px; + position: absolute; + border-radius: 50%; + } +.shape:first-child{ + background: linear-gradient( + #1641a6, + #1845ad, + #23a2f6 + ); + left: -380px; + top: -190px; + } + .shape:last-child{ + background: linear-gradient( + to right, + #ff512f, + #f09819 + ); + right: -300px; + bottom: -75px; + } + +.uncompletedBox, .completedBox { + width: 20%; + max-width: 160px; + padding: 0 10px; + border-radius: 15px; +} +.backlog { + box-shadow: rgba(124, 124, 124, 0.4) 5px -5px, rgba(124, 124, 124, 0.3) 10px -10px, rgba(124, 124, 124, 0.2) 15px -15px, rgba(124, 124, 124, 0.1) 20px -20px, rgba(124, 124, 124, 0.05) 25px -25px; + border: solid 2px rgba(124, 124, 124, 0.4); +} +.todo { + box-shadow: rgba(236, 45, 45, 0.4) 5px -5px, rgba(236, 45, 45, 0.3) 10px -10px, rgba(236, 45, 45, 0.2) 15px -15px, rgba(236, 45, 45, 0.1) 20px -20px, rgba(236, 45, 45, 0.05) 25px -25px; + border: solid 2px rgba(236, 45, 45, 0.4); +} +.doing { + box-shadow: rgba(254, 201, 25, 0.4) 5px -5px, rgba(254, 201, 25, 0.3) 10px -10px, rgba(254, 201, 25, 0.2) 15px -15px, rgba(254, 201, 25, 0.1) 20px -20px, rgba(254, 201, 25, 0.05) 25px -25px; + border: solid 2px rgba(254, 201, 25, 0.4); +} +.completedBox { + background-color: rgb(46, 240, 56, 0.6); + box-shadow: rgba(46, 240, 56, 0.4) 5px -5px, rgba(46, 240, 56, 0.3) 10px -10px, rgba(46, 240, 56, 0.2) 15px -15px, rgba(46, 240, 56, 0.1) 20px -20px, rgba(46, 240, 56, 0.05) 25px -25px; +} + +.tasksBox { + margin: 10vh 0; + display: flex; + justify-content: center; + gap: 50px; } +.fa-ul { + margin-left: 0 !important; +} +.todoItem { + list-style-type: none; + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: 5px; + margin-bottom: 5px; + border-bottom: solid 1px rgba(128, 128, 128, 0.238); +} +.fa-square-xmark { + line-height: 0 !important; + color:rgb(220, 36, 36); + cursor: pointer; +} + +.not, .completed { + width: 100px; +} + .completed{ text-decoration: line-through; - color: gray; + color: rgb(99, 99, 99); +} + +.btn { + border-radius: 4px; + font-size: 12px; + padding: 4px 6px; + text-decoration: none; + } + +.btn::after { +max-width: 100%; +height: 2px; +display: block; +content: ""; +background: linear-gradient(-90deg, #ff9100 0%, #f10366 50%, #6173ff 100%); +opacity: 1; +margin-bottom: -6px; +margin-top: 1px; +} + +.c-checkbox, .c-form { + font-size: 10px; + background-color: #ff7b73; + margin: 0; + display: grid; + height: 100vh; + place-items: center; +} +.c-checkbox { + display: none; +} +.c-checkbox:checked + .c-formContainer .c-form { + width: 27.5em; +} +.c-checkbox:checked + .c-formContainer .c-form__toggle { + visibility: hidden; + opacity: 0; + transform: scale(0.7); +} +.c-checkbox:checked + .c-formContainer .c-form__input, .c-checkbox:checked + .c-formContainer .c-form__buttonLabel { + transition: 0.2s 0.1s; + visibility: visible; + opacity: 1; + transform: scale(1); +} +.c-checkbox:not(:checked) + .c-formContainer .c-form__input:required:valid ~ .c-form__toggle::before, .c-checkbox:checked + .c-formContainer .c-form__input:required:valid ~ .c-form__toggle::before { + content: 'Thank You! \1F60A'; +} +.c-checkbox:not(:checked) + .c-formContainer .c-form__input:required:valid ~ .c-form__toggle { + pointer-events: none; + cursor: default; +} +.c-formContainer, .c-form, .c-form__toggle { + width: 15em; + height: 3.25em; +} +.c-formContainer { + position: relative; + font-weight: 700; + padding-top: 20px; +} +.c-form, .c-form__toggle { + position: absolute; + border-radius: 4.25em; + transition: 0.2s; +} +.c-form { + left: 50%; + transform: translateX(-50%); + padding: 0.425em; + box-sizing: border-box; + box-shadow: 0 0.125em 0.3125em rgba(0, 0, 0, 0.3); + display: flex; + gap: 5px; + justify-content: center; +} +.c-form__toggle { + color: #ff7b73; + top: 0; + cursor: pointer; + z-index: 1; + display: flex; + align-items: center; + justify-content: center;; +} +.c-form__toggle::before { + color: white; + content: attr(data-title); +} + +.c-form { + width: 160px; + font-size: 12px; + font-weight: 600; + color: #fff; + cursor: pointer; + text-align:center; + border: none; + background-size: 300% 100%; + border-radius: 12px; + -moz-transition: all .4s ease-in-out; + -o-transition: all .4s ease-in-out; + -webkit-transition: all .4s ease-in-out; + transition: all .4s ease-in-out; +} + +.c-form:hover { + background-position: 100% 0; + -moz-transition: all .4s ease-in-out; + -o-transition: all .4s ease-in-out; + -webkit-transition: all .4s ease-in-out; + transition: all .4s ease-in-out; +} + +.c-form:focus { + outline: none; +} + +.c-form { + background-image: linear-gradient( + to right, + #0ba360, + #3cba92, + #30dd8a, + #2bb673 + ); + box-shadow: 0 4px 15px 0 rgba(23, 168, 108, 0.75); +} + +.c-form__input, .c-form__button { + font: inherit; + border: 0; + outline: 0; + border-radius: 8px; + box-sizing: border-box; +} +.c-form__input, .c-form__buttonLabel { + opacity: 0; + visibility: hidden; + transform: scale(0.7); + transition: 0s; +} +.c-form__input { + color: rgb(83, 83, 83); + font-weight: 400; + height: 100%; + width: 100%; + padding: 0 0.714em; +} +.c-form__input::placeholder { + color: currentColor; +} +.c-form__input:required:valid { + color: #ff7b73; +} +.c-form__input:required:valid + .c-form__buttonLabel { + color: #fff; +} +.c-form__input:required:valid + .c-form__buttonLabel::before { + pointer-events: initial; +} +.c-form__buttonLabel { + color: #ffffff; + height: 100%; + width: auto; +} +.c-form__buttonLabel::before { + content: ''; + position: absolute; + width: 100%; + height: 100%; + pointer-events: none; + cursor: pointer; +} +.c-form__button { + color: inherit; + font-weight: 400; + font-size: 10px; + padding: 0; + height: 100%; + width: 5em; + cursor: pointer; + background-color: #5c74ff; } -.not{ - text-decoration: underline; -} \ No newline at end of file diff --git a/public/js/main.js b/public/js/main.js index b4cfee075..1f78fc122 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,17 +1,21 @@ -const deleteBtn = document.querySelectorAll('.del') -const todoItem = document.querySelectorAll('span.not') -const todoComplete = document.querySelectorAll('span.completed') +const deleteBtn = document.querySelectorAll('.fa-square-xmark') +const todoItem = document.querySelectorAll('span.not, span.completed') +const taskCheckbox = document.querySelectorAll('input[name=taskCheckbox]') +// Delete task Array.from(deleteBtn).forEach((el)=>{ el.addEventListener('click', deleteTodo) }) +// Change status (move right) Array.from(todoItem).forEach((el)=>{ - el.addEventListener('click', markComplete) + el.addEventListener('click', changeStatus) }) -Array.from(todoComplete).forEach((el)=>{ - el.addEventListener('click', markIncomplete) +// Mark Completed using checkbox +Array.from(taskCheckbox).forEach(el => { + let boxStatus = el.checked + el.addEventListener('click', boxStatus ? changeStatus : markComplete) }) async function deleteTodo(){ @@ -32,10 +36,10 @@ async function deleteTodo(){ } } -async function markComplete(){ +async function changeStatus(){ const todoId = this.parentNode.dataset.id try{ - const response = await fetch('todos/markComplete', { + const response = await fetch('todos/changeStatus', { method: 'put', headers: {'Content-type': 'application/json'}, body: JSON.stringify({ @@ -50,6 +54,25 @@ async function markComplete(){ } } +async function markComplete(){ + const todoId = this.parentNode.dataset.id + try{ + const response = await fetch('todos/markComplete', { + method: 'put', + headers: {'Content-type': 'application/json'}, + body: JSON.stringify({ + 'todoIdFromJSFile': todoId + }) + }) + const data = await response.json() + console.log(data) + location.reload() + + }catch(err){ + console.log(err) + } +} + async function markIncomplete(){ const todoId = this.parentNode.dataset.id try{ diff --git a/routes/todos.js b/routes/todos.js index 03dcf42e4..c974541e8 100644 --- a/routes/todos.js +++ b/routes/todos.js @@ -7,9 +7,9 @@ router.get('/', ensureAuth, todosController.getTodos) router.post('/createTodo', todosController.createTodo) -router.put('/markComplete', todosController.markComplete) +router.put('/changeStatus', todosController.changeStatus) -router.put('/markIncomplete', todosController.markIncomplete) +router.put('/markComplete', todosController.markComplete) router.delete('/deleteTodo', todosController.deleteTodo) diff --git a/views/index.ejs b/views/index.ejs index 774fc9b53..80c9f6fdd 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -1,14 +1,33 @@ + +
-