first commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/node_modules
|
||||||
|
.env
|
||||||
28
app.js
Normal file
28
app.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
const express = require('express')
|
||||||
|
const app = express()
|
||||||
|
const taskRouter = require('./routes/tasks')
|
||||||
|
const connectDB = require('./db/connect')
|
||||||
|
const dotenv = require('dotenv')
|
||||||
|
dotenv.config({path: './.env'})
|
||||||
|
|
||||||
|
app.use(express.json())
|
||||||
|
app.use(express.static('./public/'))
|
||||||
|
app.use('/api/v1/tasks', taskRouter)
|
||||||
|
|
||||||
|
app.get('*', (req, res) => {
|
||||||
|
console.log("Bad Request")
|
||||||
|
res.status(404).send("404 - Not Found " + req.url)
|
||||||
|
})
|
||||||
|
|
||||||
|
const start = async () => {
|
||||||
|
try {
|
||||||
|
await connectDB(process.env.MONGO_URI)
|
||||||
|
console.log("Database connected successfully")
|
||||||
|
app.listen(process.env.SERVER_PORT, () => console.log("Server listening on port:", process.env.SERVER_PORT))
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start()
|
||||||
|
|
||||||
12
controllers/createTask.js
Normal file
12
controllers/createTask.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const Task = require('../models/taskModel')
|
||||||
|
|
||||||
|
const createTask = async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const newTask = await Task.create({name: req.body.name, completed: false})
|
||||||
|
res.status(201).send("Success!")
|
||||||
|
} catch (error) {
|
||||||
|
res.status(400).json({msg: error.errors.name.message})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = createTask
|
||||||
14
controllers/deleteTask.js
Normal file
14
controllers/deleteTask.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const { findById } = require("../models/taskModel")
|
||||||
|
const taskModel = require("../models/taskModel")
|
||||||
|
|
||||||
|
const deleteTask = async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const task = await taskModel.findByIdAndDelete(req.params.id)
|
||||||
|
if (!task) return res.status(404).json({msg: `Null task with id ${req.params.id}`})
|
||||||
|
res.status(200).json({msg: "Success deletion"})
|
||||||
|
} catch (error) {
|
||||||
|
res.status(400).json({msg: error})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = deleteTask
|
||||||
16
controllers/editTask.js
Normal file
16
controllers/editTask.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
const taskModel = require("../models/taskModel")
|
||||||
|
|
||||||
|
const editTask = async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const { id: taskID } = req.params
|
||||||
|
const task = await taskModel.findOneAndUpdate({ _id: taskID }, req.body, {
|
||||||
|
new: true,
|
||||||
|
runValidators: true,
|
||||||
|
})
|
||||||
|
res.status(200).json({ task })
|
||||||
|
} catch (error) {
|
||||||
|
res.status(400).json({msg: error})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = editTask
|
||||||
14
controllers/getTask.js
Normal file
14
controllers/getTask.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const taskModel = require("../models/taskModel")
|
||||||
|
|
||||||
|
const getTask = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { id: taskID } = req.params
|
||||||
|
const task = await taskModel.findOne({ _id: taskID })
|
||||||
|
if (!task) return res.status(404).json({msg: `No task existing with id ${req.params.id}`})
|
||||||
|
res.status(200).json({task})
|
||||||
|
} catch (error) {
|
||||||
|
res.status(400).json({msg: error})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = getTask
|
||||||
12
controllers/getTasks.js
Normal file
12
controllers/getTasks.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const taskModel = require("../models/taskModel")
|
||||||
|
|
||||||
|
const getTasks = async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const tasks = await taskModel.find({})
|
||||||
|
res.status(200).json({tasks})
|
||||||
|
} catch (error) {
|
||||||
|
res.status(400).json({msg: error.errors.name.message})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = getTasks
|
||||||
11
db/connect.js
Normal file
11
db/connect.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
const mongoose = require('mongoose')
|
||||||
|
|
||||||
|
const connectDB = (url) => {
|
||||||
|
mongoose.connect(url,{
|
||||||
|
useNewUrlParser: true,
|
||||||
|
useUnifiedTopology: true,
|
||||||
|
useFindAndModify: true,
|
||||||
|
useCreateIndex: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = connectDB
|
||||||
16
models/taskModel.js
Normal file
16
models/taskModel.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
const mongoose = require('mongoose')
|
||||||
|
|
||||||
|
const taskSchema = new mongoose.Schema({
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: [true, "Must provide text!"],
|
||||||
|
trim: true,
|
||||||
|
maxlength: [50, "Not longer than 50 characters!"]
|
||||||
|
},
|
||||||
|
completed: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = mongoose.model('Task', taskSchema)
|
||||||
1263
package-lock.json
generated
Normal file
1263
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
package.json
Normal file
21
package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "jobs",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "app.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node app.js",
|
||||||
|
"dev": "nodemon app.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": "^8.2.0",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"mongoose": "^5.13.15"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"nodemon": "^2.0.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
87
public/browser-app.js
Normal file
87
public/browser-app.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
const tasksDOM = document.querySelector('.tasks')
|
||||||
|
const loadingDOM = document.querySelector('.loading-text')
|
||||||
|
const formDOM = document.querySelector('.task-form')
|
||||||
|
const taskInputDOM = document.querySelector('.task-input')
|
||||||
|
const formAlertDOM = document.querySelector('.form-alert')
|
||||||
|
// Load tasks from /api/tasks
|
||||||
|
const showTasks = async () => {
|
||||||
|
loadingDOM.style.visibility = 'visible'
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
data: { tasks },
|
||||||
|
} = await axios.get('/api/v1/tasks')
|
||||||
|
if (tasks.length < 1) {
|
||||||
|
tasksDOM.innerHTML = '<h5 class="empty-list">No tasks in your list</h5>'
|
||||||
|
loadingDOM.style.visibility = 'hidden'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const allTasks = tasks
|
||||||
|
.map((task) => {
|
||||||
|
const { completed, _id: taskID, name } = task
|
||||||
|
return `<div class="single-task ${completed && 'task-completed'}">
|
||||||
|
<h5><span><i class="far fa-check-circle"></i></span>${name}</h5>
|
||||||
|
<div class="task-links">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- edit link -->
|
||||||
|
<a href="task.html?id=${taskID}" class="edit-link">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</a>
|
||||||
|
<!-- delete btn -->
|
||||||
|
<button type="button" class="delete-btn" data-id="${taskID}">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
|
.join('')
|
||||||
|
tasksDOM.innerHTML = allTasks
|
||||||
|
} catch (error) {
|
||||||
|
tasksDOM.innerHTML =
|
||||||
|
'<h5 class="empty-list">There was an error, please try later....</h5>'
|
||||||
|
}
|
||||||
|
loadingDOM.style.visibility = 'hidden'
|
||||||
|
}
|
||||||
|
|
||||||
|
showTasks()
|
||||||
|
|
||||||
|
// delete task /api/tasks/:id
|
||||||
|
|
||||||
|
tasksDOM.addEventListener('click', async (e) => {
|
||||||
|
const el = e.target
|
||||||
|
if (el.parentElement.classList.contains('delete-btn')) {
|
||||||
|
loadingDOM.style.visibility = 'visible'
|
||||||
|
const id = el.parentElement.dataset.id
|
||||||
|
try {
|
||||||
|
await axios.delete(`/api/v1/tasks/${id}`)
|
||||||
|
showTasks()
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadingDOM.style.visibility = 'hidden'
|
||||||
|
})
|
||||||
|
|
||||||
|
// form
|
||||||
|
|
||||||
|
formDOM.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const name = taskInputDOM.value
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.post('/api/v1/tasks', { name })
|
||||||
|
showTasks()
|
||||||
|
taskInputDOM.value = ''
|
||||||
|
formAlertDOM.style.display = 'block'
|
||||||
|
formAlertDOM.textContent = `success, task added`
|
||||||
|
formAlertDOM.classList.add('text-success')
|
||||||
|
} catch (error) {
|
||||||
|
formAlertDOM.style.display = 'block'
|
||||||
|
formAlertDOM.innerHTML = `error, please try again`
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
formAlertDOM.style.display = 'none'
|
||||||
|
formAlertDOM.classList.remove('text-success')
|
||||||
|
}, 3000)
|
||||||
|
})
|
||||||
67
public/edit-task.js
Normal file
67
public/edit-task.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
const taskIDDOM = document.querySelector('.task-edit-id')
|
||||||
|
const taskNameDOM = document.querySelector('.task-edit-name')
|
||||||
|
const taskCompletedDOM = document.querySelector('.task-edit-completed')
|
||||||
|
const editFormDOM = document.querySelector('.single-task-form')
|
||||||
|
const editBtnDOM = document.querySelector('.task-edit-btn')
|
||||||
|
const formAlertDOM = document.querySelector('.form-alert')
|
||||||
|
const params = window.location.search
|
||||||
|
const id = new URLSearchParams(params).get('id')
|
||||||
|
let tempName
|
||||||
|
|
||||||
|
const showTask = async () => {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
data: { task },
|
||||||
|
} = await axios.get(`/api/v1/tasks/${id}`)
|
||||||
|
const { _id: taskID, completed, name } = task
|
||||||
|
|
||||||
|
taskIDDOM.textContent = taskID
|
||||||
|
taskNameDOM.value = name
|
||||||
|
tempName = name
|
||||||
|
if (completed) {
|
||||||
|
taskCompletedDOM.checked = true
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showTask()
|
||||||
|
|
||||||
|
editFormDOM.addEventListener('submit', async (e) => {
|
||||||
|
editBtnDOM.textContent = 'Loading...'
|
||||||
|
e.preventDefault()
|
||||||
|
try {
|
||||||
|
const taskName = taskNameDOM.value
|
||||||
|
const taskCompleted = taskCompletedDOM.checked
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: { task },
|
||||||
|
} = await axios.patch(`/api/v1/tasks/${id}`, {
|
||||||
|
name: taskName,
|
||||||
|
completed: taskCompleted,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { _id: taskID, completed, name } = task
|
||||||
|
|
||||||
|
taskIDDOM.textContent = taskID
|
||||||
|
taskNameDOM.value = name
|
||||||
|
tempName = name
|
||||||
|
if (completed) {
|
||||||
|
taskCompletedDOM.checked = true
|
||||||
|
}
|
||||||
|
formAlertDOM.style.display = 'block'
|
||||||
|
formAlertDOM.textContent = `success, edited task`
|
||||||
|
formAlertDOM.classList.add('text-success')
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
taskNameDOM.value = tempName
|
||||||
|
formAlertDOM.style.display = 'block'
|
||||||
|
formAlertDOM.innerHTML = `error, please try again`
|
||||||
|
}
|
||||||
|
editBtnDOM.textContent = 'Edit'
|
||||||
|
setTimeout(() => {
|
||||||
|
formAlertDOM.style.display = 'none'
|
||||||
|
formAlertDOM.classList.remove('text-success')
|
||||||
|
}, 3000)
|
||||||
|
})
|
||||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
47
public/index.html
Normal file
47
public/index.html
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<!-- favicon -->
|
||||||
|
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
|
||||||
|
<!-- font awesome -->
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css"
|
||||||
|
/>
|
||||||
|
<!-- normalize css -->
|
||||||
|
<link rel="stylesheet" href="./normalize.css" />
|
||||||
|
<!-- main css -->
|
||||||
|
<link rel="stylesheet" href="./main.css" />
|
||||||
|
<title>Task Manager</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form class="task-form">
|
||||||
|
<h4>task manager</h4>
|
||||||
|
<div class="form-control">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
class="task-input"
|
||||||
|
placeholder="e.g. wash dishes"
|
||||||
|
/>
|
||||||
|
<button type="submit" class="btn submit-btn">submit</button>
|
||||||
|
</div>
|
||||||
|
<div class="form-alert"></div>
|
||||||
|
</form>
|
||||||
|
<section class="tasks-container">
|
||||||
|
<p class="loading-text">Loading...</p>
|
||||||
|
<div class="tasks"></div>
|
||||||
|
</section>
|
||||||
|
<!-- axios -->
|
||||||
|
<script
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"
|
||||||
|
integrity="sha512-bZS47S7sPOxkjU/4Bt0zrhEtWx0y0CRkhEp8IckzK+ltifIIE9EMIMTuT/mEzoIMewUINruDBIR/jJnbguonqQ=="
|
||||||
|
crossorigin="anonymous"
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
></script>
|
||||||
|
<script src="./browser-app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
446
public/main.css
Normal file
446
public/main.css
Normal file
@@ -0,0 +1,446 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css?family=Montserrat:400|Inconsolata:400');
|
||||||
|
*,
|
||||||
|
::after,
|
||||||
|
::before {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
:root {
|
||||||
|
/* COLORS */
|
||||||
|
/* primary */
|
||||||
|
--primary-0: #f0efff;
|
||||||
|
--primary-50: #d8d6ff;
|
||||||
|
--primary-100: #c1beff;
|
||||||
|
--primary-200: #aaa5ff;
|
||||||
|
--primary-300: #938dff;
|
||||||
|
--primary-400: #7b74ff;
|
||||||
|
/* main */
|
||||||
|
--primary-500: #645cff;
|
||||||
|
--primary-600: #554ed9;
|
||||||
|
--primary-700: #4640b3;
|
||||||
|
--primary-800: #37338c;
|
||||||
|
--primary-900: #282566;
|
||||||
|
--primary-1000: #191740;
|
||||||
|
--primary-1100: #0a0919;
|
||||||
|
/* grey */
|
||||||
|
--grey-0: #fafbfc;
|
||||||
|
--grey-50: #f2f4f8;
|
||||||
|
--grey-100: #eaedf3;
|
||||||
|
--grey-200: #e2e7ef;
|
||||||
|
--grey-300: #dbe0ea;
|
||||||
|
--grey-400: #d3dae6;
|
||||||
|
--grey-500: #cbd3e1;
|
||||||
|
--grey-600: #adb3bf;
|
||||||
|
--grey-700: #8e949e;
|
||||||
|
--grey-800: #70747c;
|
||||||
|
--grey-900: #51545a;
|
||||||
|
--grey-1000: #333538;
|
||||||
|
--grey-1100: #141516;
|
||||||
|
|
||||||
|
/* rest */
|
||||||
|
--black: #222;
|
||||||
|
--white: #fff;
|
||||||
|
--red-light: #f8d7da;
|
||||||
|
--red-dark: #842029;
|
||||||
|
--green-light: #d1e7dd;
|
||||||
|
--green-dark: #0f5132;
|
||||||
|
|
||||||
|
/* typography */
|
||||||
|
--headingFont: 'Inconsolata', monospace;
|
||||||
|
--bodyFont: 'Montserrat', sans-serif;
|
||||||
|
--smallText: 0.7em;
|
||||||
|
/* rest */
|
||||||
|
--backgroundColor: var(--grey-50);
|
||||||
|
--textColor: var(--grey-1000);
|
||||||
|
--borderRadius: 0.25rem;
|
||||||
|
--letterSpacing: 1px;
|
||||||
|
--transition: 0.3s ease-in-out all;
|
||||||
|
--max-width: 1120px;
|
||||||
|
--fixed-width: 600px;
|
||||||
|
/* box shadows */
|
||||||
|
--shadow-1: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||||
|
--shadow-2: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
||||||
|
0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
--shadow-3: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
||||||
|
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
--shadow-4: 0 20px 25px -5px rgba(0, 0, 0, 0.1),
|
||||||
|
0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 112.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--backgroundColor);
|
||||||
|
color: var(--textColor);
|
||||||
|
font-family: var(--bodyFont);
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
max-width: 40em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5 {
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 1.38rem;
|
||||||
|
font-family: var(--headingFont);
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.3;
|
||||||
|
text-transform: capitalize;
|
||||||
|
letter-spacing: var(--letterSpacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3.052rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 2.441rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.953rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 1.563rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
small,
|
||||||
|
.text-small {
|
||||||
|
font-size: var(--smallText);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
/*************
|
||||||
|
Buttons
|
||||||
|
*************/
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
cursor: pointer;
|
||||||
|
appearance: none;
|
||||||
|
color: var(--white);
|
||||||
|
background: var(--primary-500);
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
letter-spacing: var(--letterSpacing);
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
box-shadow: var(--shadow-1);
|
||||||
|
transition: var(--transition);
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
color: var(--white);
|
||||||
|
background: var(--primary-700);
|
||||||
|
box-shadow: var(--shadow-2);
|
||||||
|
}
|
||||||
|
button.small {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: var(--smallText);
|
||||||
|
}
|
||||||
|
button.hipster {
|
||||||
|
color: var(--primary-500);
|
||||||
|
background: var(--primary-50);
|
||||||
|
box-shadow: var(--shadow-1);
|
||||||
|
}
|
||||||
|
button.hipster:hover {
|
||||||
|
background: var(--primary-100);
|
||||||
|
box-shadow: var(--shadow-2);
|
||||||
|
}
|
||||||
|
button.block {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************
|
||||||
|
FORM
|
||||||
|
*************/
|
||||||
|
form {
|
||||||
|
width: 100%;
|
||||||
|
background: var(--white);
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
box-shadow: var(--shadow-2);
|
||||||
|
padding: 2rem 2.5rem;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
font-size: var(--smallText);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
background: var(--grey-50);
|
||||||
|
border-color: transparent;
|
||||||
|
border: 1px solid var(--grey-300);
|
||||||
|
}
|
||||||
|
.form-row {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
::placeholder {
|
||||||
|
color: var(--grey-600);
|
||||||
|
}
|
||||||
|
.form-alert {
|
||||||
|
color: var(--red-dark);
|
||||||
|
letter-spacing: var(--letterSpacing);
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-form,
|
||||||
|
.single-task-form {
|
||||||
|
width: 90vw;
|
||||||
|
max-width: var(--fixed-width);
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-top: 6rem;
|
||||||
|
}
|
||||||
|
.task-form h4 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.task-form .form-alert {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
.submit-btn {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 576px) {
|
||||||
|
.form-control {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.task-input {
|
||||||
|
border-radius: 0;
|
||||||
|
border-top-left-radius: var(--borderRadius);
|
||||||
|
border-bottom-left-radius: var(--borderRadius);
|
||||||
|
}
|
||||||
|
.submit-btn {
|
||||||
|
margin-top: 0;
|
||||||
|
width: 175px;
|
||||||
|
border-radius: 0;
|
||||||
|
border-top-right-radius: var(--borderRadius);
|
||||||
|
border-bottom-right-radius: var(--borderRadius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form > :first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
form > :last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
/* Basic Classes */
|
||||||
|
.alert {
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-color: transparent;
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
color: var(--green-dark);
|
||||||
|
background-color: var(--green-light);
|
||||||
|
}
|
||||||
|
.alert-danger {
|
||||||
|
color: var(--red-dark);
|
||||||
|
background-color: var(--red-light);
|
||||||
|
}
|
||||||
|
.text-danger {
|
||||||
|
color: var(--red-dark);
|
||||||
|
}
|
||||||
|
.text-success {
|
||||||
|
color: var(--green-dark);
|
||||||
|
}
|
||||||
|
/* loading */
|
||||||
|
@keyframes spinner {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
width: 6rem;
|
||||||
|
height: 6rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 3px solid #ccc;
|
||||||
|
border-top-color: var(--clr-primary-5);
|
||||||
|
animation: spinner 0.6s linear infinite;
|
||||||
|
}
|
||||||
|
.loading {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
/* form loading */
|
||||||
|
|
||||||
|
.form-loading {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.form-loading::before {
|
||||||
|
background: var(--white);
|
||||||
|
position: absolute;
|
||||||
|
content: '';
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-loading::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: calc(50% - 3rem);
|
||||||
|
left: calc(50% - 3rem);
|
||||||
|
width: 6rem;
|
||||||
|
height: 6rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 3px solid #ccc;
|
||||||
|
border-top-color: var(--clr-primary-5);
|
||||||
|
animation: spinner 0.6s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************
|
||||||
|
TASKS
|
||||||
|
*************/
|
||||||
|
.empty-list {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.tasks-container {
|
||||||
|
width: 100%;
|
||||||
|
width: 90vw;
|
||||||
|
max-width: var(--fixed-width);
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding-bottom: 4rem;
|
||||||
|
}
|
||||||
|
.tasks {
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
.single-task {
|
||||||
|
padding: 1rem 2.5rem;
|
||||||
|
background: var(--white);
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
box-shadow: var(--shadow-2);
|
||||||
|
transition: var(--transition);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.single-task:hover {
|
||||||
|
box-shadow: var(--shadow-4);
|
||||||
|
}
|
||||||
|
.single-task h5 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.single-task span {
|
||||||
|
display: inline-block;
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
margin-left: -1.25rem;
|
||||||
|
margin-right: 1.5rem;
|
||||||
|
}
|
||||||
|
.single-task .fa-check-circle {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--green-dark);
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.task-completed h5 {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
.task-completed .fa-check-circle {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
.loading-text {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
/* Task Links */
|
||||||
|
.task-links {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0 0.5rem;
|
||||||
|
}
|
||||||
|
.delete-btn,
|
||||||
|
.edit-link {
|
||||||
|
background: transparent;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
border: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.edit-link {
|
||||||
|
color: var(--green-dark);
|
||||||
|
}
|
||||||
|
.delete-btn {
|
||||||
|
color: var(--red-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************
|
||||||
|
SINGLE TASK
|
||||||
|
*************/
|
||||||
|
|
||||||
|
.single-task-form h4 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-task-form p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-task-form .form-control {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 150px 1fr;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: start;
|
||||||
|
gap: 0 1rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-task-form label {
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-edit-completed {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
.back-link {
|
||||||
|
display: block;
|
||||||
|
width: 250px;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 6rem;
|
||||||
|
background: var(--black);
|
||||||
|
}
|
||||||
|
.back-link:hover {
|
||||||
|
background: var(--black);
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-task-form .form-alert {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
351
public/normalize.css
vendored
Normal file
351
public/normalize.css
vendored
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the `main` element consistently in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box; /* 1 */
|
||||||
|
height: 0; /* 1 */
|
||||||
|
overflow: visible; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the gray background on active links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57-
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none; /* 1 */
|
||||||
|
text-decoration: underline; /* 2 */
|
||||||
|
text-decoration: underline dotted; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers.
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit; /* 1 */
|
||||||
|
font-size: 100%; /* 1 */
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
margin: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input {
|
||||||
|
/* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
/* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type='button'],
|
||||||
|
[type='reset'],
|
||||||
|
[type='submit'] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type='button']::-moz-focus-inner,
|
||||||
|
[type='reset']::-moz-focus-inner,
|
||||||
|
[type='submit']::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type='button']:-moz-focusring,
|
||||||
|
[type='reset']:-moz-focusring,
|
||||||
|
[type='submit']:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
color: inherit; /* 2 */
|
||||||
|
display: table; /* 1 */
|
||||||
|
max-width: 100%; /* 1 */
|
||||||
|
padding: 0; /* 3 */
|
||||||
|
white-space: normal; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10.
|
||||||
|
* 2. Remove the padding in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type='checkbox'],
|
||||||
|
[type='radio'] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type='number']::-webkit-inner-spin-button,
|
||||||
|
[type='number']::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type='search'] {
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
outline-offset: -2px; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type='search']::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
details {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
47
public/task.html
Normal file
47
public/task.html
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<!-- favicon -->
|
||||||
|
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
|
||||||
|
<!-- normalize -->
|
||||||
|
<link rel="stylesheet" href="./normalize.css" />
|
||||||
|
<!-- main -->
|
||||||
|
<link rel="stylesheet" href="./main.css" />
|
||||||
|
|
||||||
|
<title>Edit Task</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<form class="single-task-form">
|
||||||
|
<h4>Edit Task</h4>
|
||||||
|
<div class="form-control">
|
||||||
|
<label>Task ID</label>
|
||||||
|
<p class="task-edit-id"></p>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<input type="text" name="name" class="task-edit-name" />
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label for="completed">completed</label>
|
||||||
|
<input type="checkbox" name="completed" class="task-edit-completed" />
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="block btn task-edit-btn">edit</button>
|
||||||
|
<div class="form-alert"></div>
|
||||||
|
</form>
|
||||||
|
<a href="index.html" class="btn back-link">back to tasks</a>
|
||||||
|
</div>
|
||||||
|
<!-- axios -->
|
||||||
|
<script
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"
|
||||||
|
integrity="sha512-bZS47S7sPOxkjU/4Bt0zrhEtWx0y0CRkhEp8IckzK+ltifIIE9EMIMTuT/mEzoIMewUINruDBIR/jJnbguonqQ=="
|
||||||
|
crossorigin="anonymous"
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
></script>
|
||||||
|
<!-- js -->
|
||||||
|
<script src="./edit-task.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
18
routes/tasks.js
Normal file
18
routes/tasks.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const express = require('express')
|
||||||
|
const router = express.Router()
|
||||||
|
const createTask = require('../controllers/createTask')
|
||||||
|
const getTask = require('../controllers/getTask')
|
||||||
|
const getTasks = require('../controllers/getTasks')
|
||||||
|
const editTask = require('../controllers/editTask')
|
||||||
|
const deleteTask = require('../controllers/deleteTask')
|
||||||
|
|
||||||
|
|
||||||
|
router.route('/').get(getTasks).post(createTask)
|
||||||
|
|
||||||
|
router.route('/:id')
|
||||||
|
.get(getTask)
|
||||||
|
.delete(deleteTask)
|
||||||
|
.patch(editTask)
|
||||||
|
|
||||||
|
module.exports = router
|
||||||
|
|
||||||
2
starter/.gitignore
vendored
Normal file
2
starter/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/node_modules
|
||||||
|
.env
|
||||||
1
starter/app.js
Normal file
1
starter/app.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
console.log('Task Manager App')
|
||||||
20
starter/package.json
Normal file
20
starter/package.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "jobs",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "app.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "nodemon app.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": "^8.2.0",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"mongoose": "^5.11.10"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"nodemon": "^2.0.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
87
starter/public/browser-app.js
Normal file
87
starter/public/browser-app.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
const tasksDOM = document.querySelector('.tasks')
|
||||||
|
const loadingDOM = document.querySelector('.loading-text')
|
||||||
|
const formDOM = document.querySelector('.task-form')
|
||||||
|
const taskInputDOM = document.querySelector('.task-input')
|
||||||
|
const formAlertDOM = document.querySelector('.form-alert')
|
||||||
|
// Load tasks from /api/tasks
|
||||||
|
const showTasks = async () => {
|
||||||
|
loadingDOM.style.visibility = 'visible'
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
data: { tasks },
|
||||||
|
} = await axios.get('/api/v1/tasks')
|
||||||
|
if (tasks.length < 1) {
|
||||||
|
tasksDOM.innerHTML = '<h5 class="empty-list">No tasks in your list</h5>'
|
||||||
|
loadingDOM.style.visibility = 'hidden'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const allTasks = tasks
|
||||||
|
.map((task) => {
|
||||||
|
const { completed, _id: taskID, name } = task
|
||||||
|
return `<div class="single-task ${completed && 'task-completed'}">
|
||||||
|
<h5><span><i class="far fa-check-circle"></i></span>${name}</h5>
|
||||||
|
<div class="task-links">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- edit link -->
|
||||||
|
<a href="task.html?id=${taskID}" class="edit-link">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</a>
|
||||||
|
<!-- delete btn -->
|
||||||
|
<button type="button" class="delete-btn" data-id="${taskID}">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
|
.join('')
|
||||||
|
tasksDOM.innerHTML = allTasks
|
||||||
|
} catch (error) {
|
||||||
|
tasksDOM.innerHTML =
|
||||||
|
'<h5 class="empty-list">There was an error, please try later....</h5>'
|
||||||
|
}
|
||||||
|
loadingDOM.style.visibility = 'hidden'
|
||||||
|
}
|
||||||
|
|
||||||
|
showTasks()
|
||||||
|
|
||||||
|
// delete task /api/tasks/:id
|
||||||
|
|
||||||
|
tasksDOM.addEventListener('click', async (e) => {
|
||||||
|
const el = e.target
|
||||||
|
if (el.parentElement.classList.contains('delete-btn')) {
|
||||||
|
loadingDOM.style.visibility = 'visible'
|
||||||
|
const id = el.parentElement.dataset.id
|
||||||
|
try {
|
||||||
|
await axios.delete(`/api/v1/tasks/${id}`)
|
||||||
|
showTasks()
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadingDOM.style.visibility = 'hidden'
|
||||||
|
})
|
||||||
|
|
||||||
|
// form
|
||||||
|
|
||||||
|
formDOM.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const name = taskInputDOM.value
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.post('/api/v1/tasks', { name })
|
||||||
|
showTasks()
|
||||||
|
taskInputDOM.value = ''
|
||||||
|
formAlertDOM.style.display = 'block'
|
||||||
|
formAlertDOM.textContent = `success, task added`
|
||||||
|
formAlertDOM.classList.add('text-success')
|
||||||
|
} catch (error) {
|
||||||
|
formAlertDOM.style.display = 'block'
|
||||||
|
formAlertDOM.innerHTML = `error, please try again`
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
formAlertDOM.style.display = 'none'
|
||||||
|
formAlertDOM.classList.remove('text-success')
|
||||||
|
}, 3000)
|
||||||
|
})
|
||||||
67
starter/public/edit-task.js
Normal file
67
starter/public/edit-task.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
const taskIDDOM = document.querySelector('.task-edit-id')
|
||||||
|
const taskNameDOM = document.querySelector('.task-edit-name')
|
||||||
|
const taskCompletedDOM = document.querySelector('.task-edit-completed')
|
||||||
|
const editFormDOM = document.querySelector('.single-task-form')
|
||||||
|
const editBtnDOM = document.querySelector('.task-edit-btn')
|
||||||
|
const formAlertDOM = document.querySelector('.form-alert')
|
||||||
|
const params = window.location.search
|
||||||
|
const id = new URLSearchParams(params).get('id')
|
||||||
|
let tempName
|
||||||
|
|
||||||
|
const showTask = async () => {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
data: { task },
|
||||||
|
} = await axios.get(`/api/v1/tasks/${id}`)
|
||||||
|
const { _id: taskID, completed, name } = task
|
||||||
|
|
||||||
|
taskIDDOM.textContent = taskID
|
||||||
|
taskNameDOM.value = name
|
||||||
|
tempName = name
|
||||||
|
if (completed) {
|
||||||
|
taskCompletedDOM.checked = true
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showTask()
|
||||||
|
|
||||||
|
editFormDOM.addEventListener('submit', async (e) => {
|
||||||
|
editBtnDOM.textContent = 'Loading...'
|
||||||
|
e.preventDefault()
|
||||||
|
try {
|
||||||
|
const taskName = taskNameDOM.value
|
||||||
|
const taskCompleted = taskCompletedDOM.checked
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: { task },
|
||||||
|
} = await axios.patch(`/api/v1/tasks/${id}`, {
|
||||||
|
name: taskName,
|
||||||
|
completed: taskCompleted,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { _id: taskID, completed, name } = task
|
||||||
|
|
||||||
|
taskIDDOM.textContent = taskID
|
||||||
|
taskNameDOM.value = name
|
||||||
|
tempName = name
|
||||||
|
if (completed) {
|
||||||
|
taskCompletedDOM.checked = true
|
||||||
|
}
|
||||||
|
formAlertDOM.style.display = 'block'
|
||||||
|
formAlertDOM.textContent = `success, edited task`
|
||||||
|
formAlertDOM.classList.add('text-success')
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
taskNameDOM.value = tempName
|
||||||
|
formAlertDOM.style.display = 'block'
|
||||||
|
formAlertDOM.innerHTML = `error, please try again`
|
||||||
|
}
|
||||||
|
editBtnDOM.textContent = 'Edit'
|
||||||
|
setTimeout(() => {
|
||||||
|
formAlertDOM.style.display = 'none'
|
||||||
|
formAlertDOM.classList.remove('text-success')
|
||||||
|
}, 3000)
|
||||||
|
})
|
||||||
BIN
starter/public/favicon.ico
Normal file
BIN
starter/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
47
starter/public/index.html
Normal file
47
starter/public/index.html
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<!-- favicon -->
|
||||||
|
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
|
||||||
|
<!-- font awesome -->
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css"
|
||||||
|
/>
|
||||||
|
<!-- normalize css -->
|
||||||
|
<link rel="stylesheet" href="./normalize.css" />
|
||||||
|
<!-- main css -->
|
||||||
|
<link rel="stylesheet" href="./main.css" />
|
||||||
|
<title>Task Manager</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form class="task-form">
|
||||||
|
<h4>task manager</h4>
|
||||||
|
<div class="form-control">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
class="task-input"
|
||||||
|
placeholder="e.g. wash dishes"
|
||||||
|
/>
|
||||||
|
<button type="submit" class="btn submit-btn">submit</button>
|
||||||
|
</div>
|
||||||
|
<div class="form-alert"></div>
|
||||||
|
</form>
|
||||||
|
<section class="tasks-container">
|
||||||
|
<p class="loading-text">Loading...</p>
|
||||||
|
<div class="tasks"></div>
|
||||||
|
</section>
|
||||||
|
<!-- axios -->
|
||||||
|
<script
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"
|
||||||
|
integrity="sha512-bZS47S7sPOxkjU/4Bt0zrhEtWx0y0CRkhEp8IckzK+ltifIIE9EMIMTuT/mEzoIMewUINruDBIR/jJnbguonqQ=="
|
||||||
|
crossorigin="anonymous"
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
></script>
|
||||||
|
<script src="./browser-app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
446
starter/public/main.css
Normal file
446
starter/public/main.css
Normal file
@@ -0,0 +1,446 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css?family=Montserrat:400|Inconsolata:400');
|
||||||
|
*,
|
||||||
|
::after,
|
||||||
|
::before {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
:root {
|
||||||
|
/* COLORS */
|
||||||
|
/* primary */
|
||||||
|
--primary-0: #f0efff;
|
||||||
|
--primary-50: #d8d6ff;
|
||||||
|
--primary-100: #c1beff;
|
||||||
|
--primary-200: #aaa5ff;
|
||||||
|
--primary-300: #938dff;
|
||||||
|
--primary-400: #7b74ff;
|
||||||
|
/* main */
|
||||||
|
--primary-500: #645cff;
|
||||||
|
--primary-600: #554ed9;
|
||||||
|
--primary-700: #4640b3;
|
||||||
|
--primary-800: #37338c;
|
||||||
|
--primary-900: #282566;
|
||||||
|
--primary-1000: #191740;
|
||||||
|
--primary-1100: #0a0919;
|
||||||
|
/* grey */
|
||||||
|
--grey-0: #fafbfc;
|
||||||
|
--grey-50: #f2f4f8;
|
||||||
|
--grey-100: #eaedf3;
|
||||||
|
--grey-200: #e2e7ef;
|
||||||
|
--grey-300: #dbe0ea;
|
||||||
|
--grey-400: #d3dae6;
|
||||||
|
--grey-500: #cbd3e1;
|
||||||
|
--grey-600: #adb3bf;
|
||||||
|
--grey-700: #8e949e;
|
||||||
|
--grey-800: #70747c;
|
||||||
|
--grey-900: #51545a;
|
||||||
|
--grey-1000: #333538;
|
||||||
|
--grey-1100: #141516;
|
||||||
|
|
||||||
|
/* rest */
|
||||||
|
--black: #222;
|
||||||
|
--white: #fff;
|
||||||
|
--red-light: #f8d7da;
|
||||||
|
--red-dark: #842029;
|
||||||
|
--green-light: #d1e7dd;
|
||||||
|
--green-dark: #0f5132;
|
||||||
|
|
||||||
|
/* typography */
|
||||||
|
--headingFont: 'Inconsolata', monospace;
|
||||||
|
--bodyFont: 'Montserrat', sans-serif;
|
||||||
|
--smallText: 0.7em;
|
||||||
|
/* rest */
|
||||||
|
--backgroundColor: var(--grey-50);
|
||||||
|
--textColor: var(--grey-1000);
|
||||||
|
--borderRadius: 0.25rem;
|
||||||
|
--letterSpacing: 1px;
|
||||||
|
--transition: 0.3s ease-in-out all;
|
||||||
|
--max-width: 1120px;
|
||||||
|
--fixed-width: 600px;
|
||||||
|
/* box shadows */
|
||||||
|
--shadow-1: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||||
|
--shadow-2: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
||||||
|
0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
--shadow-3: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
||||||
|
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
--shadow-4: 0 20px 25px -5px rgba(0, 0, 0, 0.1),
|
||||||
|
0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 112.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--backgroundColor);
|
||||||
|
color: var(--textColor);
|
||||||
|
font-family: var(--bodyFont);
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
max-width: 40em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5 {
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 1.38rem;
|
||||||
|
font-family: var(--headingFont);
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.3;
|
||||||
|
text-transform: capitalize;
|
||||||
|
letter-spacing: var(--letterSpacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3.052rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 2.441rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.953rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 1.563rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
small,
|
||||||
|
.text-small {
|
||||||
|
font-size: var(--smallText);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
/*************
|
||||||
|
Buttons
|
||||||
|
*************/
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
cursor: pointer;
|
||||||
|
appearance: none;
|
||||||
|
color: var(--white);
|
||||||
|
background: var(--primary-500);
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
letter-spacing: var(--letterSpacing);
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
box-shadow: var(--shadow-1);
|
||||||
|
transition: var(--transition);
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
color: var(--white);
|
||||||
|
background: var(--primary-700);
|
||||||
|
box-shadow: var(--shadow-2);
|
||||||
|
}
|
||||||
|
button.small {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: var(--smallText);
|
||||||
|
}
|
||||||
|
button.hipster {
|
||||||
|
color: var(--primary-500);
|
||||||
|
background: var(--primary-50);
|
||||||
|
box-shadow: var(--shadow-1);
|
||||||
|
}
|
||||||
|
button.hipster:hover {
|
||||||
|
background: var(--primary-100);
|
||||||
|
box-shadow: var(--shadow-2);
|
||||||
|
}
|
||||||
|
button.block {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************
|
||||||
|
FORM
|
||||||
|
*************/
|
||||||
|
form {
|
||||||
|
width: 100%;
|
||||||
|
background: var(--white);
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
box-shadow: var(--shadow-2);
|
||||||
|
padding: 2rem 2.5rem;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
font-size: var(--smallText);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
background: var(--grey-50);
|
||||||
|
border-color: transparent;
|
||||||
|
border: 1px solid var(--grey-300);
|
||||||
|
}
|
||||||
|
.form-row {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
::placeholder {
|
||||||
|
color: var(--grey-600);
|
||||||
|
}
|
||||||
|
.form-alert {
|
||||||
|
color: var(--red-dark);
|
||||||
|
letter-spacing: var(--letterSpacing);
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-form,
|
||||||
|
.single-task-form {
|
||||||
|
width: 90vw;
|
||||||
|
max-width: var(--fixed-width);
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-top: 6rem;
|
||||||
|
}
|
||||||
|
.task-form h4 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.task-form .form-alert {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
.submit-btn {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 576px) {
|
||||||
|
.form-control {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.task-input {
|
||||||
|
border-radius: 0;
|
||||||
|
border-top-left-radius: var(--borderRadius);
|
||||||
|
border-bottom-left-radius: var(--borderRadius);
|
||||||
|
}
|
||||||
|
.submit-btn {
|
||||||
|
margin-top: 0;
|
||||||
|
width: 175px;
|
||||||
|
border-radius: 0;
|
||||||
|
border-top-right-radius: var(--borderRadius);
|
||||||
|
border-bottom-right-radius: var(--borderRadius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form > :first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
form > :last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
/* Basic Classes */
|
||||||
|
.alert {
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-color: transparent;
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
color: var(--green-dark);
|
||||||
|
background-color: var(--green-light);
|
||||||
|
}
|
||||||
|
.alert-danger {
|
||||||
|
color: var(--red-dark);
|
||||||
|
background-color: var(--red-light);
|
||||||
|
}
|
||||||
|
.text-danger {
|
||||||
|
color: var(--red-dark);
|
||||||
|
}
|
||||||
|
.text-success {
|
||||||
|
color: var(--green-dark);
|
||||||
|
}
|
||||||
|
/* loading */
|
||||||
|
@keyframes spinner {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
width: 6rem;
|
||||||
|
height: 6rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 3px solid #ccc;
|
||||||
|
border-top-color: var(--clr-primary-5);
|
||||||
|
animation: spinner 0.6s linear infinite;
|
||||||
|
}
|
||||||
|
.loading {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
/* form loading */
|
||||||
|
|
||||||
|
.form-loading {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.form-loading::before {
|
||||||
|
background: var(--white);
|
||||||
|
position: absolute;
|
||||||
|
content: '';
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-loading::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: calc(50% - 3rem);
|
||||||
|
left: calc(50% - 3rem);
|
||||||
|
width: 6rem;
|
||||||
|
height: 6rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 3px solid #ccc;
|
||||||
|
border-top-color: var(--clr-primary-5);
|
||||||
|
animation: spinner 0.6s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************
|
||||||
|
TASKS
|
||||||
|
*************/
|
||||||
|
.empty-list {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.tasks-container {
|
||||||
|
width: 100%;
|
||||||
|
width: 90vw;
|
||||||
|
max-width: var(--fixed-width);
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding-bottom: 4rem;
|
||||||
|
}
|
||||||
|
.tasks {
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
.single-task {
|
||||||
|
padding: 1rem 2.5rem;
|
||||||
|
background: var(--white);
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
box-shadow: var(--shadow-2);
|
||||||
|
transition: var(--transition);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.single-task:hover {
|
||||||
|
box-shadow: var(--shadow-4);
|
||||||
|
}
|
||||||
|
.single-task h5 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.single-task span {
|
||||||
|
display: inline-block;
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
margin-left: -1.25rem;
|
||||||
|
margin-right: 1.5rem;
|
||||||
|
}
|
||||||
|
.single-task .fa-check-circle {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--green-dark);
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.task-completed h5 {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
.task-completed .fa-check-circle {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
.loading-text {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
/* Task Links */
|
||||||
|
.task-links {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0 0.5rem;
|
||||||
|
}
|
||||||
|
.delete-btn,
|
||||||
|
.edit-link {
|
||||||
|
background: transparent;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
border: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.edit-link {
|
||||||
|
color: var(--green-dark);
|
||||||
|
}
|
||||||
|
.delete-btn {
|
||||||
|
color: var(--red-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************
|
||||||
|
SINGLE TASK
|
||||||
|
*************/
|
||||||
|
|
||||||
|
.single-task-form h4 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-task-form p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-task-form .form-control {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 150px 1fr;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: start;
|
||||||
|
gap: 0 1rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-task-form label {
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-edit-completed {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
.back-link {
|
||||||
|
display: block;
|
||||||
|
width: 250px;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 6rem;
|
||||||
|
background: var(--black);
|
||||||
|
}
|
||||||
|
.back-link:hover {
|
||||||
|
background: var(--black);
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-task-form .form-alert {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
351
starter/public/normalize.css
vendored
Normal file
351
starter/public/normalize.css
vendored
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the `main` element consistently in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box; /* 1 */
|
||||||
|
height: 0; /* 1 */
|
||||||
|
overflow: visible; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the gray background on active links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57-
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none; /* 1 */
|
||||||
|
text-decoration: underline; /* 2 */
|
||||||
|
text-decoration: underline dotted; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers.
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit; /* 1 */
|
||||||
|
font-size: 100%; /* 1 */
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
margin: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input {
|
||||||
|
/* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
/* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type='button'],
|
||||||
|
[type='reset'],
|
||||||
|
[type='submit'] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type='button']::-moz-focus-inner,
|
||||||
|
[type='reset']::-moz-focus-inner,
|
||||||
|
[type='submit']::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type='button']:-moz-focusring,
|
||||||
|
[type='reset']:-moz-focusring,
|
||||||
|
[type='submit']:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
color: inherit; /* 2 */
|
||||||
|
display: table; /* 1 */
|
||||||
|
max-width: 100%; /* 1 */
|
||||||
|
padding: 0; /* 3 */
|
||||||
|
white-space: normal; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10.
|
||||||
|
* 2. Remove the padding in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type='checkbox'],
|
||||||
|
[type='radio'] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type='number']::-webkit-inner-spin-button,
|
||||||
|
[type='number']::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type='search'] {
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
outline-offset: -2px; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type='search']::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
details {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
47
starter/public/task.html
Normal file
47
starter/public/task.html
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<!-- favicon -->
|
||||||
|
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
|
||||||
|
<!-- normalize -->
|
||||||
|
<link rel="stylesheet" href="./normalize.css" />
|
||||||
|
<!-- main -->
|
||||||
|
<link rel="stylesheet" href="./main.css" />
|
||||||
|
|
||||||
|
<title>Edit Task</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<form class="single-task-form">
|
||||||
|
<h4>Edit Task</h4>
|
||||||
|
<div class="form-control">
|
||||||
|
<label>Task ID</label>
|
||||||
|
<p class="task-edit-id"></p>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<input type="text" name="name" class="task-edit-name" />
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label for="completed">completed</label>
|
||||||
|
<input type="checkbox" name="completed" class="task-edit-completed" />
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="block btn task-edit-btn">edit</button>
|
||||||
|
<div class="form-alert"></div>
|
||||||
|
</form>
|
||||||
|
<a href="index.html" class="btn back-link">back to tasks</a>
|
||||||
|
</div>
|
||||||
|
<!-- axios -->
|
||||||
|
<script
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"
|
||||||
|
integrity="sha512-bZS47S7sPOxkjU/4Bt0zrhEtWx0y0CRkhEp8IckzK+ltifIIE9EMIMTuT/mEzoIMewUINruDBIR/jJnbguonqQ=="
|
||||||
|
crossorigin="anonymous"
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
></script>
|
||||||
|
<!-- js -->
|
||||||
|
<script src="./edit-task.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user