diff --git a/frontend/index.html b/frontend/index.html
index 92a1978..b2e00af 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -698,6 +698,12 @@
// Remove trailing punctuation (period, comma, etc.)
transcript = transcript.replace(/[.,!?;:]+$/, '');
+ // Remove trailing "job" if present
+ transcript = transcript.replace(/\s+job$/i, '');
+
+ // Replace "new" with "latest"
+ transcript = transcript.replace(/\bnew\b/gi, 'latest');
+
// Capitalize each word (title case)
transcript = transcript.split(' ').map(word =>
word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
@@ -721,6 +727,16 @@
// Trigger search with results
applyFiltersAndRender();
+ // If exactly 1 result, automatically open Manager Info
+ if (visibleJobs.length === 1) {
+ const job = visibleJobs[0];
+ currentJob = job;
+ detailModal.classList.remove('hidden');
+ document.getElementById('detailBox').scrollTop = 0;
+ switchTab('managerInfo');
+ populateManagerInfo(job);
+ }
+
// Check if all results are final, if so prepare to stop
const allFinal = Array.from(event.results).every(r => r.isFinal);
if (allFinal && event.results.length > 0) {
@@ -770,6 +786,12 @@
return;
}
+ // Check if text is highlighted and clear it if so
+ if (searchBox.selectionStart === 0 && searchBox.selectionEnd === searchBox.value.length && searchBox.value.length > 0) {
+ searchBox.value = '';
+ baseSearchText = '';
+ }
+
// Request permission first time
if (!hasPermission) {
const granted = await requestMicPermission();
@@ -1314,102 +1336,6 @@
row5.appendChild(estimatedEndDate);
managerInfoTable.appendChild(row5);
- // Links Section: Plans, Estimates, Documents & Files
- if(job.Job_Folder_Link && String(job.Job_Folder_Link).trim() !== ''){
- try {
- const files = await fetchJobFiles(job);
-
- // Categorize files
- const plans = [];
- const estimates = [];
- const documentsAndFiles = [];
-
- files.forEach(file => {
- const name = file.name.toLowerCase();
- if(name.includes('plan') || name.includes('drawing') || name.includes('dwg')) {
- plans.push(file);
- } else if(name.includes('estimate') || name.includes('est')) {
- estimates.push(file);
- } else {
- documentsAndFiles.push(file);
- }
- });
-
- // Links Section Container
- const linksSection = document.createElement('div');
- linksSection.className = 'mt-4 pb-3 border-b border-gray-200';
- linksSection.innerHTML = `
Links
`;
-
- const linksGrid = document.createElement('div');
- linksGrid.className = 'grid grid-cols-3 gap-4';
-
- // Plans column
- const plansCol = document.createElement('div');
- plansCol.innerHTML = `Plans
`;
- const plansList = document.createElement('div');
- plansList.className = 'space-y-1';
- if(plans.length > 0) {
- plans.forEach(file => {
- const fileItem = document.createElement('div');
- fileItem.className = 'text-blue-600 hover:text-blue-800 cursor-pointer underline';
- fileItem.style.fontSize = '10px';
- fileItem.textContent = file.name;
- fileItem.onclick = () => openFilePreview(file);
- plansList.appendChild(fileItem);
- });
- } else {
- plansList.innerHTML = `No plans
`;
- }
- plansCol.appendChild(plansList);
-
- // Estimates column
- const estimatesCol = document.createElement('div');
- estimatesCol.innerHTML = `Estimates
`;
- const estimatesList = document.createElement('div');
- estimatesList.className = 'space-y-1';
- if(estimates.length > 0) {
- estimates.forEach(file => {
- const fileItem = document.createElement('div');
- fileItem.className = 'text-blue-600 hover:text-blue-800 cursor-pointer underline';
- fileItem.style.fontSize = '10px';
- fileItem.textContent = file.name;
- fileItem.onclick = () => openFilePreview(file);
- estimatesList.appendChild(fileItem);
- });
- } else {
- estimatesList.innerHTML = `No estimates
`;
- }
- estimatesCol.appendChild(estimatesList);
-
- // Documents & Files column
- const docsCol = document.createElement('div');
- docsCol.innerHTML = `Documents & Files
`;
- const docsList = document.createElement('div');
- docsList.className = 'space-y-1';
- if(documentsAndFiles.length > 0) {
- documentsAndFiles.forEach(file => {
- const fileItem = document.createElement('div');
- fileItem.className = 'text-blue-600 hover:text-blue-800 cursor-pointer underline';
- fileItem.style.fontSize = '10px';
- fileItem.textContent = file.name;
- fileItem.onclick = () => openFilePreview(file);
- docsList.appendChild(fileItem);
- });
- } else {
- docsList.innerHTML = `No documents
`;
- }
- docsCol.appendChild(docsList);
-
- linksGrid.appendChild(plansCol);
- linksGrid.appendChild(estimatesCol);
- linksGrid.appendChild(docsCol);
- linksSection.appendChild(linksGrid);
- managerInfoTable.appendChild(linksSection);
- } catch(err) {
- console.log('Error loading files for Manager Info:', err);
- }
- }
-
// Notes Section with Tabs
const notesSection = document.createElement('div');
notesSection.className = 'mt-4';
@@ -1439,12 +1365,21 @@
jobNotesContent.id = 'jobNotesContent';
jobNotesContent.className = 'notes-tab-content';
+ // Container for display with expand button
+ const jobNotesDisplayContainer = document.createElement('div');
+ jobNotesDisplayContainer.className = 'relative mb-3';
+
// Display notes first
const jobNotesDisplay = document.createElement('div');
- jobNotesDisplay.className = 'text-gray-600 whitespace-pre-wrap break-words p-2 border border-gray-300 rounded bg-gray-50 min-h-[80px] mb-3';
+ jobNotesDisplay.id = 'jobNotesDisplay';
+ jobNotesDisplay.className = 'text-gray-600 whitespace-pre-wrap break-words p-2 border border-gray-300 rounded bg-gray-50 overflow-y-auto';
jobNotesDisplay.style.fontSize = '10px';
+ jobNotesDisplay.style.height = '120px';
+ jobNotesDisplay.style.maxHeight = '120px';
jobNotesDisplay.innerHTML = job.Notes || 'No notes available';
- jobNotesContent.appendChild(jobNotesDisplay);
+ jobNotesDisplayContainer.appendChild(jobNotesDisplay);
+
+ jobNotesContent.appendChild(jobNotesDisplayContainer);
// Add Note button below notes display
const addNoteBtn = document.createElement('button');
@@ -1509,12 +1444,21 @@
managerNotesContent.id = 'managerNotesContent';
managerNotesContent.className = 'notes-tab-content hidden';
+ // Container for display with expand button
+ const managerNotesDisplayContainer = document.createElement('div');
+ managerNotesDisplayContainer.className = 'relative mb-3';
+
// Display manager notes first
const managerNotesDisplay = document.createElement('div');
- managerNotesDisplay.className = 'text-gray-600 whitespace-pre-wrap break-words p-2 border border-gray-300 rounded bg-gray-50 min-h-[80px] mb-3';
+ managerNotesDisplay.id = 'managerNotesDisplay';
+ managerNotesDisplay.className = 'text-gray-600 whitespace-pre-wrap break-words p-2 border border-gray-300 rounded bg-gray-50 overflow-y-auto';
managerNotesDisplay.style.fontSize = '10px';
+ managerNotesDisplay.style.height = '120px';
+ managerNotesDisplay.style.maxHeight = '120px';
managerNotesDisplay.innerHTML = 'No manager notes available';
- managerNotesContent.appendChild(managerNotesDisplay);
+ managerNotesDisplayContainer.appendChild(managerNotesDisplay);
+
+ managerNotesContent.appendChild(managerNotesDisplayContainer);
// Add Note button for manager below notes display
const addManagerNoteBtn = document.createElement('button');
@@ -1899,7 +1843,7 @@
});
const currentUserName = getDisplayName();
const noteType = isManagerNote ? 'manager' : 'job';
- const typeLabel = isManagerNote ? '[MANAGER NOTE]' : '';
+ const typeLabel = isManagerNote ? '[MANAGER]' : '';
const formatted = `${typeLabel}[${currentUserName} - ${timestamp}] ${html}`;
try {
@@ -2008,8 +1952,8 @@
const jobNotes = allNotes.filter(n => !n.type || n.type === 'job');
const managerNotes = allNotes.filter(n => n.type === 'manager');
- // Update job notes display
- const jobNotesDisplay = document.querySelector('#jobNotesContent > div:first-child');
+ // Update job notes display using ID selector
+ const jobNotesDisplay = document.getElementById('jobNotesDisplay');
if(jobNotesDisplay) {
if(jobNotes.length > 0) {
jobNotesDisplay.innerHTML = jobNotes.map(n => n.Note).join('\n\n');
@@ -2018,8 +1962,8 @@
}
}
- // Update manager notes display
- const managerNotesDisplay = document.querySelector('#managerNotesContent > div:first-child');
+ // Update manager notes display using ID selector
+ const managerNotesDisplay = document.getElementById('managerNotesDisplay');
if(managerNotesDisplay) {
if(managerNotes.length > 0) {
managerNotesDisplay.innerHTML = managerNotes.map(n => n.Note).join('\n\n');
@@ -2061,6 +2005,7 @@
otherFiles.push(file);
}
});
+ console.log('Categorized - Plans:', plans.length, 'Estimates:', estimates.length, 'Other:', otherFiles.length);
}
// Plans column
@@ -2074,10 +2019,11 @@
fileItem.className = 'flex items-center gap-1 text-blue-600 hover:text-blue-800 cursor-pointer';
fileItem.style.fontSize = '10px';
- // File icon
- const icon = document.createElement('span');
- icon.textContent = '📄';
- fileItem.appendChild(icon);
+ // File icon using getFileIcon
+ const iconWrapper = document.createElement('span');
+ iconWrapper.className = 'flex-shrink-0';
+ iconWrapper.innerHTML = getFileIcon(file.name, file.contentType);
+ fileItem.appendChild(iconWrapper);
const fileName = document.createElement('span');
fileName.textContent = file.name;
@@ -2103,10 +2049,11 @@
fileItem.className = 'flex items-center gap-1 text-blue-600 hover:text-blue-800 cursor-pointer';
fileItem.style.fontSize = '10px';
- // File icon
- const icon = document.createElement('span');
- icon.textContent = '📊';
- fileItem.appendChild(icon);
+ // File icon using getFileIcon
+ const iconWrapper = document.createElement('span');
+ iconWrapper.className = 'flex-shrink-0';
+ iconWrapper.innerHTML = getFileIcon(file.name, file.contentType);
+ fileItem.appendChild(iconWrapper);
const fileName = document.createElement('span');
fileName.textContent = file.name;
@@ -2132,19 +2079,11 @@
fileItem.className = 'flex items-center gap-1 text-blue-600 hover:text-blue-800 cursor-pointer';
fileItem.style.fontSize = '10px';
- // File icon based on extension
- const ext = file.name.split('.').pop().toLowerCase();
- const icon = document.createElement('span');
- if(['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(ext)) {
- icon.textContent = '🖼️';
- } else if(['pdf'].includes(ext)) {
- icon.textContent = '📕';
- } else if(['doc', 'docx'].includes(ext)) {
- icon.textContent = '📝';
- } else {
- icon.textContent = '📄';
- }
- fileItem.appendChild(icon);
+ // File icon using getFileIcon
+ const iconWrapper = document.createElement('span');
+ iconWrapper.className = 'flex-shrink-0';
+ iconWrapper.innerHTML = getFileIcon(file.name, file.contentType);
+ fileItem.appendChild(iconWrapper);
const fileName = document.createElement('span');
fileName.textContent = file.name;
@@ -2410,28 +2349,28 @@
const type = (contentType || '').toLowerCase();
if (ext === 'pdf' || type.includes('pdf'))
- return '';
+ return '';
// Show Word icon with PDF badge to indicate it will be converted
if (['doc', 'docx'].includes(ext) || type.includes('word'))
- return '';
+ return '';
if (['xls', 'xlsx'].includes(ext) || type.includes('excel') || type.includes('spreadsheet'))
- return '';
+ return '';
if (['ppt', 'pptx'].includes(ext) || type.includes('powerpoint') || type.includes('presentation'))
- return '';
+ return '';
if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg'].includes(ext) || type.includes('image'))
- return '';
+ return '';
if (['zip', 'rar', '7z', 'tar', 'gz'].includes(ext) || type.includes('zip') || type.includes('compressed'))
- return '';
+ return '';
if (['txt', 'csv', 'log'].includes(ext) || type.includes('text'))
- return '';
+ return '';
- return '';
+ return '';
};
const canPreviewInline = (name, contentType) => {
@@ -3069,6 +3008,17 @@
const prefs = await loadUserPreferences();
console.log('Init: loaded prefs=', prefs);
applyPrefsToFilters(prefs);
+
+ // Proactively refresh Graph token on load
+ console.log('Init: refreshing Graph token');
+ await refreshGraphToken();
+
+ // Set up background token refresh every 50 minutes (tokens typically expire after 60 min)
+ setInterval(async () => {
+ console.log('Background: refreshing Graph token');
+ await refreshGraphToken();
+ }, 50 * 60 * 1000); // 50 minutes
+
console.log('Init: about to fetch jobs');
fetchAllJobs();
})();