Navya-Sree commited on
Commit
fa1bd0f
·
verified ·
1 Parent(s): 100a76f

Create index.html

Browse files
Files changed (1) hide show
  1. index.html +693 -0
index.html ADDED
@@ -0,0 +1,693 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AI-Powered Reminder System</title>
7
+ <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/global/luxon.min.js"></script>
10
+ <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
11
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
12
+ <style>
13
+ :root {
14
+ --primary: #4361ee;
15
+ --secondary: #3f37c9;
16
+ --success: #4cc9f0;
17
+ --danger: #f72585;
18
+ --warning: #f8961e;
19
+ --light: #f8f9fa;
20
+ --dark: #212529;
21
+ }
22
+
23
+ body {
24
+ background-color: #f0f2f5;
25
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
26
+ }
27
+
28
+ .card {
29
+ border-radius: 12px;
30
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
31
+ transition: transform 0.2s;
32
+ }
33
+
34
+ .card:hover {
35
+ transform: translateY(-5px);
36
+ }
37
+
38
+ .priority-low { border-left: 4px solid #6c757d; }
39
+ .priority-medium { border-left: 4px solid #ffc107; }
40
+ .priority-high { border-left: 4px solid #dc3545; }
41
+
42
+ .voice-recorder {
43
+ background: #e9ecef;
44
+ border-radius: 8px;
45
+ padding: 15px;
46
+ margin: 10px 0;
47
+ }
48
+
49
+ #app {
50
+ max-width: 1400px;
51
+ margin: 0 auto;
52
+ padding: 20px;
53
+ }
54
+
55
+ .sidebar {
56
+ background: white;
57
+ border-radius: 12px;
58
+ padding: 20px;
59
+ height: fit-content;
60
+ }
61
+
62
+ .calendar-day {
63
+ border: 1px solid #dee2e6;
64
+ min-height: 120px;
65
+ padding: 10px;
66
+ cursor: pointer;
67
+ }
68
+
69
+ .calendar-day.active {
70
+ background-color: #e7f5ff;
71
+ border-color: #4361ee;
72
+ }
73
+
74
+ .notification-badge {
75
+ position: absolute;
76
+ top: -8px;
77
+ right: -8px;
78
+ }
79
+ </style>
80
+ </head>
81
+ <body>
82
+ <div id="app">
83
+ <!-- Header -->
84
+ <header class="py-4 mb-4 border-bottom">
85
+ <div class="container-fluid">
86
+ <div class="row align-items-center">
87
+ <div class="col-md-6">
88
+ <h1 class="display-4 fw-bold">
89
+ <i class="bi bi-alarm"></i> AI-Powered Reminder System
90
+ </h1>
91
+ </div>
92
+ <div class="col-md-6 text-end">
93
+ <button class="btn btn-primary me-2" @click="showNotifications = !showNotifications">
94
+ <i class="bi bi-bell"></i>
95
+ <span v-if="notifications.length > 0" class="badge bg-danger notification-badge">
96
+ {{ notifications.length }}
97
+ </span>
98
+ </button>
99
+ <button class="btn btn-outline-primary" @click="showSettings = !showSettings">
100
+ <i class="bi bi-gear"></i> Settings
101
+ </button>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ </header>
106
+
107
+ <!-- Main Content -->
108
+ <div class="container-fluid">
109
+ <div class="row">
110
+ <!-- Sidebar -->
111
+ <div class="col-lg-4 mb-4">
112
+ <div class="sidebar sticky-top">
113
+ <h3 class="mb-4"><i class="bi bi-plus-circle"></i> Add New Reminder</h3>
114
+ <form @submit.prevent="createReminder">
115
+ <div class="mb-3">
116
+ <label class="form-label">Title *</label>
117
+ <input v-model="newReminder.title" type="text" class="form-control" required>
118
+ </div>
119
+ <div class="mb-3">
120
+ <label class="form-label">Description</label>
121
+ <textarea v-model="newReminder.description" class="form-control" rows="2"></textarea>
122
+ </div>
123
+ <div class="row mb-3">
124
+ <div class="col-md-6">
125
+ <label class="form-label">Date *</label>
126
+ <input v-model="newReminder.due_date" type="date" class="form-control" required>
127
+ </div>
128
+ <div class="col-md-6">
129
+ <label class="form-label">Time *</label>
130
+ <input v-model="newReminder.due_time" type="time" class="form-control" required>
131
+ </div>
132
+ </div>
133
+ <div class="mb-3">
134
+ <label class="form-label">Priority</label>
135
+ <select v-model="newReminder.priority" class="form-select">
136
+ <option value="Low">Low</option>
137
+ <option value="Medium">Medium</option>
138
+ <option value="High">High</option>
139
+ </select>
140
+ </div>
141
+ <div class="mb-3">
142
+ <label class="form-check-label">
143
+ <input type="checkbox" v-model="showVoiceRecorder" class="form-check-input">
144
+ Add Voice Reminder
145
+ </label>
146
+ </div>
147
+
148
+ <div v-if="showVoiceRecorder" class="voice-recorder mb-3">
149
+ <div class="d-flex justify-content-between align-items-center mb-2">
150
+ <div>
151
+ <button class="btn btn-sm btn-danger" @click="toggleRecording" :disabled="isRecording">
152
+ <i class="bi bi-record-circle"></i>
153
+ {{ isRecording ? 'Stop Recording' : 'Start Recording' }}
154
+ </button>
155
+ <button v-if="audioUrl" class="btn btn-sm btn-outline-secondary ms-2" @click="playRecordedAudio">
156
+ <i class="bi bi-play"></i> Play
157
+ </button>
158
+ </div>
159
+ <audio ref="audioPlayer" :src="audioUrl" hidden></audio>
160
+ </div>
161
+ <div v-if="recordingStatus" class="text-muted small">{{ recordingStatus }}</div>
162
+ </div>
163
+
164
+ <button type="submit" class="btn btn-primary w-100">
165
+ <i class="bi bi-check-circle"></i> Create Reminder
166
+ </button>
167
+ </form>
168
+ </div>
169
+ </div>
170
+
171
+ <!-- Main Content -->
172
+ <div class="col-lg-8">
173
+ <!-- Dashboard Stats -->
174
+ <div class="row mb-4">
175
+ <div class="col-md-4 mb-3">
176
+ <div class="card h-100">
177
+ <div class="card-body text-center">
178
+ <h5 class="card-title">Total Reminders</h5>
179
+ <h2 class="text-primary">{{ reminders.length }}</h2>
180
+ </div>
181
+ </div>
182
+ </div>
183
+ <div class="col-md-4 mb-3">
184
+ <div class="card h-100">
185
+ <div class="card-body text-center">
186
+ <h5 class="card-title">Pending Tasks</h5>
187
+ <h2 class="text-warning">{{ pendingReminders }}</h2>
188
+ </div>
189
+ </div>
190
+ </div>
191
+ <div class="col-md-4 mb-3">
192
+ <div class="card h-100">
193
+ <div class="card-body text-center">
194
+ <h5 class="card-title">High Priority</h5>
195
+ <h2 class="text-danger">{{ highPriorityReminders }}</h2>
196
+ </div>
197
+ </div>
198
+ </div>
199
+ </div>
200
+
201
+ <!-- Calendar View -->
202
+ <div class="card mb-4">
203
+ <div class="card-header bg-white">
204
+ <h4 class="mb-0"><i class="bi bi-calendar"></i> Calendar View</h4>
205
+ </div>
206
+ <div class="card-body">
207
+ <div class="d-flex justify-content-between mb-3">
208
+ <button class="btn btn-sm btn-outline-secondary" @click="prevMonth">
209
+ <i class="bi bi-chevron-left"></i>
210
+ </button>
211
+ <h5 class="mb-0">{{ currentMonth }}</h5>
212
+ <button class="btn btn-sm btn-outline-secondary" @click="nextMonth">
213
+ <i class="bi bi-chevron-right"></i>
214
+ </button>
215
+ </div>
216
+
217
+ <div class="calendar-grid">
218
+ <div class="row text-center fw-bold border-bottom mb-2">
219
+ <div class="col p-2">Sun</div>
220
+ <div class="col p-2">Mon</div>
221
+ <div class="col p-2">Tue</div>
222
+ <div class="col p-2">Wed</div>
223
+ <div class="col p-2">Thu</div>
224
+ <div class="col p-2">Fri</div>
225
+ <div class="col p-2">Sat</div>
226
+ </div>
227
+
228
+ <div v-for="week in calendar" :key="week[0].date" class="row">
229
+ <div v-for="day in week" :key="day.date"
230
+ class="col calendar-day"
231
+ :class="{
232
+ 'active': isToday(day.date),
233
+ 'text-muted': day.month !== currentMonthNum
234
+ }"
235
+ @click="selectedDate = day.date">
236
+ <div class="d-flex justify-content-between">
237
+ <span>{{ day.day }}</span>
238
+ <span v-if="countRemindersForDate(day.date) > 0"
239
+ class="badge bg-primary rounded-pill">
240
+ {{ countRemindersForDate(day.date) }}
241
+ </span>
242
+ </div>
243
+ <div v-if="isToday(day.date)" class="small text-success">Today</div>
244
+ </div>
245
+ </div>
246
+ </div>
247
+ </div>
248
+ </div>
249
+
250
+ <!-- Reminders for Selected Date -->
251
+ <div class="card">
252
+ <div class="card-header bg-white d-flex justify-content-between align-items-center">
253
+ <h4 class="mb-0"><i class="bi bi-list-check"></i> Reminders for {{ selectedDate }}</h4>
254
+ <div>
255
+ <button class="btn btn-sm btn-outline-primary" @click="getAIInsightsForDate">
256
+ <i class="bi bi-robot"></i> AI Insights
257
+ </button>
258
+ </div>
259
+ </div>
260
+ <div class="card-body">
261
+ <div v-if="aiInsights" class="alert alert-info mb-4">
262
+ <h5><i class="bi bi-lightbulb"></i> AI Insights</h5>
263
+ <div v-html="aiInsights"></div>
264
+ </div>
265
+
266
+ <div v-if="filteredReminders.length === 0" class="text-center py-5">
267
+ <i class="bi bi-check2-circle" style="font-size: 3rem;"></i>
268
+ <h5 class="mt-3">No reminders for this date</h5>
269
+ <p class="text-muted">Add a new reminder using the form on the left</p>
270
+ </div>
271
+
272
+ <div v-for="reminder in filteredReminders" :key="reminder.id" class="mb-3">
273
+ <div class="card" :class="'priority-' + reminder.priority.toLowerCase()">
274
+ <div class="card-body">
275
+ <div class="d-flex justify-content-between">
276
+ <div class="form-check form-switch">
277
+ <input class="form-check-input" type="checkbox"
278
+ v-model="reminder.completed"
279
+ @change="updateReminder(reminder)">
280
+ <label class="form-check-label">
281
+ <h5 class="mb-0">{{ reminder.title }}</h5>
282
+ </label>
283
+ </div>
284
+ <div>
285
+ <button class="btn btn-sm btn-outline-secondary"
286
+ @click="showAIInsights(reminder)">
287
+ <i class="bi bi-robot"></i>
288
+ </button>
289
+ <button class="btn btn-sm btn-outline-danger ms-1"
290
+ @click="deleteReminder(reminder.id)">
291
+ <i class="bi bi-trash"></i>
292
+ </button>
293
+ </div>
294
+ </div>
295
+
296
+ <div class="mt-2">
297
+ <div class="text-muted">
298
+ <i class="bi bi-clock"></i> {{ reminder.due_time }} |
299
+ <span :class="{
300
+ 'text-danger': reminder.priority === 'High',
301
+ 'text-warning': reminder.priority === 'Medium',
302
+ 'text-muted': reminder.priority === 'Low'
303
+ }">
304
+ {{ reminder.priority }} priority
305
+ </span>
306
+ </div>
307
+
308
+ <p v-if="reminder.description" class="mt-2">{{ reminder.description }}</p>
309
+
310
+ <div v-if="reminder.voice_note" class="mt-2">
311
+ <audio controls :src="reminder.voice_note" class="w-100"></audio>
312
+ </div>
313
+
314
+ <div v-if="reminder.ai_insights" class="mt-3">
315
+ <div class="alert alert-light">
316
+ <h6><i class="bi bi-lightbulb"></i> AI Insights</h6>
317
+ <div v-html="reminder.ai_insights"></div>
318
+ </div>
319
+ </div>
320
+ </div>
321
+ </div>
322
+ </div>
323
+ </div>
324
+ </div>
325
+ </div>
326
+ </div>
327
+ </div>
328
+ </div>
329
+
330
+ <!-- Notifications Modal -->
331
+ <div v-if="showNotifications" class="modal fade show" style="display: block; background: rgba(0,0,0,0.5)">
332
+ <div class="modal-dialog modal-dialog-centered">
333
+ <div class="modal-content">
334
+ <div class="modal-header bg-primary text-white">
335
+ <h5 class="modal-title"><i class="bi bi-bell"></i> Active Notifications</h5>
336
+ <button type="button" class="btn-close btn-close-white" @click="showNotifications = false"></button>
337
+ </div>
338
+ <div class="modal-body">
339
+ <div v-if="notifications.length === 0" class="text-center py-4">
340
+ <i class="bi bi-check2-circle" style="font-size: 3rem;"></i>
341
+ <h5 class="mt-3">No active notifications</h5>
342
+ </div>
343
+
344
+ <div v-for="notification in notifications" :key="notification.id" class="alert alert-warning">
345
+ <div class="d-flex justify-content-between align-items-center">
346
+ <div>
347
+ <strong>{{ notification.title }}</strong> is due now!
348
+ </div>
349
+ <button class="btn btn-sm btn-outline-secondary" @click="dismissNotification(notification.id)">
350
+ Dismiss
351
+ </button>
352
+ </div>
353
+ </div>
354
+ </div>
355
+ </div>
356
+ </div>
357
+ </div>
358
+ </div>
359
+
360
+ <script>
361
+ const { createApp, ref, computed, onMounted } = Vue;
362
+
363
+ createApp({
364
+ setup() {
365
+ // Application state
366
+ const reminders = ref([]);
367
+ const notifications = ref([]);
368
+ const newReminder = ref({
369
+ title: '',
370
+ description: '',
371
+ due_date: luxon.DateTime.local().toISODate(),
372
+ due_time: luxon.DateTime.local().toFormat('HH:mm'),
373
+ priority: 'Medium',
374
+ voice_note: ''
375
+ });
376
+ const selectedDate = ref(luxon.DateTime.local().toISODate());
377
+ const currentDate = ref(luxon.DateTime.local());
378
+ const showVoiceRecorder = ref(false);
379
+ const isRecording = ref(false);
380
+ const audioUrl = ref(null);
381
+ const audioBlob = ref(null);
382
+ const recordingStatus = ref('');
383
+ const showNotifications = ref(false);
384
+ const showSettings = ref(false);
385
+ const aiInsights = ref('');
386
+ const mediaRecorder = ref(null);
387
+ const audioChunks = ref([]);
388
+ const audioContext = ref(null);
389
+ const audioPlayer = ref(null);
390
+
391
+ // Computed properties
392
+ const pendingReminders = computed(() => {
393
+ return reminders.value.filter(r => !r.completed).length;
394
+ });
395
+
396
+ const highPriorityReminders = computed(() => {
397
+ return reminders.value.filter(r => r.priority === 'High' && !r.completed).length;
398
+ });
399
+
400
+ const filteredReminders = computed(() => {
401
+ return reminders.value
402
+ .filter(r => r.due_date === selectedDate.value)
403
+ .sort((a, b) => {
404
+ if (a.completed !== b.completed) return a.completed ? 1 : -1;
405
+ if (a.priority !== b.priority) {
406
+ const priorityOrder = { 'High': 1, 'Medium': 2, 'Low': 3 };
407
+ return priorityOrder[a.priority] - priorityOrder[b.priority];
408
+ }
409
+ return a.due_time.localeCompare(b.due_time);
410
+ });
411
+ });
412
+
413
+ const currentMonth = computed(() => {
414
+ return currentDate.value.toFormat('MMMM yyyy');
415
+ });
416
+
417
+ const currentMonthNum = computed(() => {
418
+ return currentDate.value.month;
419
+ });
420
+
421
+ const calendar = computed(() => {
422
+ const startOfMonth = currentDate.value.startOf('month');
423
+ const startDate = startOfMonth.startOf('week');
424
+ const endDate = currentDate.value.endOf('month').endOf('week');
425
+
426
+ const weeks = [];
427
+ let currentDay = startDate;
428
+
429
+ while (currentDay <= endDate) {
430
+ const week = [];
431
+ for (let i = 0; i < 7; i++) {
432
+ week.push({
433
+ date: currentDay.toISODate(),
434
+ day: currentDay.day,
435
+ month: currentDay.month
436
+ });
437
+ currentDay = currentDay.plus({ days: 1 });
438
+ }
439
+ weeks.push(week);
440
+ }
441
+
442
+ return weeks;
443
+ });
444
+
445
+ // Methods
446
+ const loadReminders = async () => {
447
+ try {
448
+ const response = await axios.get('/api/reminders');
449
+ reminders.value = response.data;
450
+ checkForNotifications();
451
+ } catch (error) {
452
+ console.error('Error loading reminders:', error);
453
+ }
454
+ };
455
+
456
+ const createReminder = async () => {
457
+ try {
458
+ const response = await axios.post('/api/reminders', {
459
+ ...newReminder.value,
460
+ voice_note: audioUrl.value
461
+ });
462
+
463
+ reminders.value.push(response.data);
464
+ newReminder.value = {
465
+ title: '',
466
+ description: '',
467
+ due_date: luxon.DateTime.local().toISODate(),
468
+ due_time: luxon.DateTime.local().toFormat('HH:mm'),
469
+ priority: 'Medium',
470
+ voice_note: ''
471
+ };
472
+
473
+ audioUrl.value = null;
474
+ audioBlob.value = null;
475
+ recordingStatus.value = '';
476
+ showVoiceRecorder.value = false;
477
+
478
+ alert('Reminder created successfully!');
479
+ } catch (error) {
480
+ console.error('Error creating reminder:', error);
481
+ alert('Failed to create reminder');
482
+ }
483
+ };
484
+
485
+ const updateReminder = async (reminder) => {
486
+ try {
487
+ await axios.put(`/api/reminders/${reminder.id}`, reminder);
488
+ checkForNotifications();
489
+ } catch (error) {
490
+ console.error('Error updating reminder:', error);
491
+ }
492
+ };
493
+
494
+ const deleteReminder = async (id) => {
495
+ if (!confirm('Are you sure you want to delete this reminder?')) return;
496
+
497
+ try {
498
+ await axios.delete(`/api/reminders/${id}`);
499
+ reminders.value = reminders.value.filter(r => r.id !== id);
500
+ checkForNotifications();
501
+ } catch (error) {
502
+ console.error('Error deleting reminder:', error);
503
+ }
504
+ };
505
+
506
+ const startRecording = async () => {
507
+ try {
508
+ audioChunks.value = [];
509
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
510
+ mediaRecorder.value = new MediaRecorder(stream);
511
+
512
+ mediaRecorder.value.ondataavailable = event => {
513
+ audioChunks.value.push(event.data);
514
+ };
515
+
516
+ mediaRecorder.value.onstop = async () => {
517
+ const audioBlob = new Blob(audioChunks.value, { type: 'audio/wav' });
518
+ audioUrl.value = URL.createObjectURL(audioBlob);
519
+
520
+ // Convert to base64 for saving
521
+ const reader = new FileReader();
522
+ reader.readAsDataURL(audioBlob);
523
+ reader.onloadend = async () => {
524
+ const base64Audio = reader.result;
525
+ try {
526
+ const response = await axios.post('/api/save-voice-note', {
527
+ audio_data: base64Audio
528
+ });
529
+ newReminder.value.voice_note = response.data.path;
530
+ } catch (error) {
531
+ console.error('Error saving voice note:', error);
532
+ }
533
+ };
534
+ };
535
+
536
+ mediaRecorder.value.start();
537
+ isRecording.value = true;
538
+ recordingStatus.value = 'Recording... Click stop when finished';
539
+ } catch (error) {
540
+ console.error('Error starting recording:', error);
541
+ alert('Microphone access denied. Please enable microphone permissions.');
542
+ }
543
+ };
544
+
545
+ const stopRecording = () => {
546
+ if (mediaRecorder.value) {
547
+ mediaRecorder.value.stop();
548
+ mediaRecorder.value.stream.getTracks().forEach(track => track.stop());
549
+ isRecording.value = false;
550
+ recordingStatus.value = 'Recording saved. Click play to review';
551
+ }
552
+ };
553
+
554
+ const toggleRecording = () => {
555
+ if (isRecording.value) {
556
+ stopRecording();
557
+ } else {
558
+ startRecording();
559
+ }
560
+ };
561
+
562
+ const playRecordedAudio = () => {
563
+ if (audioPlayer.value && audioUrl.value) {
564
+ audioPlayer.value.play();
565
+ }
566
+ };
567
+
568
+ const countRemindersForDate = (date) => {
569
+ return reminders.value.filter(r => r.due_date === date && !r.completed).length;
570
+ };
571
+
572
+ const isToday = (date) => {
573
+ return date === luxon.DateTime.local().toISODate();
574
+ };
575
+
576
+ const nextMonth = () => {
577
+ currentDate.value = currentDate.value.plus({ months: 1 });
578
+ };
579
+
580
+ const prevMonth = () => {
581
+ currentDate.value = currentDate.value.minus({ months: 1 });
582
+ };
583
+
584
+ const checkForNotifications = () => {
585
+ const now = luxon.DateTime.local();
586
+ notifications.value = reminders.value
587
+ .filter(r => !r.completed)
588
+ .filter(r => {
589
+ const reminderDate = luxon.DateTime.fromISO(r.due_date);
590
+ const reminderTime = luxon.DateTime.fromISO(`${r.due_date}T${r.due_time}`);
591
+
592
+ // Check if reminder is due within the last minute
593
+ return reminderDate.hasSame(now, 'day') &&
594
+ Math.abs(reminderTime.diff(now, 'minutes').minutes) < 1;
595
+ })
596
+ .map(r => ({
597
+ id: r.id,
598
+ title: r.title,
599
+ time: r.due_time
600
+ }));
601
+
602
+ if (notifications.value.length > 0) {
603
+ showNotifications.value = true;
604
+ }
605
+ };
606
+
607
+ const dismissNotification = (id) => {
608
+ notifications.value = notifications.value.filter(n => n.id !== id);
609
+ };
610
+
611
+ const showAIInsights = async (reminder) => {
612
+ try {
613
+ const response = await axios.post('/api/ai-insights', {
614
+ title: reminder.title,
615
+ description: reminder.description
616
+ });
617
+
618
+ reminder.ai_insights = response.data.insights;
619
+ await updateReminder(reminder);
620
+ } catch (error) {
621
+ console.error('Error getting AI insights:', error);
622
+ }
623
+ };
624
+
625
+ const getAIInsightsForDate = async () => {
626
+ const dateReminders = reminders.value.filter(r => r.due_date === selectedDate.value);
627
+
628
+ if (dateReminders.length === 0) {
629
+ aiInsights.value = 'No reminders to analyze for this date';
630
+ return;
631
+ }
632
+
633
+ try {
634
+ const response = await axios.post('/api/ai-insights', {
635
+ title: `Reminders for ${selectedDate.value}`,
636
+ description: `You have ${dateReminders.length} reminders scheduled for this date.
637
+ ${dateReminders.filter(r => r.priority === 'High').length} are high priority.`
638
+ });
639
+
640
+ aiInsights.value = response.data.insights;
641
+ } catch (error) {
642
+ console.error('Error getting AI insights:', error);
643
+ }
644
+ };
645
+
646
+ // Lifecycle hooks
647
+ onMounted(() => {
648
+ loadReminders();
649
+
650
+ // Check for notifications every minute
651
+ setInterval(() => {
652
+ checkForNotifications();
653
+ }, 60000);
654
+ });
655
+
656
+ return {
657
+ reminders,
658
+ notifications,
659
+ newReminder,
660
+ selectedDate,
661
+ currentDate,
662
+ showVoiceRecorder,
663
+ isRecording,
664
+ audioUrl,
665
+ recordingStatus,
666
+ showNotifications,
667
+ showSettings,
668
+ aiInsights,
669
+ audioPlayer,
670
+ pendingReminders,
671
+ highPriorityReminders,
672
+ filteredReminders,
673
+ currentMonth,
674
+ currentMonthNum,
675
+ calendar,
676
+ createReminder,
677
+ updateReminder,
678
+ deleteReminder,
679
+ toggleRecording,
680
+ playRecordedAudio,
681
+ countRemindersForDate,
682
+ isToday,
683
+ nextMonth,
684
+ prevMonth,
685
+ dismissNotification,
686
+ showAIInsights,
687
+ getAIInsightsForDate
688
+ };
689
+ }
690
+ }).mount('#app');
691
+ </script>
692
+ </body>
693
+ </html>