Compare commits

..

5 Commits

Author SHA1 Message Date
abd3458606 stuff 2026-05-07 16:40:27 +08:00
8841b571c9 update page title to use app name 2026-05-05 17:22:37 +08:00
cf2df504b1 add task delete modal and alert 2026-05-05 17:00:42 +08:00
584ef9cbb7 changes 2026-05-05 16:40:41 +08:00
597df4e886 Enhance API documentation and error handling in task routes 2026-05-05 16:40:41 +08:00
12 changed files with 171 additions and 102 deletions

BIN
Documentation.docx Normal file

Binary file not shown.

BIN
Documentation.pdf Normal file

Binary file not shown.

View File

@@ -41,124 +41,141 @@ taskSchema.index({ dateCreated: 1 })
const Task = mongoose.model("Task", taskSchema); const Task = mongoose.model("Task", taskSchema);
// this api route gets all the tasks from our database.
// if the request included a query string, use that for our sorting.
app.get('/api/tasks', async (req, res) => { app.get('/api/tasks', async (req, res) => {
try { try {
const { sortBy } = req.query; // ?sortBy=dueDate or ?sortBy=dateCreated const { sortBy } = req.query; // destructs the sortBy from the query string. e.g. ?sortBy=dueDate or ?sortBy=dateCreated
let sortOption = {}; let sortOption = {}; // create an empty sort object for the database query
if (sortBy === 'dueDate') { if (sortBy === 'dueDate') {
sortOption = { dueDate: 1 }; // Ascending sortOption = { dueDate: 1 }; // if the sortBy query string is dueDate, set the sortOption to dueDate and set the order to ascending (1)
} else if (sortBy === 'dateCreated') { } else if (sortBy === 'dateCreated') {
sortOption = { dateCreated: 1 }; sortOption = { dateCreated: 1 }; // if the sortBy query string is dateCreated, set the sortOption to dateCreated and set the order to ascending (1)
} }
const tasks = await Task.find({}).sort(sortOption); const tasks = await Task.find({}).sort(sortOption); // fetch all task documents and sort them using sortOption
if (!tasks) { if (!tasks) {
// return the following if the query failed to return a value
return res.status(404).json({ message: "Tasks not found!" }); return res.status(404).json({ message: "Tasks not found!" });
} }
res.json(tasks); res.json(tasks); // send a json response with the task objects
} catch (error) { } catch (error) {
// if there are any errors, do the following
console.error("Error:", error); console.error("Error:", error);
res.status(500).json({ message: "Error grabbing tasks!" }); res.status(500).json({ message: "Error grabbing tasks!" });
} }
}); });
// this api route takes post requests to save a new MongoDB document, aka, our task
app.post('/api/tasks/todo', async (req, res) => { app.post('/api/tasks/todo', async (req, res) => {
try { try {
const { title, description, dueDate } = req.body; const { title, description, dueDate } = req.body; //destructures the request body
const taskData = { title, description, dueDate }; const taskData = { title, description, dueDate }; // creating the object to be used for creating the task
const createTask = new Task(taskData); const createTask = new Task(taskData); // instantiates a new Task document/model instance using the taskData object values
const newTask = await createTask.save(); const newTask = await createTask.save(); // this saves the new document to MongoDB using the Task model/schema.
res.json({ task: newTask, message: "New task created successfully!" }); res.json({ task: newTask, message: "New task created successfully!" }); // if there were no error above, send a response where the body is an JSON object.
} catch (error) { } catch (error) {
// if there are any errors, do the following
console.error("Error:", error); console.error("Error:", error);
res.status(500).json({ message: "Error creating the task!" }); res.status(500).json({ message: "Error creating the task!" });
} }
}); });
// To 'complete' the task and move columns ↓ // this api route marks a task as complete using its id.
app.patch('/api/tasks/complete/:id', async (req, res) => { app.patch('/api/tasks/complete/:id', async (req, res) => {
try { try {
const { completed } = req.body; const { completed } = req.body; // expected boolean value coming from the frontend
const taskId = req.params.id; const taskId = req.params.id; // grab the task id from the URL params
const completedTask = await Task.findByIdAndUpdate(taskId, { completed }, { new: true }); const completedTask = await Task.findByIdAndUpdate(taskId, { completed }, { new: true }); // update completed status and return updated document
if (!completedTask) { if (!completedTask) {
// return 404 if no task matches the provided id
return res.status(404).json({ message: "Task not found!" }); return res.status(404).json({ message: "Task not found!" });
} }
// return updated task document and confirmation message
res.json({ task: completedTask, message: "Task set to 'Complete'" }); res.json({ task: completedTask, message: "Task set to 'Complete'" });
} catch (error) { } catch (error) {
// return 500 if database update fails
console.error("Error:", error); console.error("Error:", error);
res.status(500).json({ message: "Error completing the task!" }); res.status(500).json({ message: "Error completing the task!" });
} }
}); });
// To make the task 'not complete' and move columns ↓ // this api route marks a task as not complete using its id.
app.patch('/api/tasks/notComplete/:id', async (req, res) => { app.patch('/api/tasks/notComplete/:id', async (req, res) => {
try { try {
const { completed } = req.body; const { completed } = req.body; // expected boolean value coming from the frontend
const taskId = req.params.id; const taskId = req.params.id; // grab the task id from the URL params
const taskNotComplete = await Task.findByIdAndUpdate(taskId, { completed }, { new: true }); const taskNotComplete = await Task.findByIdAndUpdate(taskId, { completed }, { new: true }); // update completed status and return updated document
if (!taskNotComplete) { if (!taskNotComplete) {
// return 404 if no task matches the provided id
return res.status(404).json({ message: "Task not found!" }); return res.status(404).json({ message: "Task not found!" });
} }
// return updated task document and confirmation message
res.json({ task: taskNotComplete, message: "Task set to 'Not Complete'" }); res.json({ task: taskNotComplete, message: "Task set to 'Not Complete'" });
} catch (error) { } catch (error) {
// return 500 if database update fails
console.error("Error:", error); console.error("Error:", error);
res.status(500).json({ message: "Error making the task NOT complete!" }); res.status(500).json({ message: "Error making the task NOT complete!" });
} }
}); });
// this api route deletes a task by id.
app.delete('/api/tasks/delete/:id', async (req, res) => { app.delete('/api/tasks/delete/:id', async (req, res) => {
try { try {
const taskId = req.params.id; const taskId = req.params.id; // grab the task id from the URL params
const deletedTask = await Task.findByIdAndDelete(taskId); const deletedTask = await Task.findByIdAndDelete(taskId); // delete the matching task document
if (!deletedTask) { if (!deletedTask) {
// return 404 if no task matches the provided id
return res.status(404).json({ message: "Task not found!" }); return res.status(404).json({ message: "Task not found!" });
} }
// return deleted task document and confirmation message
res.json({ task: deletedTask, message: "Task deleted successfully!" }); res.json({ task: deletedTask, message: "Task deleted successfully!" });
} catch (error) { } catch (error) {
// return 500 if deletion fails
console.error("Error:", error); console.error("Error:", error);
res.status(500).json({ message: "Error deleting the task!" }); res.status(500).json({ message: "Error deleting the task!" });
} }
}); });
// To edit exisiting task and update // this api route updates a task's editable fields by id.
app.put('/api/tasks/update/:id', async (req, res) => { app.put('/api/tasks/update/:id', async (req, res) => {
try { try {
const taskId = req.params.id; const taskId = req.params.id; // grab the task id from the URL params
const { title, description, dueDate } = req.body; // Extract data from front end request const { title, description, dueDate } = req.body; // extract updated fields from request body
const taskData = { title, description, dueDate }; const taskData = { title, description, dueDate }; // build the update object
const updatedTask = await Task.findByIdAndUpdate(taskId, taskData, { new: true }); const updatedTask = await Task.findByIdAndUpdate(taskId, taskData, { new: true }); // apply update and return updated document
if (!updatedTask) { if (!updatedTask) {
// return 404 if no task matches the provided id
return res.status(404).json({ message: "Task not found!" }); return res.status(404).json({ message: "Task not found!" });
} }
// return updated task document and confirmation message
res.json({ task: updatedTask, message: "Task updated successfully!" }); res.json({ task: updatedTask, message: "Task updated successfully!" });
} catch (error) { } catch (error) {
// return 500 if update fails
console.error('Error:', error); console.error('Error:', error);
res.status(500).json({ message: "Error updating the task!" }); res.status(500).json({ message: "Error updating the task!" });

View File

@@ -57,6 +57,10 @@ footer {
color: var(--accent); color: var(--accent);
} }
.navbar-nav .nav-link.active {
color: white;
}
.navbar-toggler { .navbar-toggler {
border: none !important; border: none !important;
box-shadow: none !important; box-shadow: none !important;

View File

@@ -4,7 +4,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>To Do App | Dashboard</title> <title>BusketLisk &#x2022; Dashboard</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" /> integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" />
<link rel="stylesheet" href="./css/styles.css" /> <link rel="stylesheet" href="./css/styles.css" />
@@ -92,7 +92,8 @@
<div class="d-flex gap-2 flex-wrap mb-3"> <div class="d-flex gap-2 flex-wrap mb-3">
<button type="button" class="btn btn-outline-light quick-date-btn" data-quick="today">Today</button> <button type="button" class="btn btn-outline-light quick-date-btn" data-quick="today">Today</button>
<button type="button" class="btn btn-outline-light quick-date-btn" data-quick="tomorrow">Tomorrow</button> <button type="button" class="btn btn-outline-light quick-date-btn" data-quick="tomorrow">Tomorrow</button>
<button type="button" class="btn btn-outline-light quick-date-btn" data-quick="nextWeek">Next week</button> <button type="button" class="btn btn-outline-light quick-date-btn" data-quick="nextWeek">Next
week</button>
</div> </div>
<button type="submit" class="btn btn-primary shadow-sm px-5"> <button type="submit" class="btn btn-primary shadow-sm px-5">
Create Task Create Task
@@ -156,6 +157,28 @@
</div> </div>
</div> </div>
</footer> </footer>
<!-- Toast-style alert container, populated by showAlert() in api.js -->
<div id="alertContainer" class="position-fixed top-0 end-0 p-3" style="z-index: 1080;"></div>
<!-- Delete confirmation modal -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteTaskModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="deleteTaskModalLabel">Delete Task</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p class="mb-0">Are you sure you want to delete this task? This action cannot be undone.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button id="confirmDeleteButton" type="button" class="btn btn-danger">Delete</button>
</div>
</div>
</div>
</div>
<!-- Modal --> <!-- Modal -->
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editTaskModalLabel" aria-hidden="true"> <div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editTaskModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered"> <div class="modal-dialog modal-dialog-centered">
@@ -167,7 +190,8 @@
<div class="modal-body"> <div class="modal-body">
<form id="editTaskForm"> <form id="editTaskForm">
<input id="editTaskName" placeholder="Task Name" class="mb-2 form-control shadow-sm" type="text"> <input id="editTaskName" placeholder="Task Name" class="mb-2 form-control shadow-sm" type="text">
<textarea id="editTaskDescription" placeholder="Task Description" rows="7" class="form-control mb-2 shadow-sm"></textarea> <textarea id="editTaskDescription" placeholder="Task Description" rows="7"
class="form-control mb-2 shadow-sm"></textarea>
<input id="editDueDate" placeholder="Task Name" class="mb-3 form-control shadow-sm" type="date"> <input id="editDueDate" placeholder="Task Name" class="mb-3 form-control shadow-sm" type="date">
</form> </form>
</div> </div>

View File

@@ -4,7 +4,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>To Do App | Features</title> <title>BusketLisk &#x2022; Features</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" /> integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" />
<link rel="stylesheet" href="./css/styles.css" /> <link rel="stylesheet" href="./css/styles.css" />

View File

@@ -4,7 +4,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>To Do App | Home</title> <title>BusketLisk &#x2022; Home</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" /> integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" />
<link rel="stylesheet" href="./css/styles.css" /> <link rel="stylesheet" href="./css/styles.css" />
@@ -50,7 +50,8 @@
<section class="d-flex justify-content-center flex-column align-items-center bg-img text-center px-3"> <section class="d-flex justify-content-center flex-column align-items-center bg-img text-center px-3">
<h1 class="display-2 text-white">Stay organised with BucketLisk</h1> <h1 class="display-2 text-white">Stay organised with BucketLisk</h1>
<p class="lead text-white-50 col-12 col-md-8 col-lg-6 mt-3"> <p class="lead text-white-50 col-12 col-md-8 col-lg-6 mt-3">
The simple, no-nonsense to-do app that keeps your team on track, from morning stand-up to end-of-quarter shipping. The simple, no-nonsense to-do app that keeps your team on track, from morning stand-up to end-of-quarter
shipping.
</p> </p>
<div class="d-flex justify-content-center align-items-center gap-3 mt-4 flex-wrap"> <div class="d-flex justify-content-center align-items-center gap-3 mt-4 flex-wrap">
<a href="./dashboard.html" class="btn btn-primary btn-lg px-5">Try it for free</a> <a href="./dashboard.html" class="btn btn-primary btn-lg px-5">Try it for free</a>
@@ -102,9 +103,11 @@
When you're done, tick it off and move it to the completed column to keep the momentum going. When you're done, tick it off and move it to the completed column to keep the momentum going.
</p> </p>
<ul class="list-unstyled"> <ul class="list-unstyled">
<li class="mb-2"><img src="./images/Tick.svg" alt="" width="18" class="me-2">Sort by due date or created date</li> <li class="mb-2"><img src="./images/Tick.svg" alt="" width="18" class="me-2">Sort by due date or created
date</li>
<li class="mb-2"><img src="./images/Tick.svg" alt="" width="18" class="me-2">Edit any task inline</li> <li class="mb-2"><img src="./images/Tick.svg" alt="" width="18" class="me-2">Edit any task inline</li>
<li class="mb-2"><img src="./images/Tick.svg" alt="" width="18" class="me-2">Works on desktop, tablet and mobile</li> <li class="mb-2"><img src="./images/Tick.svg" alt="" width="18" class="me-2">Works on desktop, tablet and
mobile</li>
</ul> </ul>
<a href="./dashboard.html" class="btn btn-primary px-4 mt-3">Open the dashboard</a> <a href="./dashboard.html" class="btn btn-primary px-4 mt-3">Open the dashboard</a>
</div> </div>
@@ -160,7 +163,8 @@
<div class="container py-5"> <div class="container py-5">
<div class="rounded shadow bg-translucent p-4 p-lg-5 col-12 col-lg-8 mx-auto text-white text-center"> <div class="rounded shadow bg-translucent p-4 p-lg-5 col-12 col-lg-8 mx-auto text-white text-center">
<p class="fs-4 fst-italic mb-4"> <p class="fs-4 fst-italic mb-4">
"We switched our whole team over in a week. Everyone actually uses it, and that's the first time I've ever said that about a task tool." "We switched our whole team over in a week. Everyone actually uses it, and that's the first time I've ever
said that about a task tool."
</p> </p>
<p class="mb-0"><strong>Jamie L.</strong></p> <p class="mb-0"><strong>Jamie L.</strong></p>
<p class="text-white-50 mb-0">Operations Lead, Greenfield Studio</p> <p class="text-white-50 mb-0">Operations Lead, Greenfield Studio</p>

View File

@@ -42,6 +42,20 @@ function resetForm() {
taskForm.reset(); taskForm.reset();
} }
function showAlert(message, variant = 'success') {
const container = document.getElementById('alertContainer');
if (!container) return;
const alert = document.createElement('div');
alert.className = `alert alert-${variant} alert-dismissible fade show shadow-sm`;
alert.role = 'alert';
alert.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
`;
container.appendChild(alert);
setTimeout(() => bootstrap.Alert.getOrCreateInstance(alert).close(), 3000);
}
const sortButton = document.getElementById("sortSelect"); const sortButton = document.getElementById("sortSelect");
window.addEventListener("DOMContentLoaded", () => { window.addEventListener("DOMContentLoaded", () => {
sortButton.value = "default"; sortButton.value = "default";
@@ -93,7 +107,11 @@ taskForm.addEventListener("submit", (event) => {
else if (event.target.classList.contains("delete")) { else if (event.target.classList.contains("delete")) {
const taskId = event.target.getAttribute("data-id"); const taskId = event.target.getAttribute("data-id");
deleteTask(taskId); const confirmButton = document.getElementById('confirmDeleteButton');
confirmButton.addEventListener('click', async () => {
await deleteTask(taskId);
bootstrap.Modal.getInstance(document.getElementById('deleteModal')).hide();
}, { once: true });
} }
else if (event.target.classList.contains('edit')) { else if (event.target.classList.contains('edit')) {
const task = { const task = {
@@ -148,7 +166,7 @@ function formatTask(task) {
li.innerHTML = ` li.innerHTML = `
<div class="d-flex justify-content-between align-items-start"> <div class="d-flex justify-content-between align-items-start">
<h4 class="${done} col-11">${task.title}</h4> <h4 class="${done} col-11">${task.title}</h4>
<button data-id="${task._id}" type="button" class="btn-close delete" aria-label="Close"></button> <button data-id="${task._id}" type="button" class="btn-close delete" data-bs-toggle="modal" data-bs-target="#deleteModal" aria-label="Close"></button>
</div> </div>
<p class="${done}">${task.description}</p> <p class="${done}">${task.description}</p>
<p class="${done}"><strong>Due: </strong>${new Date(task.dueDate).toLocaleDateString()}</p> <p class="${done}"><strong>Due: </strong>${new Date(task.dueDate).toLocaleDateString()}</p>
@@ -328,6 +346,7 @@ async function deleteTask(taskId) {
const data = await response.json(); const data = await response.json();
console.log({ message: "Deleted Task:", Task: data }); console.log({ message: "Deleted Task:", Task: data });
displayTasks(); displayTasks();
showAlert('Task deleted successfully.');
} catch (error) { } catch (error) {
console.error("Error:", error); console.error("Error:", error);

View File

@@ -4,7 +4,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>To Do App | Login</title> <title>BusketLisk &#x2022; Login</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" /> integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" />
<link rel="stylesheet" href="./css/styles.css" /> <link rel="stylesheet" href="./css/styles.css" />

View File

@@ -4,7 +4,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>To Do App | Pricing</title> <title>BusketLisk &#x2022; Pricing</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" /> integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" />
<link rel="stylesheet" href="./css/styles.css" /> <link rel="stylesheet" href="./css/styles.css" />

View File

@@ -4,7 +4,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>To Do App | Signup</title> <title>BusketLisk &#x2022; Signup</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" /> integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" />
<link rel="stylesheet" href="./css/styles.css" /> <link rel="stylesheet" href="./css/styles.css" />

View File

@@ -4,7 +4,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>To Do App | Support</title> <title>BusketLisk &#x2022; Support</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" /> integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" />
<link rel="stylesheet" href="./css/styles.css" /> <link rel="stylesheet" href="./css/styles.css" />
@@ -69,7 +69,8 @@
<form id="contactForm"> <form id="contactForm">
<input name="name" type="text" class="form-control mb-3 shadow-sm" placeholder="Your name" required> <input name="name" type="text" class="form-control mb-3 shadow-sm" placeholder="Your name" required>
<input name="email" type="email" class="form-control mb-3 shadow-sm" placeholder="Your email" required> <input name="email" type="email" class="form-control mb-3 shadow-sm" placeholder="Your email" required>
<textarea name="message" rows="5" class="form-control mb-3 shadow-sm" placeholder="How can we help?" required></textarea> <textarea name="message" rows="5" class="form-control mb-3 shadow-sm" placeholder="How can we help?"
required></textarea>
<button type="submit" class="btn btn-primary px-4">Send message</button> <button type="submit" class="btn btn-primary px-4">Send message</button>
</form> </form>
<hr class="border-light my-4"> <hr class="border-light my-4">