diff --git a/frontend/index.html b/frontend/index.html
index 22de787..1b8d2d6 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -159,6 +159,14 @@
+
+
+
+
@@ -1122,6 +1131,810 @@
newNote.innerHTML = '';
tbButtons.forEach(b=>b.classList.remove('bg-blue-100', 'text-gray-900', 'border-blue-600'));
}
+
+ async function populateManagerInfo(job) {
+ const managerInfoTable = document.getElementById('managerInfoTable');
+ managerInfoTable.innerHTML = '';
+
+ // Row 1: Job Full Name (spans 2 cols), Manager, Job Status
+ const row1 = document.createElement('div');
+ row1.className = 'grid grid-cols-4 gap-4 pb-3 border-b border-gray-200';
+
+ const jobFullNameDiv = document.createElement('div');
+ jobFullNameDiv.className = 'col-span-2';
+ jobFullNameDiv.innerHTML = `Job Full Name
${job.Job_Full_Name || '—'}
`;
+
+ const managerDiv = document.createElement('div');
+ managerDiv.innerHTML = `Manager
${job.Manager || '—'}
`;
+
+ const jobStatusDiv = document.createElement('div');
+ jobStatusDiv.innerHTML = `Job Status
${job.Job_Status || '—'}
`;
+
+ row1.appendChild(jobFullNameDiv);
+ row1.appendChild(managerDiv);
+ row1.appendChild(jobStatusDiv);
+ managerInfoTable.appendChild(row1);
+
+ // Row 2: Job Number, Job Division, Job Type, Scope
+ const row2 = document.createElement('div');
+ row2.className = 'grid grid-cols-4 gap-4 pb-3 border-b border-gray-200';
+
+ const jobNum = document.createElement('div');
+ jobNum.innerHTML = `Job Number
${job.Job_Number || '—'}
`;
+
+ const jobDivision = document.createElement('div');
+ jobDivision.innerHTML = `Job Division
${job.Job_Division || '—'}
`;
+
+ const jobType = document.createElement('div');
+ jobType.innerHTML = `Job Type
${job.Job_Type || '—'}
`;
+
+ const scopeDiv = document.createElement('div');
+ scopeDiv.innerHTML = `Scope
—
`;
+
+ row2.appendChild(jobNum);
+ row2.appendChild(jobDivision);
+ row2.appendChild(jobType);
+ row2.appendChild(scopeDiv);
+ managerInfoTable.appendChild(row2);
+
+ // Row 3: Company/Client, Contact Person, Contact Number, EXT.
+ const row3 = document.createElement('div');
+ row3.className = 'grid grid-cols-4 gap-4 pb-3 border-b border-gray-200';
+
+ const companyClient = document.createElement('div');
+ companyClient.innerHTML = `Company/Client
${job.Company_Client || '—'}
`;
+
+ const contactPerson = document.createElement('div');
+ contactPerson.innerHTML = `Contact Person
${job.Contact_Person || '—'}
`;
+
+ const contactNumber = document.createElement('div');
+ contactNumber.innerHTML = `Contact Number
`;
+ const phoneDiv = document.createElement('div');
+ phoneDiv.className = 'text-center';
+ phoneDiv.style.fontSize = '10px';
+ if(job.Phone_Number && job.Phone_Number.trim() !== ''){
+ const phoneLink = document.createElement('a');
+ const phoneNumber = job.Phone_Number.replace(/\D/g, '');
+ phoneLink.href = `tel:${phoneNumber}`;
+ phoneLink.className = 'text-blue-600 hover:text-blue-800 underline';
+ phoneLink.textContent = job.Phone_Number;
+ phoneDiv.appendChild(phoneLink);
+ } else {
+ phoneDiv.className = 'text-gray-900 text-center';
+ phoneDiv.textContent = '—';
+ }
+ contactNumber.appendChild(phoneDiv);
+
+ const extn = document.createElement('div');
+ extn.innerHTML = `EXT.
—
`;
+
+ row3.appendChild(companyClient);
+ row3.appendChild(contactPerson);
+ row3.appendChild(contactNumber);
+ row3.appendChild(extn);
+ managerInfoTable.appendChild(row3);
+
+ // Row 4: Job Address (spans 2 cols), Map Icons (3rd col), EST Cost (4th col)
+ const row4 = document.createElement('div');
+ row4.className = 'grid grid-cols-4 gap-4 pb-3 border-b border-gray-200';
+
+ // Job Address spanning 2 columns
+ const addressContainer = document.createElement('div');
+ addressContainer.className = 'col-span-2';
+ addressContainer.innerHTML = `Job Address
`;
+ const addressVal = document.createElement('div');
+ addressVal.style.fontSize = '10px';
+ addressVal.className = 'text-gray-900';
+ addressVal.textContent = job.Job_Address || '—';
+ addressContainer.appendChild(addressVal);
+
+ // Map Icons in 3rd column
+ const mapIconsContainer = document.createElement('div');
+ mapIconsContainer.className = 'flex items-end gap-2';
+
+ if(job.Job_Address && job.Job_Address.trim() !== ''){
+ const appleLink = document.createElement('a');
+ const encodedAddress = encodeURIComponent(job.Job_Address);
+ appleLink.href = `https://maps.apple.com/?q=${encodedAddress}`;
+ appleLink.target = '_blank';
+ appleLink.className = 'inline-flex items-center justify-center w-5 h-5 hover:opacity-80 transition-opacity';
+ appleLink.title = 'Open in Apple Maps';
+ appleLink.innerHTML = '';
+ mapIconsContainer.appendChild(appleLink);
+
+ const googleLink = document.createElement('a');
+ googleLink.href = `https://www.google.com/maps/search/?api=1&query=${encodedAddress}`;
+ googleLink.target = '_blank';
+ googleLink.className = 'inline-flex items-center justify-center w-5 h-5 hover:opacity-80 transition-opacity';
+ googleLink.title = 'Open in Google Maps';
+ googleLink.innerHTML = '';
+ mapIconsContainer.appendChild(googleLink);
+ }
+
+ // EST Cost in 4th column (centered)
+ const estimatedCostContainer = document.createElement('div');
+ estimatedCostContainer.innerHTML = `EST Cost
—
`;
+
+ row4.appendChild(addressContainer);
+ row4.appendChild(mapIconsContainer);
+ row4.appendChild(estimatedCostContainer);
+ managerInfoTable.appendChild(row4);
+
+ // Row 5: EST Start Date, Actual Start Date, EST End Date
+ const row5 = document.createElement('div');
+ row5.className = 'grid grid-cols-3 gap-4 pb-3 border-b border-gray-200';
+
+ // Format the start date for both estimated and actual (short format)
+ let startDateFormatted = '—';
+ if(job.Start_Date){
+ try {
+ const date = new Date(job.Start_Date);
+ startDateFormatted = date.toLocaleDateString('en-US', { year: 'numeric', month: '2-digit', day: '2-digit' });
+ } catch(e) {
+ startDateFormatted = job.Start_Date;
+ }
+ }
+
+ const estimatedStartDate = document.createElement('div');
+ estimatedStartDate.innerHTML = `EST Start Date
${startDateFormatted}
`;
+
+ const actualStartDate = document.createElement('div');
+ actualStartDate.innerHTML = `Actual Start Date
${startDateFormatted}
`;
+
+ const estimatedEndDate = document.createElement('div');
+ estimatedEndDate.innerHTML = `EST End Date
—
`;
+
+ row5.appendChild(estimatedStartDate);
+ row5.appendChild(actualStartDate);
+ 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';
+
+ // Tab buttons
+ const notesTabs = document.createElement('div');
+ notesTabs.className = 'flex gap-2 mb-2';
+
+ const jobNotesTabBtn = document.createElement('button');
+ jobNotesTabBtn.id = 'jobNotesTab';
+ jobNotesTabBtn.className = 'px-3 py-1 font-bold uppercase bg-orange-600 text-white rounded cursor-pointer';
+ jobNotesTabBtn.style.fontSize = '8px';
+ jobNotesTabBtn.textContent = 'Notes';
+
+ const managerNotesTabBtn = document.createElement('button');
+ managerNotesTabBtn.id = 'managerNotesTab';
+ managerNotesTabBtn.className = 'px-3 py-1 font-bold uppercase bg-gray-300 text-gray-700 rounded hover:bg-gray-400 cursor-pointer';
+ managerNotesTabBtn.style.fontSize = '8px';
+ managerNotesTabBtn.textContent = 'Manager Notes';
+
+ notesTabs.appendChild(jobNotesTabBtn);
+ notesTabs.appendChild(managerNotesTabBtn);
+ notesSection.appendChild(notesTabs);
+
+ // Job Notes Content
+ const jobNotesContent = document.createElement('div');
+ jobNotesContent.id = 'jobNotesContent';
+ jobNotesContent.className = 'notes-tab-content';
+
+ // 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.style.fontSize = '10px';
+ jobNotesDisplay.innerHTML = job.Notes || 'No notes available';
+ jobNotesContent.appendChild(jobNotesDisplay);
+
+ // Add Note button below notes display
+ const addNoteBtn = document.createElement('button');
+ addNoteBtn.className = 'mb-2 px-3 py-1 bg-green-600 text-white rounded font-bold hover:bg-green-700 cursor-pointer';
+ addNoteBtn.style.fontSize = '8px';
+ addNoteBtn.textContent = 'Add A Note';
+ jobNotesContent.appendChild(addNoteBtn);
+
+ // Note editor (collapsed by default)
+ const noteEditor = document.createElement('div');
+ noteEditor.id = 'jobNoteEditor';
+ noteEditor.className = 'hidden mb-3 p-2 border border-gray-300 rounded bg-gray-50';
+ noteEditor.innerHTML = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ jobNotesContent.appendChild(noteEditor);
+
+ // File columns - always create them
+ const fileColumns = document.createElement('div');
+ fileColumns.id = 'jobNotesFileColumns';
+ fileColumns.className = 'grid grid-cols-3 gap-4 mt-3';
+
+ // Initialize with empty state
+ fileColumns.innerHTML = `
+
+
+
Estimates
+
Loading...
+
+
+
Other Files
+
Loading...
+
+ `;
+ jobNotesContent.appendChild(fileColumns);
+
+ // Manager Notes Content
+ const managerNotesContent = document.createElement('div');
+ managerNotesContent.id = 'managerNotesContent';
+ managerNotesContent.className = 'notes-tab-content hidden';
+
+ // 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.style.fontSize = '10px';
+ managerNotesDisplay.innerHTML = 'No manager notes available';
+ managerNotesContent.appendChild(managerNotesDisplay);
+
+ // Add Note button for manager below notes display
+ const addManagerNoteBtn = document.createElement('button');
+ addManagerNoteBtn.className = 'mb-2 px-3 py-1 bg-green-600 text-white rounded font-bold hover:bg-green-700 cursor-pointer';
+ addManagerNoteBtn.style.fontSize = '8px';
+ addManagerNoteBtn.textContent = 'Add A Note';
+ managerNotesContent.appendChild(addManagerNoteBtn);
+
+ // Manager note editor (collapsed by default)
+ const managerNoteEditor = document.createElement('div');
+ managerNoteEditor.id = 'managerNoteEditor';
+ managerNoteEditor.className = 'hidden mb-3 p-2 border border-gray-300 rounded bg-gray-50';
+ managerNoteEditor.innerHTML = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ managerNotesContent.appendChild(managerNoteEditor);
+
+ // File columns for manager notes - always create them
+ const managerFileColumns = document.createElement('div');
+ managerFileColumns.id = 'managerNotesFileColumns';
+ managerFileColumns.className = 'grid grid-cols-3 gap-4 mt-3';
+
+ // Initialize with empty state
+ managerFileColumns.innerHTML = `
+
+
+
Estimates
+
Loading...
+
+
+
Other Files
+
Loading...
+
+ `;
+ managerNotesContent.appendChild(managerFileColumns);
+
+ notesSection.appendChild(jobNotesContent);
+ notesSection.appendChild(managerNotesContent);
+ managerInfoTable.appendChild(notesSection);
+
+ // Tab switching event listeners
+ jobNotesTabBtn.addEventListener('click', (e) => {
+ console.log('Notes tab clicked');
+ e.preventDefault();
+ e.stopPropagation();
+ jobNotesTabBtn.className = 'px-3 py-1 text-xs font-bold uppercase bg-orange-600 text-white rounded cursor-pointer';
+ managerNotesTabBtn.className = 'px-3 py-1 text-xs font-bold uppercase bg-gray-300 text-gray-700 rounded hover:bg-gray-400 cursor-pointer';
+ jobNotesContent.classList.remove('hidden');
+ managerNotesContent.classList.add('hidden');
+ });
+
+ managerNotesTabBtn.addEventListener('click', (e) => {
+ console.log('Manager Notes tab clicked');
+ e.preventDefault();
+ e.stopPropagation();
+ managerNotesTabBtn.className = 'px-3 py-1 text-xs font-bold uppercase bg-orange-600 text-white rounded cursor-pointer';
+ jobNotesTabBtn.className = 'px-3 py-1 text-xs font-bold uppercase bg-gray-300 text-gray-700 rounded hover:bg-gray-400 cursor-pointer';
+ managerNotesContent.classList.remove('hidden');
+ jobNotesContent.classList.add('hidden');
+ });
+
+ // Note editor controls
+ addNoteBtn.addEventListener('click', (e) => {
+ console.log('Add Note clicked');
+ e.preventDefault();
+ addNoteBtn.classList.add('hidden');
+ noteEditor.classList.remove('hidden');
+ });
+
+ addManagerNoteBtn.addEventListener('click', (e) => {
+ console.log('Add Manager Note clicked');
+ e.preventDefault();
+ addManagerNoteBtn.classList.add('hidden');
+ managerNoteEditor.classList.remove('hidden');
+ });
+
+ // Populate file columns
+ if(job.Job_Folder_Link && String(job.Job_Folder_Link).trim() !== ''){
+ console.log('Fetching files for notes sections...');
+ loadFilesForNotes(job.Job_Folder_Link).then(files => {
+ console.log('Files fetched, populating columns:', files.length);
+ populateNotesFileColumns(files, 'jobNotesFileColumns');
+ populateNotesFileColumns(files, 'managerNotesFileColumns');
+ }).catch(err => {
+ console.log('Error loading files for notes:', err);
+ // Show empty state on error
+ populateNotesFileColumns([], 'jobNotesFileColumns');
+ populateNotesFileColumns([], 'managerNotesFileColumns');
+ });
+ } else {
+ console.log('No Job_Folder_Link, showing empty state');
+ // Show empty state when no folder link
+ populateNotesFileColumns([], 'jobNotesFileColumns');
+ populateNotesFileColumns([], 'managerNotesFileColumns');
+ }
+
+ // Setup toolbar functionality for job notes editor
+ setupNoteEditorToolbar('jobNewNote', 'jobNoteEditor');
+
+ // Setup toolbar functionality for manager notes editor
+ setupNoteEditorToolbar('managerNewNote', 'managerNoteEditor');
+
+ // Setup close functionality
+ document.getElementById('jobNoteCollapse').addEventListener('click', (e) => {
+ console.log('Close job note editor');
+ e.preventDefault();
+ noteEditor.classList.add('hidden');
+ addNoteBtn.classList.remove('hidden');
+ });
+
+ document.getElementById('managerNoteCollapse').addEventListener('click', (e) => {
+ console.log('Close manager note editor');
+ e.preventDefault();
+ managerNoteEditor.classList.add('hidden');
+ addManagerNoteBtn.classList.remove('hidden');
+ });
+
+ // Setup submit functionality
+ document.getElementById('jobNoteSubmit').addEventListener('click', async(e) => {
+ e.preventDefault();
+ await submitNote('jobNewNote', job, false);
+ noteEditor.classList.add('hidden');
+ addNoteBtn.classList.remove('hidden');
+ });
+
+ document.getElementById('jobNoteClear').addEventListener('click', (e) => {
+ e.preventDefault();
+ document.getElementById('jobNewNote').innerHTML = '';
+ });
+
+ document.getElementById('managerNoteSubmit').addEventListener('click', async(e) => {
+ e.preventDefault();
+ await submitNote('managerNewNote', job, true);
+ managerNoteEditor.classList.add('hidden');
+ addManagerNoteBtn.classList.remove('hidden');
+ });
+
+ document.getElementById('managerNoteClear').addEventListener('click', (e) => {
+ e.preventDefault();
+ document.getElementById('managerNewNote').innerHTML = '';
+ });
+ }
+
+ // Load files for notes sections
+ async function loadFilesForNotes(folderLink) {
+ console.log('loadFilesForNotes called with:', folderLink);
+
+ const graphToken = await ensureGraphToken();
+ const headers = graphToken ? { 'x-graph-token': graphToken } : {};
+
+ try {
+ const resp = await fetch(`/api/job-files?link=${encodeURIComponent(folderLink)}`, { headers });
+
+ if(!resp.ok) {
+ console.error('Failed to fetch files:', resp.status, resp.statusText);
+ return [];
+ }
+
+ const data = await resp.json();
+ console.log('Files loaded:', data.items?.length || 0);
+ return data.items || [];
+ } catch(err) {
+ console.error('Error in loadFilesForNotes:', err);
+ return [];
+ }
+ }
+
+ // Setup toolbar for note editors
+ function setupNoteEditorToolbar(editorId, containerId) {
+ const editor = document.getElementById(editorId);
+ const container = document.getElementById(containerId);
+ if(!editor || !container) return;
+
+ let savedSelection = null;
+ function saveSelection() {
+ const sel = window.getSelection();
+ if(sel.rangeCount > 0) savedSelection = sel.getRangeAt(0);
+ }
+ function restoreSelection() {
+ const sel = window.getSelection();
+ if(savedSelection) {
+ sel.removeAllRanges();
+ sel.addRange(savedSelection);
+ }
+ }
+
+ editor.addEventListener('mouseup', saveSelection);
+ editor.addEventListener('keyup', saveSelection);
+ editor.addEventListener('focus', saveSelection);
+
+ const buttons = container.querySelectorAll('[data-cmd]');
+ buttons.forEach(btn => {
+ btn.addEventListener('mousedown', e => {
+ e.preventDefault();
+ restoreSelection();
+ const cmd = btn.dataset.cmd;
+ if(!cmd) return;
+
+ if(cmd === 'foreColor') {
+ const val = btn.dataset.val;
+ document.execCommand(cmd, false, val);
+ buttons.forEach(b => {
+ if(b.dataset.cmd === 'foreColor' && b !== btn) {
+ b.classList.remove('bg-blue-100', 'text-gray-900', 'border-blue-600');
+ }
+ });
+ btn.classList.toggle('bg-blue-100');
+ btn.classList.toggle('text-gray-900');
+ btn.classList.toggle('border-blue-600');
+ } else if(cmd === 'removeFormat') {
+ restoreSelection();
+ const sel = window.getSelection();
+ if(sel.rangeCount > 0) {
+ const range = sel.getRangeAt(0);
+ let content = range.toString();
+ range.deleteContents();
+ range.insertNode(document.createTextNode(content));
+ range.setStartAfter(range.startContainer);
+ range.collapse(true);
+ sel.removeAllRanges();
+ sel.addRange(range);
+ }
+ buttons.forEach(b => b.classList.remove('bg-blue-100', 'text-gray-900', 'border-blue-600'));
+ } else {
+ document.execCommand(cmd, false, null);
+ btn.classList.toggle('bg-blue-100');
+ btn.classList.toggle('text-gray-900');
+ btn.classList.toggle('border-blue-600');
+ }
+
+ editor.focus();
+ saveSelection();
+ });
+ });
+ }
+
+ // Submit note function
+ async function submitNote(editorId, job, isManagerNote) {
+ const editor = document.getElementById(editorId);
+ const html = editor.innerHTML.trim();
+ if(!html) {
+ alert('Enter note text');
+ return;
+ }
+
+ const now = new Date();
+ const timestamp = now.toLocaleString('en-US', {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ hour12: true
+ });
+ const currentUserName = getDisplayName();
+ const noteType = isManagerNote ? '[MANAGER NOTE]' : '';
+ const formatted = `${noteType}[${currentUserName} - ${timestamp}] [Job #${job?.Job_Number||'N/A'}] ${html}\n\n`;
+ const updatedNotes = formatted + (job.Notes || '');
+
+ try {
+ const payloadNote = {
+ Note: formatted.trim(),
+ Job_Number: job?.Job_Number || '',
+ Job_Number_Id: job?.id ? [job.id] : []
+ };
+
+ // Update job record
+ const resJob = await fetch(`${PB_COLLECTION_URL}/${job.id}`, {
+ method: 'PATCH',
+ headers: {'Content-Type': 'application/json', ...authHeaders()},
+ body: JSON.stringify({Notes: updatedNotes})
+ });
+
+ if(!resJob.ok) throw new Error('Job_Info update failed: ' + resJob.status);
+ const updated = await resJob.json();
+ const jobIdToLink = updated.id || job.id;
+
+ // Create note in notes collection
+ payloadNote.Job_Number_Id = [jobIdToLink];
+ const resNote = await fetch(`${NOTES_COLLECTION_URL}`, {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json', ...authHeaders()},
+ body: JSON.stringify(payloadNote)
+ });
+
+ if(!resNote.ok) {
+ console.warn('Note creation failed with status', resNote.status);
+ }
+
+ // Update UI
+ currentJob.Notes = updatedNotes;
+ editor.innerHTML = '';
+ alert('Note saved successfully!');
+
+ // Refresh the display
+ if(currentJob) {
+ populateManagerInfo(currentJob);
+ }
+ } catch(err) {
+ console.error('Error saving note:', err);
+ alert('Failed to save note: ' + err.message);
+ }
+ }
+
+ // Function to populate file columns in notes section
+ function populateNotesFileColumns(files, containerId) {
+ const container = document.getElementById(containerId);
+ if(!container) {
+ console.log('Container not found:', containerId);
+ return;
+ }
+
+ console.log('Populating', containerId, 'with', files.length, 'files');
+ container.innerHTML = '';
+
+ // Categorize files
+ const plans = [];
+ const estimates = [];
+ const otherFiles = [];
+
+ if(files && files.length > 0) {
+ 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 {
+ otherFiles.push(file);
+ }
+ });
+ }
+
+ // 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 = '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);
+
+ const fileName = document.createElement('span');
+ fileName.textContent = file.name;
+ fileName.className = 'truncate';
+ fileItem.appendChild(fileName);
+
+ 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 = '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);
+
+ const fileName = document.createElement('span');
+ fileName.textContent = file.name;
+ fileName.className = 'truncate';
+ fileItem.appendChild(fileName);
+
+ fileItem.onclick = () => openFilePreview(file);
+ estimatesList.appendChild(fileItem);
+ });
+ } else {
+ estimatesList.innerHTML = `No estimates
`;
+ }
+ estimatesCol.appendChild(estimatesList);
+
+ // Other Files column
+ const otherCol = document.createElement('div');
+ otherCol.innerHTML = `Other Files
`;
+ const otherList = document.createElement('div');
+ otherList.className = 'space-y-1';
+ if(otherFiles.length > 0) {
+ otherFiles.forEach(file => {
+ const fileItem = document.createElement('div');
+ 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);
+
+ const fileName = document.createElement('span');
+ fileName.textContent = file.name;
+ fileName.className = 'truncate';
+ fileItem.appendChild(fileName);
+
+ fileItem.onclick = () => openFilePreview(file);
+ otherList.appendChild(fileItem);
+ });
+ } else {
+ otherList.innerHTML = `No other files
`;
+ }
+ otherCol.appendChild(otherList);
+
+ container.appendChild(plansCol);
+ container.appendChild(estimatesCol);
+ container.appendChild(otherCol);
+ }
+
const closeModal = ()=>{ detailModal.classList.add('hidden'); renderCards(visibleJobs); };
document.getElementById('btnClose').addEventListener('click',closeModal);
document.getElementById('backBtn').addEventListener('click',closeModal);
@@ -1130,30 +1943,53 @@
function switchTab(tab){
const infoTab = document.getElementById('infoTab');
const notesTab = document.getElementById('notesTab');
+ const managerInfoTab = document.getElementById('managerInfoTab');
const infoContent = document.getElementById('infoContent');
const notesContent = document.getElementById('notesContent');
+ const managerInfoContent = document.getElementById('managerInfoContent');
const infoHeading = document.getElementById('infoHeading');
const notesHeading = document.getElementById('notesHeading');
+ const managerInfoHeading = document.getElementById('managerInfoHeading');
if(tab === 'info'){
infoTab.className = 'px-4 py-2 text-sm font-medium text-blue-600 border-b-2 border-blue-600 -mb-px';
notesTab.className = 'px-4 py-2 text-sm font-medium text-gray-500 hover:text-gray-700';
+ managerInfoTab.className = 'px-4 py-2 text-sm font-medium text-gray-500 hover:text-gray-700';
infoContent.classList.remove('hidden');
notesContent.classList.add('hidden');
+ managerInfoContent.classList.add('hidden');
if(infoHeading) infoHeading.classList.remove('hidden');
if(notesHeading) notesHeading.classList.add('hidden');
- } else {
+ if(managerInfoHeading) managerInfoHeading.classList.add('hidden');
+ } else if(tab === 'notes') {
infoTab.className = 'px-4 py-2 text-sm font-medium text-gray-500 hover:text-gray-700';
notesTab.className = 'px-4 py-2 text-sm font-medium text-orange-600 border-b-2 border-orange-600 -mb-px';
+ managerInfoTab.className = 'px-4 py-2 text-sm font-medium text-gray-500 hover:text-gray-700';
infoContent.classList.add('hidden');
notesContent.classList.remove('hidden');
+ managerInfoContent.classList.add('hidden');
if(infoHeading) infoHeading.classList.add('hidden');
if(notesHeading) notesHeading.classList.remove('hidden');
+ if(managerInfoHeading) managerInfoHeading.classList.add('hidden');
+ } else if(tab === 'managerInfo') {
+ infoTab.className = 'px-4 py-2 text-sm font-medium text-gray-500 hover:text-gray-700';
+ notesTab.className = 'px-4 py-2 text-sm font-medium text-gray-500 hover:text-gray-700';
+ managerInfoTab.className = 'px-4 py-2 text-sm font-medium text-green-600 border-b-2 border-green-600 -mb-px';
+ infoContent.classList.add('hidden');
+ notesContent.classList.add('hidden');
+ managerInfoContent.classList.remove('hidden');
+ if(infoHeading) infoHeading.classList.add('hidden');
+ if(notesHeading) notesHeading.classList.add('hidden');
+ if(managerInfoHeading) managerInfoHeading.classList.remove('hidden');
}
}
document.getElementById('infoTab').addEventListener('click',()=>switchTab('info'));
document.getElementById('notesTab').addEventListener('click',()=>switchTab('notes'));
+ document.getElementById('managerInfoTab').addEventListener('click',()=>{
+ switchTab('managerInfo');
+ populateManagerInfo(currentJob);
+ });
// --- Toolbar ---
let savedSelection = null;