Skip to content

Commit 7020ea6

Browse files
committed
feat: add print functionality and improve AI summary and live search features
1 parent cf38748 commit 7020ea6

8 files changed

Lines changed: 202 additions & 184 deletions

File tree

logs/combined.log

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,3 +973,18 @@
973973
{"level":"info","message":"::1 - - [01/Sep/2025:07:52:41 +0000] \"GET /js/collab.js HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T07:52:41.627Z"}
974974
{"level":"info","message":"::1 - - [01/Sep/2025:07:52:41 +0000] \"GET /css/main.css HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T07:52:41.637Z"}
975975
{"level":"info","message":"::1 - - [01/Sep/2025:07:52:41 +0000] \"GET /js/theme-toggle.js HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T07:52:41.887Z"}
976+
{"level":"info","message":"::1 - - [01/Sep/2025:08:17:30 +0000] \"GET / HTTP/1.1\" 302 39 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T08:17:30.165Z"}
977+
{"level":"info","message":"::1 - - [01/Sep/2025:08:17:30 +0000] \"GET /dashboard HTTP/1.1\" 200 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T08:17:30.816Z"}
978+
{"level":"info","message":"::1 - - [01/Sep/2025:08:17:31 +0000] \"GET /js/print-utils.js HTTP/1.1\" 200 294 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T08:17:31.039Z"}
979+
{"level":"info","message":"::1 - - [01/Sep/2025:08:17:31 +0000] \"GET /js/theme-toggle.js HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T08:17:31.098Z"}
980+
{"level":"info","message":"::1 - - [01/Sep/2025:08:17:31 +0000] \"GET /js/ai-summary.js HTTP/1.1\" 200 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T08:17:31.142Z"}
981+
{"level":"info","message":"::1 - - [01/Sep/2025:08:17:31 +0000] \"GET /css/main.css HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T08:17:31.623Z"}
982+
{"level":"info","message":"::1 - - [01/Sep/2025:08:17:31 +0000] \"GET /js/live-search.js HTTP/1.1\" 200 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T08:17:31.629Z"}
983+
{"level":"info","message":"::1 - - [01/Sep/2025:08:17:31 +0000] \"GET /favicon.ico HTTP/1.1\" 404 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T08:17:31.878Z"}
984+
{"level":"info","message":"::1 - - [01/Sep/2025:08:17:34 +0000] \"GET /dashboard/export/68b5550bbf049fa379a6b50a HTTP/1.1\" 200 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T08:17:34.285Z"}
985+
{"level":"info","message":"::1 - - [01/Sep/2025:08:17:34 +0000] \"GET /js/theme-toggle.js HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T08:17:34.403Z"}
986+
{"level":"info","message":"::1 - - [01/Sep/2025:08:17:34 +0000] \"GET /css/main.css HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T08:17:34.405Z"}
987+
{"level":"info","message":"::1 - - [01/Sep/2025:08:17:34 +0000] \"GET /js/print-utils.js HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T08:17:34.671Z"}
988+
{"level":"info","message":"::1 - - [01/Sep/2025:08:17:34 +0000] \"GET /js/live-search.js HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T08:17:34.689Z"}
989+
{"level":"info","message":"::1 - - [01/Sep/2025:08:17:34 +0000] \"GET /js/ai-summary.js HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\"","timestamp":"2025-09-01T08:17:34.713Z"}
990+
{"level":"info","message":"SIGINT received, shutting down gracefully","timestamp":"2025-09-01T08:17:41.091Z"}

public/js/ai-summary.js

Lines changed: 141 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,149 @@
22

33
document.addEventListener('DOMContentLoaded', function() {
44
const summarizeButton = document.getElementById('summarizeButton');
5+
const regenerateButton = document.getElementById('regenerateButton');
6+
57
if (summarizeButton) {
68
summarizeButton.addEventListener('click', summarizeNote);
79
}
10+
11+
if (regenerateButton) {
12+
regenerateButton.addEventListener('click', summarizeNote);
13+
}
14+
15+
// Handle toast close buttons
16+
document.addEventListener('click', function(e) {
17+
if (e.target.classList.contains('toast-close-btn')) {
18+
e.target.closest('.toast').remove();
19+
}
20+
});
821
});
922

10-
// Move summarizeNote function here if you want to fully decouple from inline, or keep it in EJS if it uses EJS variables.
11-
// If you want to move it, copy the function from view-notes.ejs and replace <%= noteID %> with a data attribute or window.NOTE_ID.
23+
function showToast(message, type = 'info') {
24+
let toastContainer = document.getElementById('toast-container');
25+
if (!toastContainer) {
26+
toastContainer = createToastContainer();
27+
}
28+
29+
const toast = document.createElement('div');
30+
toast.className = `toast show bg-${type} text-white`;
31+
toast.setAttribute('role', 'alert');
32+
toast.innerHTML = `
33+
<div class="d-flex">
34+
<div class="toast-body">
35+
<i class="fas fa-${type === 'success' ? 'check-circle' : 'exclamation-circle'} me-2"></i>
36+
${message}
37+
</div>
38+
<button type="button" class="btn-close btn-close-white me-2 m-auto toast-close-btn"></button>
39+
</div>
40+
`;
41+
42+
toastContainer.appendChild(toast);
43+
44+
// Auto remove after 4 seconds
45+
setTimeout(() => {
46+
if (toast.parentNode) {
47+
toast.classList.remove('show');
48+
setTimeout(() => toast.remove(), 300);
49+
}
50+
}, 4000);
51+
}
52+
53+
function createToastContainer() {
54+
const container = document.createElement('div');
55+
container.id = 'toast-container';
56+
container.className = 'toast-container position-fixed top-0 end-0 p-3';
57+
container.style.zIndex = '1055';
58+
document.body.appendChild(container);
59+
return container;
60+
}
61+
62+
async function summarizeNote() {
63+
const noteBody = document.getElementById('body').value;
64+
const summarizeButton = document.getElementById('summarizeButton');
65+
const summarySection = document.getElementById('summarySection');
66+
const summaryContent = document.getElementById('summaryContent');
67+
// Get noteId from global variable set by EJS template
68+
const noteId = window.NOTE_ID || document.body.dataset.noteId || window.location.pathname.split('/').pop();
69+
70+
if (!noteBody.trim()) {
71+
showToast('Please add some content to your note before summarizing.', 'warning');
72+
return;
73+
}
74+
75+
if (noteBody.trim().length < 50) {
76+
showToast('Note content is too short for meaningful summarization. Please add more content.', 'warning');
77+
return;
78+
}
79+
80+
// Update button state with loading animation
81+
const originalButtonContent = summarizeButton.innerHTML;
82+
summarizeButton.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Generating...';
83+
summarizeButton.disabled = true;
84+
85+
try {
86+
const response = await fetch('/dashboard/summarize', {
87+
method: 'POST',
88+
headers: {
89+
'Content-Type': 'application/json',
90+
},
91+
body: JSON.stringify({
92+
text: noteBody,
93+
noteId: noteId
94+
})
95+
});
96+
97+
if (!response.ok) {
98+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
99+
}
100+
101+
const data = await response.json();
102+
103+
if (data.success && data.summary && data.summary.trim()) {
104+
// Update summary content with fade effect
105+
summaryContent.style.opacity = '0.5';
106+
107+
setTimeout(() => {
108+
summaryContent.textContent = data.summary;
109+
summaryContent.style.opacity = '1';
110+
}, 200);
111+
112+
// Show summary section with slide animation
113+
if (summarySection.classList.contains('d-none')) {
114+
summarySection.classList.remove('d-none');
115+
summarySection.style.opacity = '0';
116+
summarySection.style.transform = 'translateY(-20px)';
117+
118+
setTimeout(() => {
119+
summarySection.style.transition = 'all 0.3s ease';
120+
summarySection.style.opacity = '1';
121+
summarySection.style.transform = 'translateY(0)';
122+
}, 10);
123+
}
124+
125+
// Smooth scroll to summary
126+
setTimeout(() => {
127+
summarySection.scrollIntoView({
128+
behavior: 'smooth',
129+
block: 'center',
130+
inline: 'nearest'
131+
});
132+
}, 300);
133+
134+
showToast('Summary generated and saved successfully!', 'success');
135+
} else {
136+
showToast(data.message || 'Failed to generate summary. Please try again.', 'danger');
137+
}
138+
} catch (error) {
139+
console.error('Summarization Error:', error);
140+
if (error.message.includes('HTML')) {
141+
showToast('Authentication error. Please refresh the page and try again.', 'danger');
142+
} else {
143+
showToast('Network error occurred. Please check your connection and try again.', 'danger');
144+
}
145+
} finally {
146+
// Restore button state
147+
summarizeButton.innerHTML = originalButtonContent;
148+
summarizeButton.disabled = false;
149+
}
150+
}

public/js/live-search.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@ document.addEventListener('DOMContentLoaded', function() {
1010
return; // Exit if elements don't exist
1111
}
1212

13+
// Add event delegation for search result clicks
14+
searchResults.addEventListener('click', function(e) {
15+
const searchResultItem = e.target.closest('.search-result-item[data-note-id]');
16+
const viewAllResults = e.target.closest('.view-all-results');
17+
18+
if (searchResultItem) {
19+
const noteId = searchResultItem.dataset.noteId;
20+
window.location.href = `/dashboard/item/${noteId}`;
21+
} else if (viewAllResults) {
22+
e.preventDefault();
23+
const searchTerm = viewAllResults.dataset.searchTerm;
24+
submitSearchForm(searchTerm);
25+
}
26+
});
27+
1328
// Function to perform live search
1429
function performLiveSearch(searchTerm) {
1530
if (searchTerm.length === 0) {
@@ -64,7 +79,7 @@ document.addEventListener('DOMContentLoaded', function() {
6479
const highlightedBody = highlightSearchTerm(truncatedBody, searchTerm);
6580

6681
html += `
67-
<div class="search-result-item p-3" onclick="window.location.href='/dashboard/item/${note._id}'">
82+
<div class="search-result-item p-3" data-note-id="${note._id}">
6883
<div class="d-flex justify-content-between align-items-start">
6984
<div class="flex-grow-1">
7085
<h6 class="search-result-title">${highlightedTitle}</h6>
@@ -85,7 +100,7 @@ document.addEventListener('DOMContentLoaded', function() {
85100
if (notes.length >= 10) {
86101
html += `
87102
<div class="search-result-item p-3 text-center border-top">
88-
<a href="/dashboard/search" class="text-primary text-decoration-none fw-semibold" onclick="submitSearchForm('${searchTerm}')">
103+
<a href="/dashboard/search" class="text-primary text-decoration-none fw-semibold view-all-results" data-search-term="${searchTerm}">
89104
View all results →
90105
</a>
91106
</div>

public/js/print-utils.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// public/js/print-utils.js
2+
3+
document.addEventListener('DOMContentLoaded', function() {
4+
const printButton = document.getElementById('printButton');
5+
if (printButton) {
6+
printButton.addEventListener('click', function() {
7+
window.print();
8+
});
9+
}
10+
});

views/dashboard/export-note.ejs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<div class="col text-end">
99
<!-- Navigation and print controls -->
1010
<a href="/dashboard" class="btn btn-outline-primary me-2">← Back to Dashboard</a>
11-
<button onclick="window.print()" class="btn btn-success">Print/Save as PDF</button>
11+
<button id="printButton" class="btn btn-success">Print/Save as PDF</button>
1212
</div>
1313
</div>
1414

views/dashboard/export.ejs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<div class="col text-end">
99
<!-- Navigation and print controls -->
1010
<a href="/dashboard" class="btn btn-outline-primary me-2">← Back to Dashboard</a>
11-
<button onclick="window.print()" class="btn btn-success">Print/Save as PDF</button>
11+
<button id="printButton" class="btn btn-success">Print/Save as PDF</button>
1212
</div>
1313
</div>
1414

0 commit comments

Comments
 (0)