{#if !authState.isAuthenticated}
Job Info
Please authenticate to access job information
Login with Microsoft
{:else}
Job Info
{#if hasVoicePermission}
{#if isListening}
●
{:else}
{/if}
{/if}
{showFiltersPanel ? 'Hide Filters' : 'Show Filters'}
{#if showFiltersPanel}
Division
{#each divisions as div}
toggleFilterCheckbox(filterDivisions, div)} style="cursor: pointer;" /> {div}
{/each}
Active
{#each activeOptions as opt}
toggleFilterCheckbox(filterActive, opt)} style="cursor: pointer;" /> {opt}
{/each}
Estimator
{#each estimators as est}
toggleFilterCheckbox(filterEstimators, est)} style="cursor: pointer;" /> {est}
{/each}
Status
{#each statuses as stat}
toggleFilterCheckbox(filterStatuses, stat)} style="cursor: pointer;" /> {stat}
{/each}
Clear Filters
{/if} {#if error}
{error}
{/if}
{#if loading}
Loading jobs...
{:else if visibleJobs.length === 0}
No jobs found
{:else} {#each visibleJobs as job (job.id)}
selectJob(job)} style="background: white; border: 1px solid #dbeafe; border-radius: 6px; padding: 12px; text-align: left; cursor: pointer; box-shadow: 0 1px 2px rgba(0,0,0,0.05); position: relative; transition: background 0.2s;" >
{job.Job_Number}
{job.Job_Full_Name}
{job.Contact_Person}
{/each} {/if}
{authState.user?.email}
Sign out
{/if}
{#if showDetailModal && selectedJob}
{ showNotesTab = false; showFolderContents = false; }} style="flex: 1; padding: 12px; border: none; background: {!showNotesTab && !showFolderContents ? '#2563eb' : '#f9fafb'}; color: {!showNotesTab && !showFolderContents ? 'white' : '#6b7280'}; font-weight: bold; cursor: pointer; font-size: 13px;" > INFO
{ showFolderContents = false; showNotesTab = true; }} style="flex: 1; padding: 12px; border: none; background: {showNotesTab && !showFolderContents ? '#ea580c' : '#f9fafb'}; color: {showNotesTab && !showFolderContents ? 'white' : '#6b7280'}; font-weight: bold; cursor: pointer; font-size: 13px;" > NOTES
{#if !showNotesTab && !showFolderContents}
{#each Object.entries(selectedJob) as [key, value]} {#if key !== 'id' && value !== null && value !== undefined && !key.startsWith('collectionId') && key !== 'collectionName' && key !== 'Job_Folder_Link'}
{key}
{typeof value === 'object' ? JSON.stringify(value) : String(value)}
{/if} {/each}
{#if selectedJob.Job_Folder_Link}
{ showFolderContents = true; loadJobFiles(); }} style="width: 100%; padding: 12px; background: #059669; color: white; border: none; border-radius: 6px; font-weight: bold; text-align: center; cursor: pointer; font-size: 13px;" > 📁 Show Folder
{/if}
{:else if showFolderContents}
{#if loadingFiles}
Loading files...
{:else if filesError}
{filesError}
showFolderContents = false} style="width: 100%; padding: 10px; background: #6b7280; color: white; border: none; border-radius: 4px; font-weight: bold; cursor: pointer; font-size: 12px;" > Back to Info
{:else if jobFiles.length === 0}
No files found in folder
showFolderContents = false} style="width: 100%; padding: 10px; background: #6b7280; color: white; border: none; border-radius: 4px; font-weight: bold; cursor: pointer; font-size: 12px;" > Back to Info
{:else}
{#each ['managerInfo', 'contracts', 'plans', 'submittals', 'other'] as category} {@const categoryFiles = jobFiles .filter(f => !f.isFolder && categorizeFile(f.name) === category) .filter(f => folderSearchFilter === '' || f.name.toLowerCase().includes(folderSearchFilter.toLowerCase())) } {@const categoryTitles = { managerInfo: 'Manager Info', contracts: 'Contracts / Estimates', plans: 'Plans', submittals: 'Submittals', other: 'Other' }} {#if categoryFiles.length > 0}
{categoryTitles[category]} ({categoryFiles.length})
{#each categoryFiles as file, idx (file.id || file.name)}
openFileInViewer(file)} style="width: 100%; padding: 10px 12px; text-align: left; border: none; background: white; cursor: pointer; border-bottom: {idx < categoryFiles.length - 1 ? '1px solid #e5e7eb' : 'none'}; transition: background 0.2s;" on:mouseenter={(e) => e.target.style.background = '#f9fafb'} on:mouseleave={(e) => e.target.style.background = 'white'} >
{@html getSvgIcon(getFileIcon(file.name))}
{file.name}
{formatFileSize(file.size)} {#if file.modified} • {formatDate(file.modified)} {/if}
{/each}
{/if} {/each}
showFolderContents = false} style="width: 100%; padding: 10px; background: #6b7280; color: white; border: none; border-radius: 4px; font-weight: bold; cursor: pointer; font-size: 12px;" > Back to Info
{/if}
{:else}
{selectedJob.Notes || '(No notes)'}
Save Note
{/if}
(showDetailModal = false)} style="width: 100%; background: #ef4444; color: white; border: none; padding: 10px; border-radius: 4px; font-weight: bold; cursor: pointer;" > Close
{/if} {#if showFileViewer && currentViewerFile}
Back
{currentViewerFile.name || 'File Preview'}
Close
{#if showPdfViewer && currentPdf}
−
{Math.round(currentPdf.scale * 100)}%
+
{Math.round(currentPdf.scale * 100)}%
{#if !currentPdf.isImage && currentPdf.pages > 1}
◀
▶
Page
{currentPdf.page}
/
{currentPdf.pages}
{/if}
{:else if currentViewerFile.url}
{:else}
Unable to preview file
No URL available
{/if}
{/if}