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