Skip to content

Commit 602b115

Browse files
committed
feat: add task prioritization && fix notification permission handling
1 parent 4e48578 commit 602b115

6 files changed

Lines changed: 233 additions & 12 deletions

File tree

.vscode/settings.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ Designed for both desktop and mobile, TaskWave delivers a smooth, reliable, app-
6767
## 📁 Project Structure
6868

6969
```text
70-
.
7170
├─ index.html # Main entry: form, task list, install/theme controls
7271
├─ style.css # Global styles, theming, responsive layout, animations
7372
├─ manifest.json # PWA manifest: name, icons, display mode, colors

index.html

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,67 @@ <h2 class="lobster text-center mb-2">
177177
Task description is optional.
178178
</div>
179179
</div>
180+
181+
<!-- Advanced task options toggle -->
182+
<div class="mb-2">
183+
<button
184+
type="button"
185+
id="toggle-advanced-fields"
186+
class="btn btn-link p-0 small advanced-fields-toggle d-flex align-items-center gap-1"
187+
aria-expanded="false"
188+
>
189+
<span class="advanced-fields-toggle-text">Show additional options</span>
190+
<i class="fa-solid fa-angle-down trans-3"></i>
191+
</button>
192+
</div>
193+
194+
<!-- Advanced Task Options (collapsed by default) -->
195+
<div
196+
id="advanced-task-options"
197+
class="advanced-task-options"
198+
>
199+
<!-- Task Priority -->
200+
<div class="mb-3">
201+
<label
202+
for="task-priority"
203+
class="form-label"
204+
>Priority:</label
205+
>
206+
<select
207+
class="form-select"
208+
id="task-priority"
209+
name="task-priority"
210+
>
211+
<option value="Low">Low</option>
212+
<option value="Medium" selected>
213+
Medium
214+
</option>
215+
<option value="High">High</option>
216+
</select>
217+
<div class="form-text">
218+
Choose how important this task is.
219+
</div>
220+
</div>
221+
222+
<!-- Task Tags -->
223+
<div class="mb-3">
224+
<label
225+
for="task-tags"
226+
class="form-label"
227+
>Tags (optional):</label
228+
>
229+
<input
230+
type="text"
231+
class="form-control"
232+
id="task-tags"
233+
name="task-tags"
234+
placeholder="e.g. Work, Personal, Urgent"
235+
/>
236+
<div class="form-text">
237+
Separate multiple tags with commas.
238+
</div>
239+
</div>
240+
</div>
180241
<!-- Task Time -->
181242
<div
182243
class="time-options mt-3 d-flex flex-column gap-2"

js/app.js

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ const filterButtons = document.querySelectorAll(".tasks-filters [data-filter]");
3131
const themeToggleBtn = document.getElementById("theme-toggle");
3232
const installBtn = document.getElementById("install-btn");
3333
const weeklySummaryEl = document.getElementById("task-weekly-summary");
34+
const advancedFieldsToggleBtn = document.getElementById(
35+
"toggle-advanced-fields",
36+
);
37+
const advancedFieldsContainer = document.getElementById(
38+
"advanced-task-options",
39+
);
3440

3541
// Global Variables
3642
let activeTab = "schedule"; // or: no-time
@@ -62,6 +68,35 @@ dateTimeField.addEventListener("change", (e) => {
6268
// when submit the form
6369
submitFormBtn.addEventListener("click", submitForm);
6470

71+
// toggle advanced (optional) fields visibility
72+
if (advancedFieldsToggleBtn && advancedFieldsContainer) {
73+
// start collapsed
74+
advancedFieldsContainer.classList.remove(
75+
"advanced-task-options--open",
76+
);
77+
advancedFieldsToggleBtn.setAttribute("aria-expanded", "false");
78+
79+
advancedFieldsToggleBtn.addEventListener("click", () => {
80+
const isOpen = advancedFieldsContainer.classList.contains(
81+
"advanced-task-options--open",
82+
);
83+
84+
advancedFieldsContainer.classList.toggle(
85+
"advanced-task-options--open",
86+
!isOpen,
87+
);
88+
89+
advancedFieldsToggleBtn.setAttribute(
90+
"aria-expanded",
91+
!isOpen ? "true" : "false",
92+
);
93+
document.querySelector(".advanced-fields-toggle-text").textContent = !isOpen
94+
? "Hide additional options"
95+
: "Show additional options";
96+
document.querySelector(".advanced-fields-toggle i").classList.toggle("active", !isOpen);
97+
});
98+
}
99+
65100
// initialize filters module for filter buttons
66101
initFilters(filterButtons, () => {
67102
const tasksToRender = getFilteredTasks();
@@ -377,6 +412,15 @@ async function submitForm(e) {
377412
const taskTitle = formData.get("task-title").trim();
378413
const taskTime = formData.get("task-time");
379414
const taskDescription = formData.get("task-description").trim();
415+
const taskPriority = (formData.get("task-priority") || "Medium").trim();
416+
const rawTags = (formData.get("task-tags") || "").trim();
417+
const tags =
418+
rawTags.length > 0
419+
? rawTags
420+
.split(",")
421+
.map((tag) => tag.trim())
422+
.filter((tag) => tag.length > 0)
423+
: [];
380424
// validate form data
381425
if (!checkInputValidity(taskTitle, taskTime, taskDescription)) return;
382426

@@ -387,6 +431,8 @@ async function submitForm(e) {
387431
description: taskDescription,
388432
endTime: taskTime,
389433
status: "pending", // pending, completed, overdue
434+
priority: taskPriority || "Medium",
435+
tags,
390436
};
391437

392438
// store form data in IndexedDB
@@ -825,11 +871,63 @@ function showTaskLists(tasks) {
825871
const title = document.createElement("h4");
826872
title.textContent = task.title ?? "";
827873

874+
// Priority + tags meta row
875+
const meta = document.createElement("div");
876+
meta.classList.add("task-meta", "d-flex", "flex-wrap", "gap-2", "mt-1");
877+
878+
if (task.priority) {
879+
const priorityBadge = document.createElement("span");
880+
priorityBadge.classList.add(
881+
"badge",
882+
"rounded-pill",
883+
"task-priority-badge",
884+
);
885+
886+
const normalizedPriority =
887+
typeof task.priority === "string"
888+
? task.priority.toLowerCase()
889+
: "";
890+
891+
if (normalizedPriority === "high") {
892+
priorityBadge.classList.add("task-priority-high");
893+
priorityBadge.textContent = "High priority";
894+
} else if (normalizedPriority === "low") {
895+
priorityBadge.classList.add("task-priority-low");
896+
priorityBadge.textContent = "Low priority";
897+
} else {
898+
priorityBadge.classList.add("task-priority-medium");
899+
priorityBadge.textContent = "Medium priority";
900+
}
901+
902+
meta.appendChild(priorityBadge);
903+
}
904+
905+
if (Array.isArray(task.tags) && task.tags.length > 0) {
906+
task.tags.forEach((tag) => {
907+
const cleanTag =
908+
typeof tag === "string" ? tag.trim() : String(tag);
909+
if (!cleanTag) return;
910+
911+
const tagBadge = document.createElement("span");
912+
tagBadge.classList.add(
913+
"badge",
914+
"rounded-pill",
915+
"task-tag-badge",
916+
);
917+
tagBadge.textContent = cleanTag;
918+
meta.appendChild(tagBadge);
919+
});
920+
}
921+
828922
const description = document.createElement("p");
829923
description.classList.add("text-secondary", "my-3");
830924
description.textContent = task.description ?? "";
831925

832-
content.append(title, description);
926+
if (meta.children.length > 0) {
927+
content.append(title, meta, description);
928+
} else {
929+
content.append(title, description);
930+
}
833931

834932
// Actions
835933
const actions = document.createElement("div");

js/pushNotificationsHandler.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@ window.addEventListener("load", () => {
77

88

99
// Push Notifications
10-
function requestNotificationPermission() {
10+
async function requestNotificationPermission() {
1111
if (!("Notification" in window)) return;
1212

1313
if (Notification.permission === "granted") return;
1414

15-
Notification.requestPermission().then(permission => {
16-
if (permission === "granted") {
17-
}
18-
});
15+
const permission = await Notification.requestPermission();
16+
if (permission === "granted") return true;
17+
return false;
1918
}
2019

2120
// Notification Toggler in UI
@@ -29,15 +28,19 @@ document.addEventListener("DOMContentLoaded", () => {
2928
toggle.checked = true;
3029
}
3130

32-
toggle.addEventListener("change", (e) => {
31+
toggle.addEventListener("change", async (e) => {
3332
const isEnabled = e.target.checked;
3433

3534
// Save preference to localStorage
3635
localStorage.setItem("notificationsEnabled", isEnabled ? "true" : "false");
3736

3837
// When user turns notifications ON, request permission
3938
if (isEnabled) {
40-
requestNotificationPermission();
39+
const permission = await requestNotificationPermission();
40+
if (!permission) {
41+
e.target.removeAttribute("checked");
42+
e.target.checked = false;
43+
}
4144
}
4245
});
4346
});

style.css

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,40 @@ main > .container {
132132
font-size: 0.95rem;
133133
color: var(--danger-hover-color);
134134
}
135+
.advanced-fields-toggle .fa-angle-down.active {
136+
rotate: -180deg;
137+
}
138+
139+
/* Advanced fields toggle */
140+
.advanced-fields-toggle {
141+
font-size: 0.9rem;
142+
color: var(--time-label-color);
143+
text-decoration: none;
144+
}
145+
146+
.advanced-fields-toggle:hover,
147+
.advanced-fields-toggle:focus-visible {
148+
color: var(--primary-color);
149+
}
150+
151+
/* Advanced task options container (smooth slide) */
152+
.advanced-task-options {
153+
max-height: 0;
154+
overflow: hidden;
155+
opacity: 0;
156+
transform: translateY(-4px);
157+
transition:
158+
max-height 0.25s ease,
159+
opacity 0.2s ease,
160+
transform 0.25s ease;
161+
}
162+
163+
.advanced-task-options--open {
164+
max-height: 350px; /* enough to show both fields */
165+
opacity: 1;
166+
transform: translateY(0);
167+
margin-top: 0.1rem;
168+
}
135169

136170
/* ========= Tabs ========= */
137171
.tabs .active {
@@ -326,6 +360,35 @@ main > .container {
326360
margin-block: 1rem !important;
327361
}
328362

363+
/* ========= Task Meta (Priority + Tags) ========= */
364+
.task-meta {
365+
font-size: 0.8rem;
366+
}
367+
368+
.task-priority-badge {
369+
padding-inline: 0.65rem;
370+
}
371+
372+
.task-priority-low {
373+
background-color: #e7f5ff;
374+
color: #0c5460;
375+
}
376+
377+
.task-priority-medium {
378+
background-color: #fff3cd;
379+
color: #856404;
380+
}
381+
382+
.task-priority-high {
383+
background-color: #f8d7da;
384+
color: #721c24;
385+
}
386+
387+
.task-tag-badge {
388+
background-color: #e9ecef;
389+
color: #495057;
390+
}
391+
329392
/* ========= Notifications ========= */
330393
.notifications-container {
331394
position: fixed;

0 commit comments

Comments
 (0)