added manager info view
This commit is contained in:
+837
-1
@@ -159,6 +159,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MANAGER INFO Tab Content -->
|
||||
<div id="managerInfoContent" class="hidden flex-1 overflow-hidden flex flex-col min-h-0">
|
||||
<div id="managerInfoHeading" class="bg-green-600 text-white text-center py-2 text-sm font-semibold rounded-t-lg flex-shrink-0">MANAGER INFO</div>
|
||||
<div class="px-3 pt-2 pb-1 flex-1 overflow-auto">
|
||||
<div id="managerInfoTable" class="w-full space-y-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab Bar at Bottom -->
|
||||
<div class="flex items-center border-t border-gray-200 pt-2 px-3 pb-2 flex-shrink-0 bg-white rounded-b-lg">
|
||||
<button id="backBtn" class="mr-6 pr-6 border-r border-gray-200 px-3 py-2 text-sm font-medium text-gray-600 hover:text-gray-800 flex items-center">
|
||||
@@ -169,6 +177,7 @@
|
||||
</button>
|
||||
<button id="infoTab" class="px-4 py-2 text-sm font-medium text-blue-600 border-b-2 border-blue-600 -mb-px">INFO</button>
|
||||
<button id="notesTab" class="px-4 py-2 text-sm font-medium text-gray-500 hover:text-gray-700">NOTES</button>
|
||||
<button id="managerInfoTab" class="px-4 py-2 text-sm font-medium text-gray-500 hover:text-gray-700">MANAGER INFO</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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 = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Job Full Name</div><div class="text-gray-900 truncate" style="font-size: 10px;" title="${job.Job_Full_Name || '—'}">${job.Job_Full_Name || '—'}</div>`;
|
||||
|
||||
const managerDiv = document.createElement('div');
|
||||
managerDiv.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Manager</div><div class="text-gray-900" style="font-size: 10px;">${job.Manager || '—'}</div>`;
|
||||
|
||||
const jobStatusDiv = document.createElement('div');
|
||||
jobStatusDiv.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Job Status</div><div class="text-gray-900" style="font-size: 10px;">${job.Job_Status || '—'}</div>`;
|
||||
|
||||
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 = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1 text-center" style="font-size: 8px;">Job Number</div><div class="text-gray-900 text-center" style="font-size: 10px;">${job.Job_Number || '—'}</div>`;
|
||||
|
||||
const jobDivision = document.createElement('div');
|
||||
jobDivision.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1 text-center" style="font-size: 8px;">Job Division</div><div class="text-gray-900 text-center" style="font-size: 10px;">${job.Job_Division || '—'}</div>`;
|
||||
|
||||
const jobType = document.createElement('div');
|
||||
jobType.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1 text-center" style="font-size: 8px;">Job Type</div><div class="text-gray-900 text-center" style="font-size: 10px;">${job.Job_Type || '—'}</div>`;
|
||||
|
||||
const scopeDiv = document.createElement('div');
|
||||
scopeDiv.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1 text-center" style="font-size: 8px;">Scope</div><div class="text-gray-900 text-center" style="font-size: 10px;">—</div>`;
|
||||
|
||||
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 = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1 text-center" style="font-size: 8px;">Company/Client</div><div class="text-gray-900 text-center" style="font-size: 10px;">${job.Company_Client || '—'}</div>`;
|
||||
|
||||
const contactPerson = document.createElement('div');
|
||||
contactPerson.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1 text-center" style="font-size: 8px;">Contact Person</div><div class="text-gray-900 text-center" style="font-size: 10px;">${job.Contact_Person || '—'}</div>`;
|
||||
|
||||
const contactNumber = document.createElement('div');
|
||||
contactNumber.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1 text-center" style="font-size: 8px;">Contact Number</div>`;
|
||||
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 = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1 text-center" style="font-size: 8px;">EXT.</div><div class="text-gray-900 text-center" style="font-size: 10px;">—</div>`;
|
||||
|
||||
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 = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Job Address</div>`;
|
||||
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 = '<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" style="fill: #000000;"><title>Apple</title><path d="M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701"/></svg>';
|
||||
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 = '<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" style="fill: #4285F4;"><title>Google</title><path d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z"/></svg>';
|
||||
mapIconsContainer.appendChild(googleLink);
|
||||
}
|
||||
|
||||
// EST Cost in 4th column (centered)
|
||||
const estimatedCostContainer = document.createElement('div');
|
||||
estimatedCostContainer.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1 text-center" style="font-size: 8px;">EST Cost</div><div class="text-gray-900 text-center" style="font-size: 10px;">—</div>`;
|
||||
|
||||
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 = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1 text-center" style="font-size: 8px;">EST Start Date</div><div class="text-gray-900 text-center" style="font-size: 10px;">${startDateFormatted}</div>`;
|
||||
|
||||
const actualStartDate = document.createElement('div');
|
||||
actualStartDate.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1 text-center" style="font-size: 8px;">Actual Start Date</div><div class="text-gray-900 text-center" style="font-size: 10px;">${startDateFormatted}</div>`;
|
||||
|
||||
const estimatedEndDate = document.createElement('div');
|
||||
estimatedEndDate.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1 text-center" style="font-size: 8px;">EST End Date</div><div class="text-gray-900 text-center" style="font-size: 10px;">—</div>`;
|
||||
|
||||
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 = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-2" style="font-size: 8px;">Links</div>`;
|
||||
|
||||
const linksGrid = document.createElement('div');
|
||||
linksGrid.className = 'grid grid-cols-3 gap-4';
|
||||
|
||||
// Plans column
|
||||
const plansCol = document.createElement('div');
|
||||
plansCol.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Plans</div>`;
|
||||
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 = `<div class="text-gray-400 italic" style="font-size: 10px;">No plans</div>`;
|
||||
}
|
||||
plansCol.appendChild(plansList);
|
||||
|
||||
// Estimates column
|
||||
const estimatesCol = document.createElement('div');
|
||||
estimatesCol.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Estimates</div>`;
|
||||
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 = `<div class="text-gray-400 italic" style="font-size: 10px;">No estimates</div>`;
|
||||
}
|
||||
estimatesCol.appendChild(estimatesList);
|
||||
|
||||
// Documents & Files column
|
||||
const docsCol = document.createElement('div');
|
||||
docsCol.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Documents & Files</div>`;
|
||||
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 = `<div class="text-gray-400 italic" style="font-size: 10px;">No documents</div>`;
|
||||
}
|
||||
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 || '<span class="text-gray-400 italic">No notes available</span>';
|
||||
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 = `
|
||||
<div class="flex flex-wrap gap-1 mb-2">
|
||||
<button data-cmd="bold" class="h-6 w-8 rounded border border-gray-400 bg-gray-400 text-gray-900 cursor-pointer flex items-center justify-center" style="font-size: 8px;"><strong>B</strong></button>
|
||||
<button data-cmd="italic" class="h-6 w-8 rounded border border-gray-400 bg-gray-400 text-gray-900 cursor-pointer flex items-center justify-center" style="font-size: 8px;"><em>I</em></button>
|
||||
<button data-cmd="underline" class="h-6 w-8 rounded border border-gray-400 bg-gray-400 text-gray-900 cursor-pointer flex items-center justify-center" style="font-size: 8px;"><u>U</u></button>
|
||||
<button data-cmd="foreColor" data-val="#ff0000" class="h-6 w-8 rounded border border-gray-400 bg-gray-400 text-gray-900 cursor-pointer flex items-center justify-center text-red-500" style="font-size: 8px;">A</button>
|
||||
<button data-cmd="foreColor" data-val="#0000ff" class="h-6 w-8 rounded border border-gray-400 bg-gray-400 text-gray-900 cursor-pointer flex items-center justify-center text-blue-600" style="font-size: 8px;">A</button>
|
||||
<button data-cmd="foreColor" data-val="#000000" class="h-6 w-8 rounded border border-gray-400 bg-gray-400 text-gray-900 cursor-pointer flex items-center justify-center text-black" style="font-size: 8px;">A</button>
|
||||
<button data-cmd="removeFormat" class="h-6 px-2 rounded border border-gray-400 bg-gray-400 text-white cursor-pointer flex items-center justify-center" style="font-size: 8px;">Reset</button>
|
||||
</div>
|
||||
<div id="jobNewNote" contenteditable="true" class="min-h-14 max-h-[120px] overflow-auto p-2 border border-gray-600 rounded text-sm bg-white mb-2"></div>
|
||||
<div class="flex gap-2">
|
||||
<button id="jobNoteSubmit" class="px-3 py-1 rounded text-white bg-green-600 hover:bg-green-700 font-bold cursor-pointer" style="font-size: 8px;">Submit</button>
|
||||
<button id="jobNoteClear" class="px-3 py-1 rounded text-white bg-gray-500 hover:bg-gray-600 font-bold cursor-pointer" style="font-size: 8px;">Clear</button>
|
||||
<button id="jobNoteCollapse" class="px-3 py-1 rounded text-white bg-red-600 hover:bg-red-700 font-bold cursor-pointer" style="font-size: 8px;">Close</button>
|
||||
</div>
|
||||
`;
|
||||
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 = `
|
||||
<div>
|
||||
<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Plans</div>
|
||||
<div class="text-gray-400 italic" style="font-size: 10px;">Loading...</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Estimates</div>
|
||||
<div class="text-gray-400 italic" style="font-size: 10px;">Loading...</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Other Files</div>
|
||||
<div class="text-gray-400 italic" style="font-size: 10px;">Loading...</div>
|
||||
</div>
|
||||
`;
|
||||
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 = '<span class="text-gray-400 italic">No manager notes available</span>';
|
||||
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 = `
|
||||
<div class="flex flex-wrap gap-1 mb-2">
|
||||
<button data-cmd="bold" class="h-6 w-8 rounded border border-gray-400 bg-gray-400 text-gray-900 cursor-pointer flex items-center justify-center" style="font-size: 8px;"><strong>B</strong></button>
|
||||
<button data-cmd="italic" class="h-6 w-8 rounded border border-gray-400 bg-gray-400 text-gray-900 cursor-pointer flex items-center justify-center" style="font-size: 8px;"><em>I</em></button>
|
||||
<button data-cmd="underline" class="h-6 w-8 rounded border border-gray-400 bg-gray-400 text-gray-900 cursor-pointer flex items-center justify-center" style="font-size: 8px;"><u>U</u></button>
|
||||
<button data-cmd="foreColor" data-val="#ff0000" class="h-6 w-8 rounded border border-gray-400 bg-gray-400 text-gray-900 cursor-pointer flex items-center justify-center text-red-500" style="font-size: 8px;">A</button>
|
||||
<button data-cmd="foreColor" data-val="#0000ff" class="h-6 w-8 rounded border border-gray-400 bg-gray-400 text-gray-900 cursor-pointer flex items-center justify-center text-blue-600" style="font-size: 8px;">A</button>
|
||||
<button data-cmd="foreColor" data-val="#000000" class="h-6 w-8 rounded border border-gray-400 bg-gray-400 text-gray-900 cursor-pointer flex items-center justify-center text-black" style="font-size: 8px;">A</button>
|
||||
<button data-cmd="removeFormat" class="h-6 px-2 rounded border border-gray-400 bg-gray-400 text-white cursor-pointer flex items-center justify-center" style="font-size: 8px;">Reset</button>
|
||||
</div>
|
||||
<div id="managerNewNote" contenteditable="true" class="min-h-14 max-h-[120px] overflow-auto p-2 border border-gray-600 rounded text-sm bg-white mb-2"></div>
|
||||
<div class="flex gap-2">
|
||||
<button id="managerNoteSubmit" class="px-3 py-1 rounded text-white bg-green-600 hover:bg-green-700 font-bold cursor-pointer" style="font-size: 8px;">Submit</button>
|
||||
<button id="managerNoteClear" class="px-3 py-1 rounded text-white bg-gray-500 hover:bg-gray-600 font-bold cursor-pointer" style="font-size: 8px;">Clear</button>
|
||||
<button id="managerNoteCollapse" class="px-3 py-1 rounded text-white bg-red-600 hover:bg-red-700 font-bold cursor-pointer" style="font-size: 8px;">Close</button>
|
||||
</div>
|
||||
`;
|
||||
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 = `
|
||||
<div>
|
||||
<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Plans</div>
|
||||
<div class="text-gray-400 italic" style="font-size: 10px;">Loading...</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Estimates</div>
|
||||
<div class="text-gray-400 italic" style="font-size: 10px;">Loading...</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Other Files</div>
|
||||
<div class="text-gray-400 italic" style="font-size: 10px;">Loading...</div>
|
||||
</div>
|
||||
`;
|
||||
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 = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Plans</div>`;
|
||||
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 = `<div class="text-gray-400 italic" style="font-size: 10px;">No plans</div>`;
|
||||
}
|
||||
plansCol.appendChild(plansList);
|
||||
|
||||
// Estimates column
|
||||
const estimatesCol = document.createElement('div');
|
||||
estimatesCol.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Estimates</div>`;
|
||||
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 = `<div class="text-gray-400 italic" style="font-size: 10px;">No estimates</div>`;
|
||||
}
|
||||
estimatesCol.appendChild(estimatesList);
|
||||
|
||||
// Other Files column
|
||||
const otherCol = document.createElement('div');
|
||||
otherCol.innerHTML = `<div class="font-bold text-gray-700 uppercase tracking-wide mb-1" style="font-size: 8px;">Other Files</div>`;
|
||||
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 = `<div class="text-gray-400 italic" style="font-size: 10px;">No other files</div>`;
|
||||
}
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user