Spaces:
Running
Running
When the "set reminder" at the bottom of the task is clicked it should produce something that the user can say yes or no and if yes the user should be able to click daily, weekly, monthly until the task is complete or deleted.
Browse files- components/task-card.js +4 -6
- index.html +58 -2
- script.js +109 -20
components/task-card.js
CHANGED
|
@@ -8,12 +8,12 @@ class TaskCard extends HTMLElement {
|
|
| 8 |
const completed = this.getAttribute('completed') === 'true';
|
| 9 |
const hasReminder = this.getAttribute('has-reminder') === 'true';
|
| 10 |
const reminderStopped = this.getAttribute('reminder-stopped') === 'true';
|
|
|
|
| 11 |
const createdAt = this.getAttribute('created-at');
|
| 12 |
|
| 13 |
const createdDate = new Date(createdAt).toLocaleDateString();
|
| 14 |
const timeAgo = this.getTimeAgo(createdAt);
|
| 15 |
-
|
| 16 |
-
this.shadowRoot.innerHTML = `
|
| 17 |
<style>
|
| 18 |
.task-card {
|
| 19 |
background: white;
|
|
@@ -156,16 +156,14 @@ class TaskCard extends HTMLElement {
|
|
| 156 |
<span>${timeAgo}</span>
|
| 157 |
</div>
|
| 158 |
</div>
|
| 159 |
-
|
| 160 |
<div class="flex items-center justify-between">
|
| 161 |
<div class="flex gap-2">
|
| 162 |
${hasReminder && !reminderStopped ?
|
| 163 |
-
|
| 164 |
${reminderStopped ?
|
| 165 |
'<span class="badge badge-stopped">Reminder Stopped</span>' : ''}
|
| 166 |
</div>
|
| 167 |
-
|
| 168 |
-
<div class="flex items-center gap-1">
|
| 169 |
${!completed ? `
|
| 170 |
<button class="action-btn reminder-btn ${hasReminder && !reminderStopped ? 'active' : ''}"
|
| 171 |
data-action="reminder"
|
|
|
|
| 8 |
const completed = this.getAttribute('completed') === 'true';
|
| 9 |
const hasReminder = this.getAttribute('has-reminder') === 'true';
|
| 10 |
const reminderStopped = this.getAttribute('reminder-stopped') === 'true';
|
| 11 |
+
const reminderFrequency = this.getAttribute('reminder-frequency');
|
| 12 |
const createdAt = this.getAttribute('created-at');
|
| 13 |
|
| 14 |
const createdDate = new Date(createdAt).toLocaleDateString();
|
| 15 |
const timeAgo = this.getTimeAgo(createdAt);
|
| 16 |
+
this.shadowRoot.innerHTML = `
|
|
|
|
| 17 |
<style>
|
| 18 |
.task-card {
|
| 19 |
background: white;
|
|
|
|
| 156 |
<span>${timeAgo}</span>
|
| 157 |
</div>
|
| 158 |
</div>
|
|
|
|
| 159 |
<div class="flex items-center justify-between">
|
| 160 |
<div class="flex gap-2">
|
| 161 |
${hasReminder && !reminderStopped ?
|
| 162 |
+
`<span class="badge badge-reminder">Active (${reminderFrequency || 'daily'})</span>` : ''}
|
| 163 |
${reminderStopped ?
|
| 164 |
'<span class="badge badge-stopped">Reminder Stopped</span>' : ''}
|
| 165 |
</div>
|
| 166 |
+
<div class="flex items-center gap-1">
|
|
|
|
| 167 |
${!completed ? `
|
| 168 |
<button class="action-btn reminder-btn ${hasReminder && !reminderStopped ? 'active' : ''}"
|
| 169 |
data-action="reminder"
|
index.html
CHANGED
|
@@ -160,7 +160,6 @@
|
|
| 160 |
<p class="text-gray-500">Add your first task to get started!</p>
|
| 161 |
</div>
|
| 162 |
</main>
|
| 163 |
-
|
| 164 |
<!-- Email Preview Modal -->
|
| 165 |
<div id="emailModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50 flex items-center justify-center p-4">
|
| 166 |
<div class="bg-white rounded-2xl max-w-2xl w-full max-h-[90vh] overflow-auto">
|
|
@@ -198,7 +197,64 @@
|
|
| 198 |
</div>
|
| 199 |
</div>
|
| 200 |
|
| 201 |
-
<!--
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
<script src="components/navbar.js"></script>
|
| 203 |
<script src="components/task-card.js"></script>
|
| 204 |
|
|
|
|
| 160 |
<p class="text-gray-500">Add your first task to get started!</p>
|
| 161 |
</div>
|
| 162 |
</main>
|
|
|
|
| 163 |
<!-- Email Preview Modal -->
|
| 164 |
<div id="emailModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50 flex items-center justify-center p-4">
|
| 165 |
<div class="bg-white rounded-2xl max-w-2xl w-full max-h-[90vh] overflow-auto">
|
|
|
|
| 197 |
</div>
|
| 198 |
</div>
|
| 199 |
|
| 200 |
+
<!-- Reminder Frequency Modal -->
|
| 201 |
+
<div id="reminderModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50 flex items-center justify-center p-4">
|
| 202 |
+
<div class="bg-white rounded-2xl max-w-md w-full">
|
| 203 |
+
<div class="p-6 border-b border-gray-200">
|
| 204 |
+
<div class="flex justify-between items-center">
|
| 205 |
+
<h3 class="text-xl font-bold text-gray-800">Set Reminder Frequency</h3>
|
| 206 |
+
<button id="closeReminderModal" class="text-gray-500 hover:text-gray-700">
|
| 207 |
+
<i data-feather="x" class="w-6 h-6"></i>
|
| 208 |
+
</button>
|
| 209 |
+
</div>
|
| 210 |
+
</div>
|
| 211 |
+
<div class="p-6">
|
| 212 |
+
<p class="text-gray-600 mb-6">How often would you like to receive email reminders for this task?</p>
|
| 213 |
+
|
| 214 |
+
<div class="space-y-3 mb-6">
|
| 215 |
+
<button class="frequency-btn w-full text-left px-4 py-3 border-2 border-gray-200 rounded-lg hover:border-primary-500 hover:bg-primary-50 transition-all" data-frequency="daily">
|
| 216 |
+
<div class="flex items-center justify-between">
|
| 217 |
+
<div>
|
| 218 |
+
<p class="font-semibold text-gray-800">📅 Daily</p>
|
| 219 |
+
<p class="text-sm text-gray-500">Receive reminders every day</p>
|
| 220 |
+
</div>
|
| 221 |
+
<i data-feather="chevron-right" class="w-5 h-5 text-gray-400"></i>
|
| 222 |
+
</div>
|
| 223 |
+
</button>
|
| 224 |
+
|
| 225 |
+
<button class="frequency-btn w-full text-left px-4 py-3 border-2 border-gray-200 rounded-lg hover:border-primary-500 hover:bg-primary-50 transition-all" data-frequency="weekly">
|
| 226 |
+
<div class="flex items-center justify-between">
|
| 227 |
+
<div>
|
| 228 |
+
<p class="font-semibold text-gray-800">📆 Weekly</p>
|
| 229 |
+
<p class="text-sm text-gray-500">Receive reminders every week</p>
|
| 230 |
+
</div>
|
| 231 |
+
<i data-feather="chevron-right" class="w-5 h-5 text-gray-400"></i>
|
| 232 |
+
</div>
|
| 233 |
+
</button>
|
| 234 |
+
|
| 235 |
+
<button class="frequency-btn w-full text-left px-4 py-3 border-2 border-gray-200 rounded-lg hover:border-primary-500 hover:bg-primary-50 transition-all" data-frequency="monthly">
|
| 236 |
+
<div class="flex items-center justify-between">
|
| 237 |
+
<div>
|
| 238 |
+
<p class="font-semibold text-gray-800">🗓️ Monthly</p>
|
| 239 |
+
<p class="text-sm text-gray-500">Receive reminders every month</p>
|
| 240 |
+
</div>
|
| 241 |
+
<i data-feather="chevron-right" class="w-5 h-5 text-gray-400"></i>
|
| 242 |
+
</div>
|
| 243 |
+
</button>
|
| 244 |
+
</div>
|
| 245 |
+
|
| 246 |
+
<div class="flex gap-3">
|
| 247 |
+
<button id="cancelReminder" class="flex-1 px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors">
|
| 248 |
+
Cancel
|
| 249 |
+
</button>
|
| 250 |
+
<button id="stopExistingReminder" class="flex-1 px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors hidden">
|
| 251 |
+
Stop Reminder
|
| 252 |
+
</button>
|
| 253 |
+
</div>
|
| 254 |
+
</div>
|
| 255 |
+
</div>
|
| 256 |
+
</div>
|
| 257 |
+
<!-- Components -->
|
| 258 |
<script src="components/navbar.js"></script>
|
| 259 |
<script src="components/task-card.js"></script>
|
| 260 |
|
script.js
CHANGED
|
@@ -12,7 +12,6 @@ class TaskManager {
|
|
| 12 |
this.updateStats();
|
| 13 |
this.startReminderScheduler();
|
| 14 |
}
|
| 15 |
-
|
| 16 |
setupEventListeners() {
|
| 17 |
// Add task
|
| 18 |
document.getElementById('addTaskBtn').addEventListener('click', () => this.addTask());
|
|
@@ -32,15 +31,56 @@ class TaskManager {
|
|
| 32 |
document.getElementById('emailModal').classList.add('hidden');
|
| 33 |
});
|
| 34 |
|
| 35 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
document.getElementById('emailModal').addEventListener('click', (e) => {
|
| 37 |
if (e.target.id === 'emailModal') {
|
| 38 |
document.getElementById('emailModal').classList.add('hidden');
|
| 39 |
}
|
| 40 |
});
|
| 41 |
-
}
|
| 42 |
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
const taskInput = document.getElementById('taskInput');
|
| 45 |
const emailInput = document.getElementById('emailInput');
|
| 46 |
const taskText = taskInput.value.trim();
|
|
@@ -111,22 +151,42 @@ class TaskManager {
|
|
| 111 |
return;
|
| 112 |
}
|
| 113 |
|
|
|
|
|
|
|
| 114 |
if (task.hasReminder && !task.reminderStopped) {
|
| 115 |
-
//
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
} else {
|
| 120 |
-
//
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
this.showEmailPreview(task);
|
| 125 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
this.saveTasks();
|
| 127 |
this.renderTasks();
|
| 128 |
this.updateStats();
|
| 129 |
}
|
|
|
|
|
|
|
|
|
|
| 130 |
}
|
| 131 |
deleteTask(id) {
|
| 132 |
if (confirm('Are you sure you want to delete this task?')) {
|
|
@@ -186,9 +246,10 @@ deleteTask(id) {
|
|
| 186 |
completed="${task.completed}"
|
| 187 |
has-reminder="${task.hasReminder}"
|
| 188 |
reminder-stopped="${task.reminderStopped}"
|
|
|
|
| 189 |
created-at="${task.createdAt}"
|
| 190 |
></task-card>
|
| 191 |
-
|
| 192 |
|
| 193 |
// Re-initialize feather icons for new elements
|
| 194 |
feather.replace();
|
|
@@ -213,17 +274,45 @@ deleteTask(id) {
|
|
| 213 |
document.getElementById('emailModal').classList.remove('hidden');
|
| 214 |
}
|
| 215 |
startReminderScheduler() {
|
| 216 |
-
//
|
| 217 |
setInterval(() => {
|
| 218 |
this.tasks.forEach(task => {
|
| 219 |
if (task.hasReminder && !task.reminderStopped && !task.completed) {
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
}
|
| 225 |
});
|
| 226 |
-
},
|
| 227 |
}
|
| 228 |
showToast(message, type = 'info') {
|
| 229 |
const toast = document.createElement('div');
|
|
|
|
| 12 |
this.updateStats();
|
| 13 |
this.startReminderScheduler();
|
| 14 |
}
|
|
|
|
| 15 |
setupEventListeners() {
|
| 16 |
// Add task
|
| 17 |
document.getElementById('addTaskBtn').addEventListener('click', () => this.addTask());
|
|
|
|
| 31 |
document.getElementById('emailModal').classList.add('hidden');
|
| 32 |
});
|
| 33 |
|
| 34 |
+
// Reminder modal
|
| 35 |
+
document.getElementById('closeReminderModal').addEventListener('click', () => {
|
| 36 |
+
document.getElementById('reminderModal').classList.add('hidden');
|
| 37 |
+
this.currentReminderTaskId = null;
|
| 38 |
+
});
|
| 39 |
+
|
| 40 |
+
document.getElementById('cancelReminder').addEventListener('click', () => {
|
| 41 |
+
document.getElementById('reminderModal').classList.add('hidden');
|
| 42 |
+
this.currentReminderTaskId = null;
|
| 43 |
+
});
|
| 44 |
+
|
| 45 |
+
document.getElementById('stopExistingReminder').addEventListener('click', () => {
|
| 46 |
+
if (this.currentReminderTaskId) {
|
| 47 |
+
const task = this.tasks.find(t => t.id === this.currentReminderTaskId);
|
| 48 |
+
if (task) {
|
| 49 |
+
task.hasReminder = false;
|
| 50 |
+
task.reminderStopped = true;
|
| 51 |
+
this.saveTasks();
|
| 52 |
+
this.renderTasks();
|
| 53 |
+
this.updateStats();
|
| 54 |
+
this.showToast('Reminder stopped', 'info');
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
document.getElementById('reminderModal').classList.add('hidden');
|
| 58 |
+
this.currentReminderTaskId = null;
|
| 59 |
+
});
|
| 60 |
+
|
| 61 |
+
// Frequency buttons
|
| 62 |
+
document.querySelectorAll('.frequency-btn').forEach(btn => {
|
| 63 |
+
btn.addEventListener('click', (e) => {
|
| 64 |
+
const frequency = e.currentTarget.dataset.frequency;
|
| 65 |
+
this.setReminderFrequency(frequency);
|
| 66 |
+
});
|
| 67 |
+
});
|
| 68 |
+
|
| 69 |
+
// Close modals on outside click
|
| 70 |
document.getElementById('emailModal').addEventListener('click', (e) => {
|
| 71 |
if (e.target.id === 'emailModal') {
|
| 72 |
document.getElementById('emailModal').classList.add('hidden');
|
| 73 |
}
|
| 74 |
});
|
|
|
|
| 75 |
|
| 76 |
+
document.getElementById('reminderModal').addEventListener('click', (e) => {
|
| 77 |
+
if (e.target.id === 'reminderModal') {
|
| 78 |
+
document.getElementById('reminderModal').classList.add('hidden');
|
| 79 |
+
this.currentReminderTaskId = null;
|
| 80 |
+
}
|
| 81 |
+
});
|
| 82 |
+
}
|
| 83 |
+
addTask() {
|
| 84 |
const taskInput = document.getElementById('taskInput');
|
| 85 |
const emailInput = document.getElementById('emailInput');
|
| 86 |
const taskText = taskInput.value.trim();
|
|
|
|
| 151 |
return;
|
| 152 |
}
|
| 153 |
|
| 154 |
+
this.currentReminderTaskId = id;
|
| 155 |
+
|
| 156 |
if (task.hasReminder && !task.reminderStopped) {
|
| 157 |
+
// Show stop reminder option
|
| 158 |
+
document.getElementById('reminderModal').classList.remove('hidden');
|
| 159 |
+
document.getElementById('stopExistingReminder').classList.remove('hidden');
|
| 160 |
+
document.querySelectorAll('.frequency-btn').forEach(btn => btn.style.display = 'none');
|
| 161 |
} else {
|
| 162 |
+
// Show frequency selection
|
| 163 |
+
document.getElementById('reminderModal').classList.remove('hidden');
|
| 164 |
+
document.getElementById('stopExistingReminder').classList.add('hidden');
|
| 165 |
+
document.querySelectorAll('.frequency-btn').forEach(btn => btn.style.display = 'block');
|
|
|
|
| 166 |
}
|
| 167 |
+
}
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
setReminderFrequency(frequency) {
|
| 171 |
+
if (!this.currentReminderTaskId) return;
|
| 172 |
+
|
| 173 |
+
const task = this.tasks.find(t => t.id === this.currentReminderTaskId);
|
| 174 |
+
if (task) {
|
| 175 |
+
task.hasReminder = true;
|
| 176 |
+
task.reminderStopped = false;
|
| 177 |
+
task.reminderFrequency = frequency;
|
| 178 |
+
|
| 179 |
+
const frequencyText = frequency === 'daily' ? 'daily' : frequency === 'weekly' ? 'weekly' : 'monthly';
|
| 180 |
+
this.showToast(`Reminder set! You'll receive ${frequencyText} email notifications`, 'success');
|
| 181 |
+
|
| 182 |
+
this.showEmailPreview(task);
|
| 183 |
this.saveTasks();
|
| 184 |
this.renderTasks();
|
| 185 |
this.updateStats();
|
| 186 |
}
|
| 187 |
+
|
| 188 |
+
document.getElementById('reminderModal').classList.add('hidden');
|
| 189 |
+
this.currentReminderTaskId = null;
|
| 190 |
}
|
| 191 |
deleteTask(id) {
|
| 192 |
if (confirm('Are you sure you want to delete this task?')) {
|
|
|
|
| 246 |
completed="${task.completed}"
|
| 247 |
has-reminder="${task.hasReminder}"
|
| 248 |
reminder-stopped="${task.reminderStopped}"
|
| 249 |
+
reminder-frequency="${task.reminderFrequency || ''}"
|
| 250 |
created-at="${task.createdAt}"
|
| 251 |
></task-card>
|
| 252 |
+
`).join('');
|
| 253 |
|
| 254 |
// Re-initialize feather icons for new elements
|
| 255 |
feather.replace();
|
|
|
|
| 274 |
document.getElementById('emailModal').classList.remove('hidden');
|
| 275 |
}
|
| 276 |
startReminderScheduler() {
|
| 277 |
+
// Check for reminders every minute
|
| 278 |
setInterval(() => {
|
| 279 |
this.tasks.forEach(task => {
|
| 280 |
if (task.hasReminder && !task.reminderStopped && !task.completed) {
|
| 281 |
+
const now = new Date();
|
| 282 |
+
const lastReminder = task.lastReminder ? new Date(task.lastReminder) : null;
|
| 283 |
+
|
| 284 |
+
let shouldSend = false;
|
| 285 |
+
|
| 286 |
+
if (!lastReminder) {
|
| 287 |
+
// First reminder
|
| 288 |
+
shouldSend = true;
|
| 289 |
+
} else {
|
| 290 |
+
const timeDiff = now - lastReminder;
|
| 291 |
+
const daysDiff = timeDiff / (1000 * 60 * 60 * 24);
|
| 292 |
+
|
| 293 |
+
switch (task.reminderFrequency) {
|
| 294 |
+
case 'daily':
|
| 295 |
+
shouldSend = daysDiff >= 1;
|
| 296 |
+
break;
|
| 297 |
+
case 'weekly':
|
| 298 |
+
shouldSend = daysDiff >= 7;
|
| 299 |
+
break;
|
| 300 |
+
case 'monthly':
|
| 301 |
+
shouldSend = daysDiff >= 30;
|
| 302 |
+
break;
|
| 303 |
+
}
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
if (shouldSend) {
|
| 307 |
+
// In a real app, this would send an email
|
| 308 |
+
console.log(`Sending ${task.reminderFrequency} reminder for task: ${task.text} to ${task.email}`);
|
| 309 |
+
this.showToast(`Reminder: ${task.text}`, 'info');
|
| 310 |
+
task.lastReminder = now.toISOString();
|
| 311 |
+
this.saveTasks();
|
| 312 |
+
}
|
| 313 |
}
|
| 314 |
});
|
| 315 |
+
}, 60000); // Check every minute
|
| 316 |
}
|
| 317 |
showToast(message, type = 'info') {
|
| 318 |
const toast = document.createElement('div');
|