910 lines
62 KiB
HTML
910 lines
62 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Job Tracking - Jobs</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/pocketbase@latest/dist/pocketbase.umd.js"></script>
|
|
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
|
|
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
|
|
</head>
|
|
<body class="min-h-screen bg-gray-50">
|
|
<!-- Navigation Header -->
|
|
<nav class="bg-white shadow-sm border-b border-gray-200">
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div class="flex flex-col sm:flex-row justify-between items-center h-16 sm:h-auto py-4 sm:py-0 gap-4 sm:gap-0">
|
|
<div class="flex items-center gap-4 w-full sm:w-auto">
|
|
<a href="dashboard.html" class="text-indigo-600 hover:text-indigo-700 font-medium text-sm sm:text-base">← Dashboard</a>
|
|
<div class="text-lg sm:text-xl font-bold text-gray-800">Job Tracking</div>
|
|
</div>
|
|
<div class="flex flex-col sm:flex-row items-center gap-3 w-full sm:w-auto">
|
|
<div class="text-xs sm:text-sm text-gray-600" id="userEmail"></div>
|
|
<button
|
|
onclick="logout()"
|
|
class="w-full sm:w-auto bg-red-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition"
|
|
>
|
|
Logout
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Main Content -->
|
|
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
<!-- Message Container -->
|
|
<div id="messageContainer" class="mb-4"></div>
|
|
|
|
<!-- Search and Filter Section -->
|
|
<div class="bg-white rounded-lg shadow border border-gray-200 p-4 mb-6">
|
|
<div class="flex flex-col gap-4">
|
|
<!-- Search Bar -->
|
|
<input
|
|
type="text"
|
|
id="searchInput"
|
|
placeholder="Search by job number, name, or client..."
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base"
|
|
>
|
|
|
|
<!-- Filter Section -->
|
|
<div class="space-y-3">
|
|
<!-- Estimator Filters -->
|
|
<div>
|
|
<label class="block text-xs font-medium text-gray-700 mb-1.5">Filter by Estimator:</label>
|
|
<div class="flex flex-wrap gap-1.5" id="filterButtons">
|
|
<button
|
|
onclick="setEstimatorFilter('all')"
|
|
id="filterAll"
|
|
class="flex-1 sm:flex-none px-3 py-1.5 rounded-md text-xs font-medium transition focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-1 bg-indigo-600 text-white hover:bg-indigo-700"
|
|
>
|
|
All
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Job Status Filters -->
|
|
<div>
|
|
<label class="block text-xs font-medium text-gray-700 mb-1.5">Filter by Status:</label>
|
|
<div class="flex flex-wrap gap-1.5" id="statusFilterButtons">
|
|
<button
|
|
onclick="setStatusFilter('all')"
|
|
id="statusFilterAll"
|
|
class="flex-1 sm:flex-none px-3 py-1.5 rounded-md text-xs font-medium transition focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-1 bg-indigo-600 text-white hover:bg-indigo-700"
|
|
>
|
|
All
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- View Toggle -->
|
|
<div class="mb-4 flex justify-end">
|
|
<div class="inline-flex rounded-lg border border-gray-300 bg-white p-1">
|
|
<button
|
|
onclick="switchView('table')"
|
|
id="tableViewBtn"
|
|
class="px-4 py-2 rounded-md text-sm font-medium transition focus:outline-none focus:ring-2 focus:ring-indigo-500 bg-indigo-600 text-white"
|
|
>
|
|
<svg class="w-5 h-5 inline-block mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
|
|
</svg>
|
|
Table
|
|
</button>
|
|
<button
|
|
onclick="switchView('card')"
|
|
id="cardViewBtn"
|
|
class="px-4 py-2 rounded-md text-sm font-medium transition focus:outline-none focus:ring-2 focus:ring-indigo-500 text-gray-700 hover:bg-gray-50"
|
|
>
|
|
<svg class="w-5 h-5 inline-block mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"></path>
|
|
</svg>
|
|
Cards
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Jobs Table View -->
|
|
<div id="tableView" class="bg-white rounded-lg shadow border border-gray-200 overflow-hidden">
|
|
<div class="overflow-x-auto">
|
|
<table class="min-w-full divide-y divide-gray-200">
|
|
<thead class="bg-gray-50">
|
|
<tr>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Job Number</th>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Job Name</th>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Client</th>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Estimator</th>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Division</th>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="jobsTableBody" class="bg-white divide-y divide-gray-200">
|
|
<tr id="loadingRow">
|
|
<td colspan="8" class="px-6 py-8 text-center text-gray-500">Loading...</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Jobs Card View -->
|
|
<div id="cardView" class="hidden">
|
|
<div id="jobsCardContainer" class="space-y-4">
|
|
<div class="text-center py-8 text-gray-500">Loading...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
<div id="paginationContainer" class="mt-4 flex justify-between items-center"></div>
|
|
</main>
|
|
|
|
<!-- Job Dashboard Modal -->
|
|
<div id="jobModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto z-50">
|
|
<div class="relative top-0 sm:top-10 mx-auto p-4 sm:p-6 border-0 sm:border w-full sm:max-w-7xl shadow-lg rounded-none sm:rounded-md bg-gray-50 mb-0 sm:mb-10 min-h-screen sm:min-h-[600px]">
|
|
<!-- Dashboard Header -->
|
|
<div class="bg-white rounded-lg shadow-sm p-6 mb-6">
|
|
<div class="flex flex-col lg:flex-row justify-between items-start gap-4 mb-4">
|
|
<div class="flex-1">
|
|
<h2 class="text-2xl font-bold text-gray-900 mb-2" id="dashboardJobName">Job Dashboard</h2>
|
|
<div class="flex items-center gap-4 flex-wrap">
|
|
<div>
|
|
<span class="text-sm text-gray-500">Job Number:</span>
|
|
<span class="text-sm font-medium text-gray-900 ml-2" id="dashboardJobNumber">-</span>
|
|
</div>
|
|
<div>
|
|
<span class="text-sm text-gray-500">Status:</span>
|
|
<span class="text-sm font-medium text-indigo-600 ml-2" id="dashboardJobStatus">-</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Info Card -->
|
|
<div class="bg-gray-50 rounded-lg p-4 border border-gray-200 min-w-[250px]">
|
|
<h3 class="text-sm font-semibold text-gray-900 mb-3">Quick Info</h3>
|
|
<div class="space-y-2">
|
|
<div class="flex justify-between">
|
|
<span class="text-xs text-gray-500">Job Type:</span>
|
|
<span class="text-xs font-medium text-gray-900" id="quickJobType">-</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-xs text-gray-500">Division:</span>
|
|
<span class="text-xs font-medium text-gray-900" id="quickDivision">-</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-xs text-gray-500">Estimator:</span>
|
|
<span class="text-xs font-medium text-gray-900" id="quickEstimator">-</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-xs text-gray-500">Office Rep:</span>
|
|
<span class="text-xs font-medium text-gray-900" id="quickOfficeRep">-</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
onclick="closeJobModal()"
|
|
class="text-gray-400 hover:text-gray-500"
|
|
>
|
|
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Tabs Navigation -->
|
|
<div class="border-b border-gray-200">
|
|
<nav class="flex space-x-8 overflow-x-auto">
|
|
<button onclick="switchDashboardTab('overview')" id="tab-overview" class="dashboard-tab border-b-2 border-indigo-500 text-indigo-600 font-medium text-sm py-2 px-1 whitespace-nowrap">Overview</button>
|
|
<button onclick="switchDashboardTab('team')" id="tab-team" class="dashboard-tab border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 font-medium text-sm py-2 px-1 whitespace-nowrap">Team & Client</button>
|
|
<button onclick="switchDashboardTab('dates')" id="tab-dates" class="dashboard-tab border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 font-medium text-sm py-2 px-1 whitespace-nowrap">Dates & Links</button>
|
|
<button onclick="switchDashboardTab('flags')" id="tab-flags" class="dashboard-tab border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 font-medium text-sm py-2 px-1 whitespace-nowrap">Status Flags</button>
|
|
<button onclick="switchDashboardTab('notes')" id="tab-notes" class="dashboard-tab border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 font-medium text-sm py-2 px-1 whitespace-nowrap hidden" id="notesTabButton">Notes</button>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Dashboard Content -->
|
|
<form id="jobForm">
|
|
<input type="hidden" id="jobId" name="id">
|
|
|
|
<div class="space-y-6">
|
|
<!-- Overview Tab Content -->
|
|
<div id="dashboard-tab-overview" class="dashboard-tab-content space-y-6 min-h-[500px]">
|
|
<!-- Basic Information Card -->
|
|
<div class="bg-white rounded-lg shadow-sm p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Basic Information</h3>
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div>
|
|
<label for="Job_Number" class="block text-sm font-medium text-gray-700 mb-1">Job Number</label>
|
|
<input type="text" id="Job_Number" name="Job_Number" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Job_Full_Name" class="block text-sm font-medium text-gray-700 mb-1">Job Full Name</label>
|
|
<input type="text" id="Job_Full_Name" name="Job_Full_Name" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Job_Name" class="block text-sm font-medium text-gray-700 mb-1">Job Name</label>
|
|
<input type="text" id="Job_Name" name="Job_Name" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Job_Address" class="block text-sm font-medium text-gray-700 mb-1">Job Address</label>
|
|
<input type="text" id="Job_Address" name="Job_Address" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Job_Type" class="block text-sm font-medium text-gray-700 mb-1">Job Type</label>
|
|
<select id="Job_Type" name="Job_Type" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
<option value="">Select type...</option>
|
|
<option value="FX">FX</option>
|
|
<option value="TM">TM</option>
|
|
<option value="UP">UP</option>
|
|
<option value="PW">PW</option>
|
|
<option value="FXEX">FXEX</option>
|
|
<option value="PWEX">PWEX</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="Job_Status" class="block text-sm font-medium text-gray-700 mb-1">Job Status</label>
|
|
<select id="Job_Status" name="Job_Status" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
<option value="">Select status...</option>
|
|
<option value="Estimating">Estimating</option>
|
|
<option value="Est Sent">Est Sent</option>
|
|
<option value="Est Hold">Est Hold</option>
|
|
<option value="Follow Up">Follow Up</option>
|
|
<option value="Awarded">Awarded</option>
|
|
<option value="In Progress">In Progress</option>
|
|
<option value="On Hold">On Hold</option>
|
|
<option value="Billed (In Progress)">Billed (In Progress)</option>
|
|
<option value="Billed (Closed)">Billed (Closed)</option>
|
|
<option value="Archive">Archive</option>
|
|
<option value="Not Awarded">Not Awarded</option>
|
|
<option value="Not Bidding">Not Bidding</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="Job_Division" class="block text-sm font-medium text-gray-700 mb-1">Job Division</label>
|
|
<select id="Job_Division" name="Job_Division" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
<option value="">Select division...</option>
|
|
<option value="C#">C#</option>
|
|
<option value="R#">R#</option>
|
|
<option value="S#">S#</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Team & Client Tab Content -->
|
|
<div id="dashboard-tab-team" class="dashboard-tab-content hidden space-y-6 min-h-[500px]">
|
|
<!-- Team Assignment Card -->
|
|
<div class="bg-white rounded-lg shadow-sm p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Team Assignment</h3>
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div>
|
|
<label for="Estimator" class="block text-sm font-medium text-gray-700 mb-1">Estimator</label>
|
|
<select id="Estimator" name="Estimator" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
<option value="">Select estimator...</option>
|
|
<option value="Beth Cardoza">Beth Cardoza</option>
|
|
<option value="Steve Brewer">Steve Brewer</option>
|
|
<option value="Timothy Cardoza">Timothy Cardoza</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="Office_Rep" class="block text-sm font-medium text-gray-700 mb-1">Office Rep</label>
|
|
<select id="Office_Rep" name="Office_Rep" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
<option value="">Select office rep...</option>
|
|
<option value="Regina McClain">Regina McClain</option>
|
|
<option value="Beth Cardoza">Beth Cardoza</option>
|
|
<option value="Steve Brewer">Steve Brewer</option>
|
|
<option value="Timothy Cardoza">Timothy Cardoza</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="Project_Manager" class="block text-sm font-medium text-gray-700 mb-1">Project Manager</label>
|
|
<input type="text" id="Project_Manager" name="Project_Manager" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Client Information Card -->
|
|
<div class="bg-white rounded-lg shadow-sm p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Client Information</h3>
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div>
|
|
<label for="Company_Client" class="block text-sm font-medium text-gray-700 mb-1">Company Client</label>
|
|
<input type="text" id="Company_Client" name="Company_Client" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Contact_Person" class="block text-sm font-medium text-gray-700 mb-1">Contact Person</label>
|
|
<input type="text" id="Contact_Person" name="Contact_Person" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Phone_Number" class="block text-sm font-medium text-gray-700 mb-1">Phone Number</label>
|
|
<input type="tel" id="Phone_Number" name="Phone_Number" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Email" class="block text-sm font-medium text-gray-700 mb-1">Email</label>
|
|
<input type="email" id="Email" name="Email" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Dates & Links Tab Content -->
|
|
<div id="dashboard-tab-dates" class="dashboard-tab-content hidden space-y-6 min-h-[500px]">
|
|
<!-- Dates & Scheduling Card -->
|
|
<div class="bg-white rounded-lg shadow-sm p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Dates & Scheduling</h3>
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div>
|
|
<label for="Start_Date" class="block text-sm font-medium text-gray-700 mb-1">Start Date</label>
|
|
<input type="date" id="Start_Date" name="Start_Date" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Due_Date" class="block text-sm font-medium text-gray-700 mb-1">Due Date</label>
|
|
<input type="date" id="Due_Date" name="Due_Date" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Submission_Date" class="block text-sm font-medium text-gray-700 mb-1">Submission Date</label>
|
|
<input type="date" id="Submission_Date" name="Submission_Date" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Due_Date_Source" class="block text-sm font-medium text-gray-700 mb-1">Due Date Source</label>
|
|
<input type="text" id="Due_Date_Source" name="Due_Date_Source" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Due_Date_Counter" class="block text-sm font-medium text-gray-700 mb-1">Due Date Counter</label>
|
|
<input type="text" id="Due_Date_Counter" name="Due_Date_Counter" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Due_Time" class="block text-sm font-medium text-gray-700 mb-1">Due Time</label>
|
|
<input type="time" id="Due_Time" name="Due_Time" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Schedule_Confidence" class="block text-sm font-medium text-gray-700 mb-1">Schedule Confidence</label>
|
|
<input type="text" id="Schedule_Confidence" name="Schedule_Confidence" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Links & References Card -->
|
|
<div class="bg-white rounded-lg shadow-sm p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Links & References</h3>
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div>
|
|
<label for="Job_QB_Link" class="block text-sm font-medium text-gray-700 mb-1">Job QB Link</label>
|
|
<input type="url" id="Job_QB_Link" name="Job_QB_Link" placeholder="https://..." class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Voxer_Link" class="block text-sm font-medium text-gray-700 mb-1">Voxer Link</label>
|
|
<input type="url" id="Voxer_Link" name="Voxer_Link" placeholder="https://..." class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Job_Folder_Link" class="block text-sm font-medium text-gray-700 mb-1">Job Folder Link</label>
|
|
<input type="url" id="Job_Folder_Link" name="Job_Folder_Link" placeholder="https://..." class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Job_Codes" class="block text-sm font-medium text-gray-700 mb-1">Job Codes</label>
|
|
<input type="text" id="Job_Codes" name="Job_Codes" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
<div>
|
|
<label for="Job_Number_Parent" class="block text-sm font-medium text-gray-700 mb-1">Job Number Parent</label>
|
|
<input type="text" id="Job_Number_Parent" name="Job_Number_Parent" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Status Flags Tab Content -->
|
|
<div id="dashboard-tab-flags" class="dashboard-tab-content hidden space-y-6 min-h-[500px]">
|
|
<!-- Status Flags Card -->
|
|
<div class="bg-white rounded-lg shadow-sm p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Status Flags</h3>
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
<div class="flex items-center">
|
|
<input type="checkbox" id="Active" name="Active" class="w-4 h-4 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500">
|
|
<label for="Active" class="ml-2 text-sm font-medium text-gray-700">Active</label>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<input type="checkbox" id="Flag" name="Flag" class="w-4 h-4 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500">
|
|
<label for="Flag" class="ml-2 text-sm font-medium text-gray-700">Flag</label>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<input type="checkbox" id="Tax_Exempt" name="Tax_Exempt" class="w-4 h-4 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500">
|
|
<label for="Tax_Exempt" class="ml-2 text-sm font-medium text-gray-700">Tax Exempt</label>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<input type="checkbox" id="Docs_Uploaded" name="Docs_Uploaded" class="w-4 h-4 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500">
|
|
<label for="Docs_Uploaded" class="ml-2 text-sm font-medium text-gray-700">Docs Uploaded</label>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<input type="checkbox" id="Tax_Exempt_Docs_Uploaded" name="Tax_Exempt_Docs_Uploaded" class="w-4 h-4 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500">
|
|
<label for="Tax_Exempt_Docs_Uploaded" class="ml-2 text-sm font-medium text-gray-700">Tax Exempt Docs Uploaded</label>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<input type="checkbox" id="Estimate_Approved" name="Estimate_Approved" class="w-4 h-4 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500">
|
|
<label for="Estimate_Approved" class="ml-2 text-sm font-medium text-gray-700">Estimate Approved</label>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<input type="checkbox" id="Estimate_Reviewed" name="Estimate_Reviewed" class="w-4 h-4 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500">
|
|
<label for="Estimate_Reviewed" class="ml-2 text-sm font-medium text-gray-700">Estimate Reviewed</label>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<input type="checkbox" id="Estimate_Draft" name="Estimate_Draft" class="w-4 h-4 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500">
|
|
<label for="Estimate_Draft" class="ml-2 text-sm font-medium text-gray-700">Estimate Draft</label>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<input type="checkbox" id="QB_Created" name="QB_Created" class="w-4 h-4 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500">
|
|
<label for="QB_Created" class="ml-2 text-sm font-medium text-gray-700">QB Created</label>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<input type="checkbox" id="Added_To_Calendar" name="Added_To_Calendar" class="w-4 h-4 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500">
|
|
<label for="Added_To_Calendar" class="ml-2 text-sm font-medium text-gray-700">Added To Calendar</label>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<input type="checkbox" id="PSwift_Uploaded" name="PSwift_Uploaded" class="w-4 h-4 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500">
|
|
<label for="PSwift_Uploaded" class="ml-2 text-sm font-medium text-gray-700">PSwift Uploaded</label>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<input type="checkbox" id="Voxer_Created" name="Voxer_Created" class="w-4 h-4 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500">
|
|
<label for="Voxer_Created" class="ml-2 text-sm font-medium text-gray-700">Voxer Created</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Notes Tab Content -->
|
|
<div id="dashboard-tab-notes" class="dashboard-tab-content hidden min-h-[500px]">
|
|
<!-- Notes Card -->
|
|
<div class="bg-white rounded-lg shadow-sm p-6">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h3 class="text-lg font-semibold text-gray-900">Notes</h3>
|
|
<button
|
|
type="button"
|
|
onclick="showAddNoteForm()"
|
|
class="bg-indigo-600 text-white px-3 py-1.5 rounded-lg text-sm font-medium hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
|
>
|
|
+ Add Note
|
|
</button>
|
|
</div>
|
|
<div id="notesList" class="space-y-4 max-h-[500px] overflow-y-auto">
|
|
<p class="text-center text-gray-500 py-8">Loading notes...</p>
|
|
</div>
|
|
<div id="addNoteForm" class="hidden mt-6 p-4 bg-gray-50 rounded-lg border border-gray-200">
|
|
<h4 class="text-md font-medium text-gray-900 mb-4">Add New Note</h4>
|
|
<div class="space-y-3">
|
|
<div>
|
|
<label for="noteEditor" class="block text-sm font-medium text-gray-700 mb-1">Note Content</label>
|
|
<div id="noteEditor" style="height: 150px; background: white;"></div>
|
|
<textarea id="noteContent" name="noteContent" class="hidden"></textarea>
|
|
</div>
|
|
<div>
|
|
<label for="noteSentiment" class="block text-sm font-medium text-gray-700 mb-1">Sentiment</label>
|
|
<select id="noteSentiment" name="noteSentiment" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm">
|
|
<option value="">Select sentiment...</option>
|
|
<option value="Positive">Positive</option>
|
|
<option value="Negative">Negative</option>
|
|
<option value="Neutral">Neutral</option>
|
|
</select>
|
|
</div>
|
|
<div class="flex flex-col sm:flex-row justify-end space-y-2 sm:space-y-0 sm:space-x-2">
|
|
<button
|
|
type="button"
|
|
onclick="cancelAddNote()"
|
|
class="w-full sm:w-auto px-3 py-2 text-sm bg-gray-200 text-gray-700 rounded hover:bg-gray-300"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onclick="saveNoteFromJob()"
|
|
class="w-full sm:w-auto px-3 py-2 text-sm bg-indigo-600 text-white rounded hover:bg-indigo-700"
|
|
>
|
|
Save Note
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form Buttons -->
|
|
<div class="mt-6 pt-6 border-t border-gray-200 flex flex-col sm:flex-row justify-end gap-3">
|
|
<button
|
|
type="button"
|
|
onclick="closeJobModal()"
|
|
class="w-full sm:w-auto px-6 py-2.5 bg-white border border-gray-300 text-gray-700 rounded-lg font-medium hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition shadow-sm"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
class="w-full sm:w-auto px-6 py-2.5 bg-indigo-600 text-white rounded-lg font-medium hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition shadow-sm"
|
|
>
|
|
Save Job
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="js/pocketbase.js"></script>
|
|
<script src="js/config.js"></script>
|
|
<script src="js/auth.js"></script>
|
|
<script src="js/jobs.js"></script>
|
|
<script src="js/notes.js"></script>
|
|
<script>
|
|
let noteEditor = null;
|
|
|
|
// Check authentication
|
|
checkAuth();
|
|
|
|
// Set user email
|
|
const user = pb.authStore.model;
|
|
if (user) {
|
|
document.getElementById('userEmail').textContent = user.email || '';
|
|
}
|
|
|
|
// Initialize Quill editor for notes
|
|
function initNoteEditor() {
|
|
if (!noteEditor) {
|
|
noteEditor = new Quill('#noteEditor', {
|
|
theme: 'snow',
|
|
modules: {
|
|
toolbar: [
|
|
[{ 'header': [1, 2, 3, false] }],
|
|
['bold', 'italic', 'underline'],
|
|
['link'],
|
|
[{ 'list': 'ordered'}, { 'list': 'bullet' }]
|
|
]
|
|
}
|
|
});
|
|
// Set global reference for notes.js
|
|
if (typeof noteEditorInstance === 'undefined') {
|
|
window.noteEditorInstance = noteEditor;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update dashboard header and quick info
|
|
function updateDashboardInfo(job) {
|
|
document.getElementById('dashboardJobName').textContent = job.Job_Name || job.Job_Full_Name || 'Job Dashboard';
|
|
document.getElementById('dashboardJobNumber').textContent = job.Job_Number || '-';
|
|
document.getElementById('dashboardJobStatus').textContent = job.Job_Status || '-';
|
|
|
|
// Update quick info
|
|
document.getElementById('quickJobType').textContent = job.Job_Type || '-';
|
|
document.getElementById('quickDivision').textContent = job.Job_Division || '-';
|
|
document.getElementById('quickEstimator').textContent = job.Estimator || '-';
|
|
document.getElementById('quickOfficeRep').textContent = job.Office_Rep || '-';
|
|
}
|
|
|
|
// Dashboard tab switching
|
|
function switchDashboardTab(tabName) {
|
|
// Hide all tab contents
|
|
document.querySelectorAll('.dashboard-tab-content').forEach(tab => {
|
|
tab.classList.add('hidden');
|
|
});
|
|
|
|
// Remove active state from all tabs
|
|
document.querySelectorAll('.dashboard-tab').forEach(btn => {
|
|
btn.classList.remove('border-indigo-500', 'text-indigo-600');
|
|
btn.classList.add('border-transparent', 'text-gray-500');
|
|
});
|
|
|
|
// Show selected tab content
|
|
const selectedTab = document.getElementById(`dashboard-tab-${tabName}`);
|
|
if (selectedTab) {
|
|
selectedTab.classList.remove('hidden');
|
|
}
|
|
|
|
// Activate selected tab button
|
|
const selectedButton = document.getElementById(`tab-${tabName}`);
|
|
if (selectedButton) {
|
|
selectedButton.classList.add('border-indigo-500', 'text-indigo-600');
|
|
selectedButton.classList.remove('border-transparent', 'text-gray-500');
|
|
}
|
|
|
|
// Initialize note editor if notes tab
|
|
if (tabName === 'notes') {
|
|
initNoteEditor();
|
|
setTimeout(() => {
|
|
loadJobNotes();
|
|
}, 100);
|
|
}
|
|
}
|
|
|
|
// Modal functions
|
|
function openJobModal(jobId = null) {
|
|
document.getElementById('jobModal').classList.remove('hidden');
|
|
if (jobId) {
|
|
document.getElementById('jobId').value = jobId;
|
|
loadJobForEdit(jobId).then(() => {
|
|
// Show notes tab for existing jobs
|
|
document.getElementById('notesTabButton').classList.remove('hidden');
|
|
initNoteEditor();
|
|
});
|
|
} else {
|
|
document.getElementById('jobForm').reset();
|
|
document.getElementById('jobId').value = '';
|
|
document.getElementById('dashboardJobName').textContent = 'Job Dashboard';
|
|
document.getElementById('dashboardJobNumber').textContent = '-';
|
|
document.getElementById('dashboardJobStatus').textContent = '-';
|
|
// Hide notes tab for new jobs
|
|
document.getElementById('notesTabButton').classList.add('hidden');
|
|
}
|
|
// Start with overview tab
|
|
switchDashboardTab('overview');
|
|
}
|
|
|
|
function closeJobModal() {
|
|
document.getElementById('jobModal').classList.add('hidden');
|
|
document.getElementById('jobForm').reset();
|
|
document.getElementById('jobId').value = '';
|
|
}
|
|
|
|
// Filter functions
|
|
window.currentEstimatorFilter = 'all';
|
|
window.currentStatusFilter = 'all';
|
|
|
|
window.setEstimatorFilter = function(filter) {
|
|
window.currentEstimatorFilter = filter;
|
|
|
|
// Update all estimator filter buttons
|
|
const container = document.getElementById('filterButtons');
|
|
if (container) {
|
|
const buttons = container.querySelectorAll('button');
|
|
buttons.forEach(btn => {
|
|
const isActive = (filter === 'all' && btn.id === 'filterAll') ||
|
|
(filter !== 'all' && btn.textContent === filter);
|
|
|
|
if (isActive) {
|
|
// Remove all possible background/text colors
|
|
btn.classList.remove('bg-gray-200', 'text-gray-700', 'hover:bg-gray-300',
|
|
'bg-orange-200', 'text-orange-800', 'hover:bg-orange-300',
|
|
'bg-blue-200', 'text-blue-800', 'hover:bg-blue-300',
|
|
'bg-purple-200', 'text-purple-800', 'hover:bg-purple-300',
|
|
'bg-indigo-600', 'text-white', 'hover:bg-indigo-700');
|
|
// Add active state
|
|
btn.classList.add('bg-indigo-600', 'text-white', 'hover:bg-indigo-700');
|
|
} else {
|
|
// Remove active state
|
|
btn.classList.remove('bg-indigo-600', 'text-white', 'hover:bg-indigo-700');
|
|
|
|
// Restore original pastel colors based on estimator name
|
|
const estimatorLower = btn.textContent.toLowerCase();
|
|
if (estimatorLower.includes('steve')) {
|
|
btn.classList.remove('bg-gray-200', 'text-gray-700', 'hover:bg-gray-300',
|
|
'bg-blue-200', 'text-blue-800', 'hover:bg-blue-300',
|
|
'bg-purple-200', 'text-purple-800', 'hover:bg-purple-300');
|
|
btn.classList.add('bg-orange-200', 'text-orange-800', 'hover:bg-orange-300', 'px-3', 'py-1.5', 'rounded-md', 'text-xs');
|
|
} else if (estimatorLower.includes('beth')) {
|
|
btn.classList.remove('bg-gray-200', 'text-gray-700', 'hover:bg-gray-300',
|
|
'bg-orange-200', 'text-orange-800', 'hover:bg-orange-300',
|
|
'bg-purple-200', 'text-purple-800', 'hover:bg-purple-300');
|
|
btn.classList.add('bg-blue-200', 'text-blue-800', 'hover:bg-blue-300', 'px-3', 'py-1.5', 'rounded-md', 'text-xs');
|
|
} else if (estimatorLower.includes('timothy')) {
|
|
btn.classList.remove('bg-gray-200', 'text-gray-700', 'hover:bg-gray-300',
|
|
'bg-orange-200', 'text-orange-800', 'hover:bg-orange-300',
|
|
'bg-blue-200', 'text-blue-800', 'hover:bg-blue-300');
|
|
btn.classList.add('bg-purple-200', 'text-purple-800', 'hover:bg-purple-300', 'px-3', 'py-1.5', 'rounded-md', 'text-xs');
|
|
} else {
|
|
// Default gray for other estimators
|
|
btn.classList.remove('bg-orange-200', 'text-orange-800', 'hover:bg-orange-300',
|
|
'bg-blue-200', 'text-blue-800', 'hover:bg-blue-300',
|
|
'bg-purple-200', 'text-purple-800', 'hover:bg-purple-300');
|
|
btn.classList.add('bg-gray-200', 'text-gray-700', 'hover:bg-gray-300', 'px-3', 'py-1.5', 'rounded-md', 'text-xs');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
loadJobs();
|
|
};
|
|
|
|
window.setStatusFilter = function(filter) {
|
|
window.currentStatusFilter = filter;
|
|
|
|
// Update all status filter buttons
|
|
const container = document.getElementById('statusFilterButtons');
|
|
if (container) {
|
|
const buttons = container.querySelectorAll('button');
|
|
buttons.forEach(btn => {
|
|
const isActive = (filter === 'all' && btn.id === 'statusFilterAll') ||
|
|
(filter !== 'all' && btn.textContent === filter);
|
|
|
|
if (isActive) {
|
|
// Remove all possible background/text colors
|
|
btn.classList.remove('bg-gray-200', 'text-gray-700', 'hover:bg-gray-300',
|
|
'bg-purple-200', 'text-purple-800', 'hover:bg-purple-300',
|
|
'bg-orange-200', 'text-orange-800', 'hover:bg-orange-300',
|
|
'bg-yellow-200', 'text-yellow-800', 'hover:bg-yellow-300',
|
|
'bg-blue-200', 'text-blue-800', 'hover:bg-blue-300',
|
|
'bg-green-200', 'text-green-800', 'hover:bg-green-300',
|
|
'bg-red-200', 'text-red-800', 'hover:bg-red-300',
|
|
'bg-indigo-600', 'text-white', 'hover:bg-indigo-700',
|
|
'px-4', 'py-2', 'rounded-lg', 'text-sm', 'sm:text-base');
|
|
// Add active state and smaller size
|
|
btn.classList.add('bg-indigo-600', 'text-white', 'hover:bg-indigo-700', 'px-3', 'py-1.5', 'rounded-md', 'text-xs');
|
|
} else {
|
|
// Remove active state and size classes
|
|
btn.classList.remove('bg-indigo-600', 'text-white', 'hover:bg-indigo-700',
|
|
'px-4', 'py-2', 'rounded-lg', 'text-sm', 'sm:text-base');
|
|
// Add smaller size classes
|
|
btn.classList.add('px-3', 'py-1.5', 'rounded-md', 'text-xs');
|
|
|
|
// Restore original colors based on status
|
|
const statusLower = btn.textContent.toLowerCase();
|
|
let bgColor = 'bg-gray-200';
|
|
let textColor = 'text-gray-700';
|
|
let hoverColor = 'hover:bg-gray-300';
|
|
|
|
if (statusLower.includes('billed') && statusLower.includes('closed')) {
|
|
bgColor = 'bg-purple-200';
|
|
textColor = 'text-purple-800';
|
|
hoverColor = 'hover:bg-purple-300';
|
|
} else if (statusLower.includes('billed') && statusLower.includes('progress')) {
|
|
bgColor = 'bg-purple-200';
|
|
textColor = 'text-purple-800';
|
|
hoverColor = 'hover:bg-purple-300';
|
|
} else if (statusLower.includes('est hold') || statusLower === 'est hold') {
|
|
bgColor = 'bg-orange-200';
|
|
textColor = 'text-orange-800';
|
|
hoverColor = 'hover:bg-orange-300';
|
|
} else if (statusLower.includes('est sent') || statusLower === 'est sent' ||
|
|
statusLower.includes('follow up') || statusLower === 'follow up') {
|
|
bgColor = 'bg-yellow-200';
|
|
textColor = 'text-yellow-800';
|
|
hoverColor = 'hover:bg-yellow-300';
|
|
} else if (statusLower.includes('estimating') || statusLower === 'estimating') {
|
|
bgColor = 'bg-blue-200';
|
|
textColor = 'text-blue-800';
|
|
hoverColor = 'hover:bg-blue-300';
|
|
} else if (statusLower.includes('in progress') || statusLower === 'in progress' ||
|
|
statusLower.includes('awarded') || statusLower === 'awarded') {
|
|
bgColor = 'bg-green-200';
|
|
textColor = 'text-green-800';
|
|
hoverColor = 'hover:bg-green-300';
|
|
} else if (statusLower.includes('not awarded') || statusLower === 'not awarded' ||
|
|
statusLower.includes('not bidding') || statusLower === 'not bidding') {
|
|
bgColor = 'bg-red-200';
|
|
textColor = 'text-red-800';
|
|
hoverColor = 'hover:bg-red-300';
|
|
} else if (statusLower.includes('archive') || statusLower === 'archive') {
|
|
bgColor = 'bg-gray-200';
|
|
textColor = 'text-gray-700';
|
|
hoverColor = 'hover:bg-gray-300';
|
|
}
|
|
|
|
// Remove all color and size classes
|
|
btn.classList.remove('bg-gray-200', 'text-gray-700', 'hover:bg-gray-300',
|
|
'bg-purple-200', 'text-purple-800', 'hover:bg-purple-300',
|
|
'bg-orange-200', 'text-orange-800', 'hover:bg-orange-300',
|
|
'bg-yellow-200', 'text-yellow-800', 'hover:bg-yellow-300',
|
|
'bg-blue-200', 'text-blue-800', 'hover:bg-blue-300',
|
|
'bg-green-200', 'text-green-800', 'hover:bg-green-300',
|
|
'bg-red-200', 'text-red-800', 'hover:bg-red-300',
|
|
'px-4', 'py-2', 'rounded-lg', 'text-sm', 'sm:text-base', 'px-3', 'py-1.5', 'rounded-md', 'text-xs');
|
|
// Add restored colors and smaller size
|
|
btn.classList.add(bgColor, textColor, hoverColor, 'px-3', 'py-1.5', 'rounded-md', 'text-xs');
|
|
}
|
|
});
|
|
}
|
|
loadJobs();
|
|
};
|
|
|
|
window.setEstimatorFilter = function(filter) {
|
|
window.currentEstimatorFilter = filter;
|
|
|
|
// Update all estimator filter buttons
|
|
const container = document.getElementById('filterButtons');
|
|
if (container) {
|
|
const buttons = container.querySelectorAll('button');
|
|
buttons.forEach(btn => {
|
|
const isActive = (filter === 'all' && btn.id === 'filterAll') ||
|
|
(filter !== 'all' && btn.textContent === filter);
|
|
|
|
if (isActive) {
|
|
// Remove all possible background/text colors
|
|
btn.classList.remove('bg-gray-200', 'text-gray-700', 'hover:bg-gray-300',
|
|
'bg-orange-200', 'text-orange-800', 'hover:bg-orange-300',
|
|
'bg-blue-200', 'text-blue-800', 'hover:bg-blue-300',
|
|
'bg-purple-200', 'text-purple-800', 'hover:bg-purple-300',
|
|
'bg-indigo-600', 'text-white', 'hover:bg-indigo-700');
|
|
// Add active state
|
|
btn.classList.add('bg-indigo-600', 'text-white', 'hover:bg-indigo-700');
|
|
} else {
|
|
// Remove active state
|
|
btn.classList.remove('bg-indigo-600', 'text-white', 'hover:bg-indigo-700');
|
|
|
|
// Restore original pastel colors based on estimator name
|
|
const estimatorLower = btn.textContent.toLowerCase();
|
|
if (estimatorLower.includes('steve')) {
|
|
btn.classList.remove('bg-gray-200', 'text-gray-700', 'hover:bg-gray-300',
|
|
'bg-blue-200', 'text-blue-800', 'hover:bg-blue-300',
|
|
'bg-purple-200', 'text-purple-800', 'hover:bg-purple-300');
|
|
btn.classList.add('bg-orange-200', 'text-orange-800', 'hover:bg-orange-300', 'px-3', 'py-1.5', 'rounded-md', 'text-xs');
|
|
} else if (estimatorLower.includes('beth')) {
|
|
btn.classList.remove('bg-gray-200', 'text-gray-700', 'hover:bg-gray-300',
|
|
'bg-orange-200', 'text-orange-800', 'hover:bg-orange-300',
|
|
'bg-purple-200', 'text-purple-800', 'hover:bg-purple-300');
|
|
btn.classList.add('bg-blue-200', 'text-blue-800', 'hover:bg-blue-300', 'px-3', 'py-1.5', 'rounded-md', 'text-xs');
|
|
} else if (estimatorLower.includes('timothy')) {
|
|
btn.classList.remove('bg-gray-200', 'text-gray-700', 'hover:bg-gray-300',
|
|
'bg-orange-200', 'text-orange-800', 'hover:bg-orange-300',
|
|
'bg-blue-200', 'text-blue-800', 'hover:bg-blue-300');
|
|
btn.classList.add('bg-purple-200', 'text-purple-800', 'hover:bg-purple-300', 'px-3', 'py-1.5', 'rounded-md', 'text-xs');
|
|
} else {
|
|
// Default gray for other estimators
|
|
btn.classList.remove('bg-orange-200', 'text-orange-800', 'hover:bg-orange-300',
|
|
'bg-blue-200', 'text-blue-800', 'hover:bg-blue-300',
|
|
'bg-purple-200', 'text-purple-800', 'hover:bg-purple-300');
|
|
btn.classList.add('bg-gray-200', 'text-gray-700', 'hover:bg-gray-300', 'px-3', 'py-1.5', 'rounded-md', 'text-xs');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
loadJobs();
|
|
};
|
|
|
|
// Search
|
|
document.getElementById('searchInput').addEventListener('input', (e) => {
|
|
clearTimeout(window.searchTimeout);
|
|
window.searchTimeout = setTimeout(() => {
|
|
loadJobs();
|
|
}, 300);
|
|
});
|
|
|
|
// Form submission
|
|
document.getElementById('jobForm').addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
await saveJob();
|
|
});
|
|
|
|
// Update quick info when fields change
|
|
['Job_Type', 'Job_Division', 'Estimator', 'Office_Rep'].forEach(fieldId => {
|
|
const field = document.getElementById(fieldId);
|
|
if (field) {
|
|
field.addEventListener('change', () => {
|
|
const quickField = document.getElementById(`quick${fieldId.replace('_', '')}`);
|
|
if (quickField) {
|
|
quickField.textContent = field.value || '-';
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Note form submission (in job modal) - make it global
|
|
window.saveNoteFromJob = async function() {
|
|
if (noteEditor) {
|
|
const content = noteEditor.root.innerHTML;
|
|
document.getElementById('noteContent').value = content;
|
|
}
|
|
// Call the saveNote function from notes.js
|
|
if (typeof saveNote === 'function') {
|
|
await saveNote();
|
|
}
|
|
};
|
|
|
|
// Load filters and jobs on page load
|
|
// Small delay to ensure DOM is ready
|
|
setTimeout(async () => {
|
|
// Wait for config to load
|
|
await initConfig();
|
|
await loadEstimatorFilters();
|
|
await loadStatusFilters();
|
|
// Initialize view display
|
|
if (typeof updateViewDisplay === 'function') {
|
|
updateViewDisplay();
|
|
}
|
|
loadJobs();
|
|
}, 100);
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|