<!-- Main Container -->
<div class="bg-white shadow-xl rounded-xl p-6 md:p-8 lg:p-10 max-w-5xl w-full text-center">
<!-- Header Section -->
<header class="mb-8">
<h1 class="text-4xl md:text-5xl font-extrabold text-blue-800 mb-2 leading-tight">
Ardur Team Dashboard
</h1>
<p class="text-lg md:text-xl text-gray-600 font-medium">
Manage and track your team's progress and operations.
</p>
<p id="user-id-display" class="text-sm text-gray-500 mt-2">User ID: Loading...</p>
</header>
<!-- Tabs Navigation -->
<nav class="mb-6">
<ul class="flex flex-wrap justify-center border-b border-gray-200">
<li class="mr-1">
<button class="tab-button py-3 px-6 text-lg font-medium text-gray-600 hover:text-blue-700 hover:border-blue-300 border-b-2 border-transparent transition-colors duration-200" data-tab="overview">Overview</button>
</li>
<li class="mr-1">
<button class="tab-button py-3 px-6 text-lg font-medium text-gray-600 hover:text-blue-700 hover:border-blue-300 border-b-2 border-transparent transition-colors duration-200" data-tab="members">Team Members</button>
</li>
<li class="mr-1">
<button class="tab-button py-3 px-6 text-lg font-medium text-gray-600 hover:text-blue-700 hover:border-blue-300 border-b-2 border-transparent transition-colors duration-200" data-tab="assets">Assets</button>
</li>
<li class="mr-1">
<button class="tab-button py-3 px-6 text-lg font-medium text-gray-600 hover:text-blue-700 hover:border-blue-300 border-b-2 border-transparent transition-colors duration-200" data-tab="documentation-tasks">Documentation Tasks</button>
</li>
<li class="mr-1">
<button class="tab-button py-3 px-6 text-lg font-medium text-gray-600 hover:text-blue-700 hover:border-blue-300 border-b-2 border-transparent transition-colors duration-200" data-tab="daily-log">Daily Log</button>
</li>
<li class="mr-1">
<button class="tab-button py-3 px-6 text-lg font-medium text-gray-600 hover:text-blue-700 hover:border-blue-300 border-b-2 border-transparent transition-colors duration-200" data-tab="reports">Reports</button>
</li>
</ul>
</nav>
<!-- Tab Content -->
<div class="tab-contents bg-gray-50 p-6 rounded-lg shadow-inner">
<!-- Overview Tab Content -->
<div id="overview" class="tab-content active">
<h2 class="text-3xl font-semibold text-gray-800 mb-4">Dashboard Overview</h2>
<p class="text-gray-700 mb-4">
Welcome to your team's central hub. Here you can see a summary of current projects, team performance, and important alerts.
</p>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 text-left mb-8">
<div class="bg-white p-5 rounded-lg shadow">
<h3 class="text-xl font-semibold text-blue-700 mb-2">Total Active Tasks</h3>
<p id="active-tasks-count" class="text-gray-600 text-3xl font-bold">0</p>
</div>
<div class="bg-white p-5 rounded-lg shadow">
<h3 class="text-xl font-semibold text-green-700 mb-2">Total Team Members</h3>
<p id="team-members-count" class="text-gray-600 text-3xl font-bold">0</p>
</div>
<div class="bg-white p-5 rounded-lg shadow">
<h3 class="text-xl font-semibold text-purple-700 mb-2">Total Assets</h3>
<p id="total-assets-count" class="text-gray-600 text-3xl font-bold">0</p>
</div>
<div class="bg-white p-5 rounded-lg shadow">
<h3 class="text-xl font-semibold text-teal-700 mb-2">Tasks Completed</h3>
<p id="tasks-completed-count" class="text-gray-600 text-3xl font-bold">0</p>
</div>
<div class="bg-white p-5 rounded-lg shadow">
<h3 class="text-xl font-semibold text-indigo-700 mb-2">Total Daily Logs Today</h3>
<p id="daily-logs-today-count" class="text-gray-600 text-3xl font-bold">0</p>
</div>
</div>
<!-- Important Alerts Section -->
<div class="mb-8">
<h3 class="text-2xl font-semibold text-gray-800 mb-4">Important Alerts</h3>
<div id="important-alerts-list" class="text-left space-y-3">
<p class="text-gray-500 text-center">No alerts at the moment.</p>
</div>
</div>
<!-- New Overview Alert Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-left mt-8">
<div class="bg-yellow-100 p-5 rounded-lg shadow border-l-4 border-yellow-500">
<h3 class="text-xl font-semibold text-yellow-700 mb-2">Tasks with No Updates (2+ Days)</h3>
<p id="tasks-no-updates-count" class="text-yellow-800 text-3xl font-bold">0</p>
</div>
<div class="bg-red-100 p-5 rounded-lg shadow border-l-4 border-red-500">
<h3 class="text-xl font-semibold text-red-700 mb-2">Tasks with Issues</h3>
<p id="tasks-with-issues-count" class="text-red-800 text-3xl font-bold">0</p>
</div>
</div>
</div>
<!-- Team Members Tab Content -->
<div id="members" class="tab-content">
<h2 class="text-3xl font-semibold text-gray-800 mb-4">Team Members Management</h2>
<p class="text-gray-700 mb-4">
View and manage details for all team members. Add new members or update existing profiles.
</p>
<div id="team-members-list" class="text-left space-y-3">
<!-- Team members will be loaded here -->
<p class="text-gray-500 text-center">Loading team members...</p>
</div>
<button id="add-member-button" class="mt-6 bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full shadow">Add New Member</button>
</div>
<!-- Assets Tab Content -->
<div id="assets" class="tab-content">
<h2 class="text-3xl font-semibold text-gray-800 mb-4">Aircraft Assets Management</h2>
<p class="text-gray-700 mb-4">
Manage the aircraft components (Landing Gears, Engines, APUs).
</p>
<div id="assets-list" class="text-left space-y-3">
<!-- Assets will be loaded here -->
<p class="text-gray-500 text-center">Loading assets...</p>
</div>
<button id="add-asset-button" class="mt-6 bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full shadow">Add New Asset</button>
</div>
<!-- Documentation Tasks Tab Content (formerly Projects) -->
<div id="documentation-tasks" class="tab-content">
<h2 class="text-3xl font-semibold text-gray-800 mb-4">Documentation Tasks Tracking</h2>
<p class="text-gray-700 mb-4">
Track the progress of documentation tasks across different assets and stages.
</p>
<!-- Task Filters -->
<div class="flex flex-wrap gap-4 mb-6 p-4 bg-white rounded-lg shadow-sm">
<div class="flex-1 min-w-[150px]">
<label for="filter-asset-type" class="block text-gray-700 text-sm font-bold mb-1 text-left">Asset Type:</label>
<select id="filter-asset-type" class="shadow border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
<option value="">All Types</option>
<option value="Landing Gear">Landing Gear</option>
<option value="Engine">Engine</option>
<option value="APU">APU</option>
</select>
</div>
<div class="flex-1 min-w-[150px]">
<label for="filter-stage" class="block text-gray-700 text-sm font-bold mb-1 text-left">Stage:</label>
<select id="filter-stage" class="shadow border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
<option value="">All Stages</option>
<option value="Pre-processing">Pre-processing</option>
<option value="Verification">Verification</option>
<option value="Traces">Traces</option>
</select>
</div>
<div class="flex-1 min-w-[150px]">
<label for="filter-member" class="block text-gray-700 text-sm font-bold mb-1 text-left">Assigned Member:</label>
<select id="filter-member" class="shadow border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
<option value="">All Members</option>
<!-- Populated dynamically -->
</select>
</div>
<div class="flex-1 min-w-[150px] flex items-end">
<button id="apply-task-filters" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full shadow w-full">Apply Filters</button>
</div>
</div>
<!-- Documentation Tasks Table/Grid Header -->
<div class="task-grid-header hidden md:grid">
<div class="task-grid-cell">Client</div>
<div class="task-grid-cell">Asset Type</div>
<div class="task-grid-cell">MSN</div>
<div class="task-grid-cell">Reg. No.</div>
<div class="task-grid-cell">Stage</div>
<div class="task-grid-cell">Assigned To</div>
<div class="task-grid-cell">Batch Range</div>
<div class="task-grid-cell">Batches Rem.</div>
<div class="task-grid-cell">Status</div>
<div class="task-grid-cell">Issue</div>
<div class="task-grid-cell text-right">Last Updated</div>
<div class="task-grid-actions">Actions</div>
</div>
<div id="documentation-tasks-list" class="text-left space-y-3">
<!-- Documentation tasks will be loaded here -->
<p class="text-gray-500 text-center">Loading documentation tasks...</p>
</div>
<div class="flex justify-between mt-6">
<button id="create-documentation-task-button" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full shadow">Create New Task</button>
<button id="export-tasks-button" class="bg-purple-500 hover:bg-purple-600 text-white font-bold py-2 px-4 rounded-full shadow"><i class="fas fa-file-export mr-2"></i>Export Tasks</button>
</div>
</div>
<!-- Daily Log Tab Content -->
<div id="daily-log" class="tab-content">
<h2 class="text-3xl font-semibold text-gray-800 mb-4">Daily Progress Log</h2>
<p class="text-gray-700 mb-4">
Log daily progress and view historical updates for all documentation tasks.
</p>
<!-- Daily Log Filters -->
<div class="flex flex-wrap gap-4 mb-6 p-4 bg-white rounded-lg shadow-sm">
<div class="flex-1 min-w-[150px]">
<label for="filter-log-start-date" class="block text-gray-700 text-sm font-bold mb-1 text-left">Start Date:</label>
<input type="date" id="filter-log-start-date" class="shadow border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
</div>
<div class="flex-1 min-w-[150px]">
<label for="filter-log-end-date" class="block text-gray-700 text-sm font-bold mb-1 text-left">End Date:</label>
<input type="date" id="filter-log-end-date" class="shadow border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
</div>
<div class="flex-1 min-w-[150px] flex items-end">
<button id="apply-log-filters" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full shadow w-full">Apply Filters</button>
</div>
</div>
<button id="add-daily-log-entry-button" class="mb-6 bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-full shadow">Add Daily Log Entry</button>
<div id="daily-log-list" class="text-left space-y-3">
<!-- Daily log entries will be loaded here -->
<p class="text-gray-500 text-center">No daily log entries yet.</p>
</div>
<button id="export-daily-log-button" class="mt-6 bg-purple-500 hover:bg-purple-600 text-white font-bold py-2 px-4 rounded-full shadow"><i class="fas fa-file-export mr-2"></i>Export Daily Log</button>
</div>
<!-- Reports Tab Content -->
<div id="reports" class="tab-content">
<h2 class="text-3xl font-semibold text-gray-800 mb-4">Performance Reports</h2>
<p class="text-gray-700 mb-4">
Access detailed reports on team performance, operational efficiency, and more.
</p>
<ul class="text-left space-y-3">
<li class="bg-white p-4 rounded-lg shadow flex justify-between items-center">
<span class="font-medium text-gray-800">Monthly Performance Summary - July</span>
<button class="text-blue-600 hover:text-blue-800">Download PDF</button>
</li>
<li class="bg-white p-4 rounded-lg shadow flex justify-between items-center">
<span class="font-medium text-gray-800">Q2 Operations Report</span>
<button class="text-blue-600 hover:text-blue-800">View Online</button>
</li>
</ul>
</div>
</div>
</div>
<!-- Modals for Add Member, Edit Member, Add Asset, Edit Asset, Create Documentation Task, Edit Documentation Task, Daily Log -->
<!-- Add Member Modal -->
<div id="add-member-modal" class="modal">
<div class="modal-content">
<span class="close-button" onclick="closeModal('add-member-modal')">×</span>
<h3 class="text-2xl font-semibold text-gray-800 mb-4">Add New Team Member</h3>
<form id="add-member-form" class="space-y-4">
<div>
<label for="member-name" class="block text-gray-700 text-sm font-bold mb-2 text-left">Member Name:</label>
<input type="text" id="member-name" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
</div>
<div>
<label for="member-role" class="block text-gray-700 text-sm font-bold mb-2 text-left">Role:</label>
<input type="text" id="member-role" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
</div>
<button type="submit" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-full shadow">Add Member</button>
</form>
</div>
</div>
<!-- Edit Member Modal -->
<div id="edit-member-modal" class="modal">
<div class="modal-content">
<span class="close-button" onclick="closeModal('edit-member-modal')">×</span>
<h3 class="text-2xl font-semibold text-gray-800 mb-4">Edit Team Member</h3>
<form id="edit-member-form" class="space-y-4">
<input type="hidden" id="edit-member-id">
<div>
<label for="edit-member-name" class="block text-gray-700 text-sm font-bold mb-2 text-left">Member Name:</label>
<input type="text" id="edit-member-name" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
</div>
<div>
<label for="edit-member-role" class="block text-gray-700 text-sm font-bold mb-2 text-left">Role:</label>
<input type="text" id="edit-member-role" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
</div>
<button type="submit" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full shadow">Update Member</button>
</form>
</div>
</div>
<!-- Add Asset Modal -->
<div id="add-asset-modal" class="modal">
<div class="modal-content">
<span class="close-button" onclick="closeModal('add-asset-modal')">×</span>
<h3 class="text-2xl font-semibold text-gray-800 mb-4">Add New Aircraft Asset</h3>
<form id="add-asset-form" class="space-y-4">
<div>
<label for="asset-name" class="block text-gray-700 text-sm font-bold mb-2 text-left">Full Asset String (e.g., BRITAIR - Fitted Landing Gears to A/C G-EUUS 26.05.2025 MSN 3301 - Landing Gear - A320 Batches: - 173749-173924):</label>
<input type="text" id="asset-name" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required oninput="parseAssetDetailsFromInput('asset-name', 'asset-client-name', 'asset-type', 'asset-msn-number', 'asset-registration-number', 'asset-batches-assigned')">
</div>
<div>
<label for="asset-client-name" class="block text-gray-700 text-sm font-bold mb-2 text-left">Client Name:</label>
<input type="text" id="asset-client-name" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
</div>
<div>
<label for="asset-type" class="block text-gray-700 text-sm font-bold mb-2 text-left">Asset Type:</label>
<select id="asset-type" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
<option value="">Select Type</option>
<option value="Landing Gear">Landing Gear</option>
<option value="Engine">Engine</option>
<option value="APU">APU</option>
</select>
</div>
<div>
<label for="asset-msn-number" class="block text-gray-700 text-sm font-bold mb-2 text-left">MSN Number:</label>
<input type="text" id="asset-msn-number" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
</div>
<div>
<label for="asset-registration-number" class="block text-gray-700 text-sm font-bold mb-2 text-left">Registration Number (Optional):</label>
<input type="text" id="asset-registration-number" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
</div>
<div>
<label for="asset-status" class="block text-gray-700 text-sm font-bold mb-2 text-left">Asset Status:</label>
<select id="asset-status" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
<option value="In Progress">In Progress</option>
<option value="Completed">Completed</option>
<option value="On Hold">On Hold</option>
</select>
</div>
<div>
<label for="asset-batches-assigned" class="block text-gray-700 text-sm font-bold mb-2 text-left">Batches Assigned (e.g., "170112–170155"):</label>
<input type="text" id="asset-batches-assigned" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
</div>
<button type="submit" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-full shadow">Add Asset</button>
</form>
</div>
</div>
<!-- Edit Asset Modal -->
<div id="edit-asset-modal" class="modal">
<div class="modal-content">
<span class="close-button" onclick="closeModal('edit-asset-modal')">×</span>
<h3 class="text-2xl font-semibold text-gray-800 mb-4">Edit Aircraft Asset</h3>
<form id="edit-asset-form" class="space-y-4">
<input type="hidden" id="edit-asset-id">
<div>
<label for="edit-asset-name" class="block text-gray-700 text-sm font-bold mb-2 text-left">Full Asset String:</label>
<input type="text" id="edit-asset-name" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required oninput="parseAssetDetailsFromInput('edit-asset-name', 'edit-asset-client-name', 'edit-asset-type', 'edit-asset-msn-number', 'edit-asset-registration-number', 'edit-asset-batches-assigned')">
</div>
<div>
<label for="edit-asset-client-name" class="block text-gray-700 text-sm font-bold mb-2 text-left">Client Name:</label>
<input type="text" id="edit-asset-client-name" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
</div>
<div>
<label for="edit-asset-type" class="block text-gray-700 text-sm font-bold mb-2 text-left">Asset Type:</label>
<select id="edit-asset-type" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
<option value="">Select Type</option>
<option value="Landing Gear">Landing Gear</option>
<option value="Engine">Engine</option>
<option value="APU">APU</option>
</select>
</div>
<div>
<label for="edit-asset-msn-number" class="block text-gray-700 text-sm font-bold mb-2 text-left">MSN Number:</label>
<input type="text" id="edit-asset-msn-number" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
</div>
<div>
<label for="edit-asset-registration-number" class="block text-gray-700 text-sm font-bold mb-2 text-left">Registration Number (Optional):</label>
<input type="text" id="edit-asset-registration-number" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
</div>
<div>
<label for="edit-asset-status" class="block text-gray-700 text-sm font-bold mb-2 text-left">Asset Status:</label>
<select id="edit-asset-status" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
<option value="In Progress">In Progress</option>
<option value="Completed">Completed</option>
<option value="On Hold">On Hold</option>
</select>
</div>
<div>
<label for="edit-asset-batches-assigned" class="block text-gray-700 text-sm font-bold mb-2 text-left">Batches Assigned (e.g., "170112–170155"):</label>
<input type="text" id="edit-asset-batches-assigned" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
</div>
<button type="submit" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full shadow">Update Asset</button>
</form>
</div>
</div>
<!-- Create Documentation Task Modal -->
<div id="create-documentation-task-modal" class="modal">
<div class="modal-content">
<span class="close-button" onclick="closeModal('create-documentation-task-modal')">×</span>
<h3 class="text-2xl font-semibold text-gray-800 mb-4">Create New Documentation Task</h3>
<form id="create-documentation-task-form" class="space-y-4">
<div>
<label for="task-asset-id" class="block text-gray-700 text-sm font-bold mb-2 text-left">Asset:</label>
<select id="task-asset-id" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
<option value="">Select Asset</option>
<!-- Options populated dynamically -->
</select>
</div>
<!-- Display fields for asset details -->
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">Client:</label>
<span id="task-display-client-name" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">Asset Type:</label>
<span id="task-display-asset-type" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">MSN:</label>
<span id="task-display-msn-number" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">Registration No.:</label>
<span id="task-display-registration-number" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
<div class="col-span-2">
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">Batch Range:</label>
<span id="task-display-batch-range" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
</div>
<div>
<label for="task-current-stage" class="block text-gray-700 text-sm font-bold mb-2 text-left">Current Stage:</label>
<select id="task-current-stage" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
<option value="">Select Stage</option>
<option value="Pre-processing">Pre-processing</option>
<option value="Verification">Verification</option>
</select>
</div>
<div>
<label for="task-assigned-to-member-ids" class="block text-gray-700 text-sm font-bold mb-2 text-left">Assigned To (Multi-select):</label>
<select id="task-assigned-to-member-ids" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" multiple required size="5">
<!-- Options populated dynamically -->
</select>
</div>
<div>
<label for="task-batches-remaining" class="block text-gray-700 text-sm font-bold mb-2 text-left">Batches Remaining:</label>
<input type="number" id="task-batches-remaining" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" min="0" required>
</div>
<div>
<label for="task-status" class="block text-gray-700 text-sm font-bold mb-2 text-left">Status:</label>
<select id="task-status" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
<option value="Not Started">Not Started</option>
<option value="In Progress">In Progress</option>
<option value="Completed">Completed</option>
<option value="Blocked">Blocked</option>
</select>
</div>
<div>
<label for="task-due-date" class="block text-gray-700 text-sm font-bold mb-2 text-left">Due Date:</label>
<input type="date" id="task-due-date" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
</div>
<div>
<label for="task-issue-description" class="block text-gray-700 text-sm font-bold mb-2 text-left">Issue Description (Optional):</label>
<textarea id="task-issue-description" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" rows="3"></textarea>
</div>
<button type="submit" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-full shadow">Create Task</button>
</form>
</div>
</div>
<!-- Edit Documentation Task Modal -->
<div id="edit-documentation-task-modal" class="modal">
<div class="modal-content">
<span class="close-button" onclick="closeModal('edit-documentation-task-modal')">×</span>
<h3 class="text-2xl font-semibold text-gray-800 mb-4">Edit Documentation Task</h3>
<form id="edit-documentation-task-form" class="space-y-4">
<input type="hidden" id="edit-task-id">
<div>
<label for="edit-task-asset-id" class="block text-gray-700 text-sm font-bold mb-2 text-left">Asset:</label>
<select id="edit-task-asset-id" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
<option value="">Select Asset</option>
<!-- Options populated dynamically -->
</select>
</div>
<!-- Display fields for asset details in edit modal -->
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">Client:</label>
<span id="edit-task-display-client-name" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">Asset Type:</label>
<span id="edit-task-display-asset-type" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">MSN:</label>
<span id="edit-task-display-msn-number" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">Registration No.:</label>
<span id="edit-task-display-registration-number" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
<div class="col-span-2">
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">Batch Range:</label>
<span id="edit-task-display-batch-range" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
</div>
<div>
<label for="edit-task-current-stage" class="block text-gray-700 text-sm font-bold mb-2 text-left">Current Stage:</label>
<select id="edit-task-current-stage" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
<option value="">Select Stage</option>
<option value="Pre-processing">Pre-processing</option>
<option value="Verification">Verification</option>
<option value="Traces">Traces</option>
</select>
</div>
<div>
<label for="edit-task-assigned-to-member-ids" class="block text-gray-700 text-sm font-bold mb-2 text-left">Assigned To (Multi-select):</label>
<select id="edit-task-assigned-to-member-ids" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" multiple required size="5">
<!-- Options populated dynamically -->
</select>
</div>
<div>
<label for="edit-task-batches-remaining" class="block text-gray-700 text-sm font-bold mb-2 text-left">Batches Remaining:</label>
<input type="number" id="edit-task-batches-remaining" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" min="0" required>
</div>
<div>
<label for="edit-task-status" class="block text-gray-700 text-sm font-bold mb-2 text-left">Status:</label>
<select id="edit-task-status" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
<option value="Not Started">Not Started</option>
<option value="In Progress">In Progress</option>
<option value="Completed">Completed</option>
<option value="Blocked">Blocked</option>
</select>
</div>
<div>
<label for="edit-task-due-date" class="block text-gray-700 text-sm font-bold mb-2 text-left">Due Date:</label>
<input type="date" id="edit-task-due-date" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
</div>
<div>
<label for="edit-task-issue-description" class="block text-gray-700 text-sm font-bold mb-2 text-left">Issue Description (Optional):</label>
<textarea id="edit-task-issue-description" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" rows="3"></textarea>
</div>
<button type="submit" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full shadow">Update Task</button>
</form>
</div>
</div>
<!-- Daily Log Modal -->
<div id="daily-log-modal" class="modal">
<div class="modal-content">
<span class="close-button" onclick="closeModal('daily-log-modal')">×</span>
<h3 class="text-2xl font-semibold text-gray-800 mb-4">Add Daily Log Entry</h3>
<form id="daily-log-form" class="space-y-4">
<div>
<label for="log-date" class="block text-gray-700 text-sm font-bold mb-2 text-left">Date:</label>
<input type="date" id="log-date" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
</div>
<div>
<label for="log-member-id" class="block text-gray-700 text-sm font-bold mb-2 text-left">Team Member:</label>
<select id="log-member-id" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
<option value="">Select Member</option>
</select>
</div>
<div>
<label for="log-asset-id" class="block text-gray-700 text-sm font-bold mb-2 text-left">Asset:</label>
<select id="log-asset-id" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
<option value="">Select Asset</option>
</select>
</div>
<!-- Display fields for asset details in daily log modal -->
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">Client:</label>
<span id="log-display-client-name" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">Asset Type:</label>
<span id="log-display-asset-type" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">MSN:</label>
<span id="log-display-msn-number" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">Registration No.:</label>
<span id="log-display-registration-number" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
<div class="col-span-2">
<label class="block text-gray-700 text-sm font-bold mb-1 text-left">Batch Range:</label>
<span id="log-display-batch-range" class="block text-gray-800 py-2 px-3 bg-gray-100 rounded">N/A</span>
</div>
</div>
<div>
<label for="log-task-id" class="block text-gray-700 text-sm font-bold mb-2 text-left">Documentation Task:</label>
<select id="log-task-id" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required>
<option value="">Select Task</option>
<!-- Populated dynamically based on selected asset -->
</select>
</div>
<div>
<label for="log-batches-completed" class="block text-gray-700 text-sm font-bold mb-2 text-left">Batches Completed:</label>
<input type="number" id="log-batches-completed" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" min="0" required>
</div>
<div>
<label for="log-comments" class="block text-gray-700 text-sm font-bold mb-2 text-left">Comments (Optional):</label>
<textarea id="log-comments" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" rows="3"></textarea>
</div>
<button type="submit" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-full shadow">Submit Log</button>
</form>
</div>
</div>
<!-- Generic Confirmation Modal -->
<div id="confirmation-modal" class="modal">
<div class="modal-content max-w-sm">
<h3 class="text-xl font-semibold text-gray-800 mb-4" id="confirmation-message">Are you sure?</h3>
<div class="flex justify-end space-x-4">
<button id="confirm-cancel-button" class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded-full shadow">Cancel</button>
<button id="confirm-proceed-button" class="bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded-full shadow">Delete</button>
</div>
</div>
</div>
<!-- JavaScript for Firebase and Tab Functionality -->
<script type="module">
// Import the functions you need from the SDKs you need
import { initializeApp } from "https://www.gstatic.com/firebasejs/12.0.0/firebase-app.js";
import { getAnalytics } from "https://www.gstatic.com/firebasejs/12.0.0/firebase-analytics.js";
import { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged } from "https://www.gstatic.com/firebasejs/12.0.0/firebase-auth.js";
import { getFirestore, collection, addDoc, onSnapshot, query, doc, updateDoc, deleteDoc, getDocs, Timestamp, where } from "https://www.gstatic.com/firebasejs/12.0.0/firebase-firestore.js";
// Global variables for Firebase (provided by Canvas environment)
const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-app-id';
// Fallback Firebase config for deployment outside Canvas
// *** REPLACE THE VALUES BELOW WITH YOUR ACTUAL FIREBASE PROJECT CONFIGURATION ***
const defaultFirebaseConfig = {
apiKey: "AIzaSyCS_YHdiZt92T3s5AgBg9iW9wSC_y_9F8E",
authDomain: "main-ardur.firebaseapp.com",
projectId: "main-ardur",
storageBucket: "main-ardur.firebasestorage.app",
messagingSenderId: "169394115220",
appId: "1:169394115220:web:8dec3b5048e0e2d5b2da81",
measurementId: "G-SRQ1132728"
};
const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : defaultFirebaseConfig;
const initialAuthToken = typeof __initial_auth_token !== 'undefined' ? __initial_auth_token : null;
console.log("Using Firebase Config:", firebaseConfig); // Added for debugging
let app, db, auth, userId = 'loading...';
let isAuthReady = false;
let allTeamMembers = []; // To store team members for dropdowns
let allAssets = []; // To store assets for dropdowns
let allDocumentationTasksData = []; // To store all tasks for dynamic filtering
let allDailyUpdatesData = []; // To store all daily updates for filtering and export
// Function to open a modal
window.openModal = function(modalId) {
document.getElementById(modalId).style.display = 'flex';
}
// Function to close a modal
window.closeModal = function(modalId) {
document.getElementById(modalId).style.display = 'none';
}
// Helper to format date for display
function formatDate(dateString) {
const options = { year: 'numeric', month: 'long', day: 'numeric' };
try {
return new Date(dateString).toLocaleDateString(undefined, options);
} catch (e) {
return 'Invalid Date';
}
}
// Initialize Firebase and authenticate
async function initializeFirebaseAndAuth() {
try {
app = initializeApp(firebaseConfig);
// Initialize Analytics if measurementId is present
if (firebaseConfig.measurementId) {
getAnalytics(app); // Initialize analytics
}
db = getFirestore(app);
auth = getAuth(app);
onAuthStateChanged(auth, async (user) => {
if (user) {
userId = user.uid;
document.getElementById('user-id-display').textContent = `User ID: ${userId}`;
isAuthReady = true;
console.log("Firebase initialized and authenticated. User ID:", userId);
// Once authenticated, load data
await loadAllStaticData(); // Load members and assets first
loadTeamMembers(); // For the 'members' tab
loadAssets(); // For the 'assets' tab
loadDocumentationTasks(); // For the 'documentation-tasks' tab and alerts
loadDailyUpdates(); // For the 'daily-log' tab
} else {
console.log("No user found, attempting anonymous sign-in or custom token sign-in if available.");
if (initialAuthToken) {
try {
await signInWithCustomToken(auth, initialAuthToken);
console.log("Signed in with custom token.");
} catch (tokenError) {
console.error("Error with custom token, signing in anonymously:", tokenError);
await signInAnonymously(auth);
}
} else {
await signInAnonymously(auth);
console.log("Signed in anonymously.");
}
}
});
} catch (error) {
console.error("Error initializing Firebase or authenticating:", error);
document.getElementById('user-id-display').textContent = `User ID: Error (${error.message})`;
}
}
// Function to load all static data (members and assets) needed for dropdowns
async function loadAllStaticData() {
if (!isAuthReady || !db || !userId) {
console.log("Firebase not ready to load static data.");
return;
}
try {
// Fetch Team Members
const membersSnapshot = await getDocs(collection(db, `artifacts/${appId}/users/${userId}/teamMembers`));
allTeamMembers = membersSnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
console.log("Loaded allTeamMembers:", allTeamMembers);
// Populate filter-member dropdown
populateDropdown('filter-member', allTeamMembers, 'id', 'name');
// Fetch Assets
const assetsSnapshot = await getDocs(collection(db, `artifacts/${appId}/users/${userId}/assets`));
allAssets = assetsSnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
console.log("Loaded allAssets:", allAssets);
} catch (error) {
console.error("Error loading static data (members/assets):", error);
}
}
// Function to populate dropdowns (now handles multi-select)
function populateDropdown(selectElementId, dataArray, valueKey, textKey, selectedValues = []) {
const selectElement = document.getElementById(selectElementId);
if (!selectElement) return;
selectElement.innerHTML = ''; // Clear existing options
if (!selectElement.multiple) { // Add default option only for single select
selectElement.innerHTML = '<option value="">Select ' + textKey.replace('Name', '').trim() + '</option>';
}
dataArray.forEach(item => {
const option = document.createElement('option');
option.value = item[valueKey];
option.textContent = item[textKey];
if (Array.isArray(selectedValues) && selectedValues.includes(item[valueKey])) {
option.selected = true;
} else if (typeof selectedValues === 'string' && item[valueKey] === selectedValues) {
option.selected = true;
}
selectElement.appendChild(option);
});
}
// Function to show/hide tabs
function showTab(tabId) {
const tabContents = document.querySelectorAll('.tab-content');
const tabButtons = document.querySelectorAll('.tab-button');
tabContents.forEach(content => {
content.classList.remove('active');
});
tabButtons.forEach(button => {
button.classList.remove('border-blue-600', 'text-blue-800');
button.classList.add('border-transparent');
});
document.getElementById(tabId).classList.add('active');
const activeButton = document.querySelector(`.tab-button[data-tab="${tabId}"]`);
if (activeButton) {
activeButton.classList.add('border-blue-600', 'text-blue-800');
activeButton.classList.remove('border-transparent');
}
}
// Load Team Members from Firestore
function loadTeamMembers() {
if (!isAuthReady || !db || !userId) {
console.log("Firebase not ready to load team members.");
return;
}
const membersListDiv = document.getElementById('team-members-list');
membersListDiv.innerHTML = '<p class="text-gray-500 text-center">Loading team members...</p>'; // Loading indicator
const membersCollectionRef = collection(db, `artifacts/${appId}/users/${userId}/teamMembers`);
const q = query(membersCollectionRef);
onSnapshot(q, (snapshot) => {
document.getElementById('team-members-count').textContent = snapshot.size;
if (document.getElementById('members').classList.contains('active')) {
membersListDiv.innerHTML = '';
if (snapshot.empty) {
membersListDiv.innerHTML = '<p class="text-gray-500 text-center">No team members added yet.</p>';
} else {
snapshot.forEach((docEntry) => {
const member = docEntry.data();
const memberId = docEntry.id;
const memberElement = `
<li class="bg-white p-4 rounded-lg shadow flex justify-between items-center flex-wrap">
<span class="font-medium text-gray-800 flex-grow text-left">${member.name} - ${member.role}</span>
<div class="flex space-x-2 mt-2 md:mt-0">
<button class="text-blue-600 hover:text-blue-800 text-sm edit-member-button" data-id="${memberId}" data-name="${member.name}" data-role="${member.role}">Edit</button>
<button class="text-red-600 hover:text-red-800 text-sm delete-member-button" data-id="${memberId}" data-type="member">Delete</button>
</div>
</li>
`;
membersListDiv.insertAdjacentHTML('beforeend', memberElement);
});
document.querySelectorAll('.edit-member-button').forEach(button => {
button.addEventListener('click', function() {
const id = this.dataset.id;
const name = this.dataset.name;
const role = this.dataset.role;
document.getElementById('edit-member-id').value = id;
document.getElementById('edit-member-name').value = name;
document.getElementById('edit-member-role').value = role;
openModal('edit-member-modal');
});
});
document.querySelectorAll('.delete-member-button').forEach(button => {
button.addEventListener('click', function() {
const id = this.dataset.id;
openConfirmationModal('Are you sure you want to delete this team member?', async () => {
try {
await deleteDoc(doc(db, `artifacts/${appId}/users/${userId}/teamMembers`, id));
console.log("Member deleted successfully!");
} catch (error) {
console.error("Error deleting member:", error);
}
});
});
});
}
}
}, (error) => {
console.error("Error fetching team members:", error);
});
}
// Load Assets from Firestore
function loadAssets() {
if (!isAuthReady || !db || !userId) {
console.log("Firebase not ready to load assets.");
return;
}
const assetsListDiv = document.getElementById('assets-list');
assetsListDiv.innerHTML = '<p class="text-gray-500 text-center">Loading assets...</p>';
const assetsCollectionRef = collection(db, `artifacts/${appId}/users/${userId}/assets`);
const q = query(assetsCollectionRef);
onSnapshot(q, (snapshot) => {
document.getElementById('total-assets-count').textContent = snapshot.size;
if (document.getElementById('assets').classList.contains('active')) {
assetsListDiv.innerHTML = '';
if (snapshot.empty) {
assetsListDiv.innerHTML = '<p class="text-gray-500 text-center">No assets added yet.</p>';
} else {
snapshot.forEach((docEntry) => {
const asset = docEntry.data();
const assetId = docEntry.id;
const assetElement = `
<li class="bg-white p-4 rounded-lg shadow flex justify-between items-center flex-wrap">
<div class="text-left flex-grow">
<span class="font-medium text-gray-800">${asset.name} (${asset.type})</span>
<p class="text-gray-600 text-sm">Client: ${asset.clientName}</p>
<p class="text-gray-600 text-sm">MSN: ${asset.msnNumber}${asset.registrationNumber ? ' | Reg: ' + asset.registrationNumber : ''}</p>
<p class="text-gray-600 text-sm">Status: <span class="${asset.status === 'In Progress' ? 'text-blue-600' : asset.status === 'Completed' ? 'text-green-600' : 'text-yellow-600'} font-semibold">${asset.status}</span></p>
<p class="text-gray-600 text-sm">Batches: ${asset.batchesAssigned}</p>
</div>
<div class="flex space-x-2 mt-2 md:mt-0">
<button class="text-blue-600 hover:text-blue-800 text-sm edit-asset-button"
data-id="${assetId}"
data-name="${asset.name}"
data-type="${asset.type}"
data-client-name="${asset.clientName}"
data-msn-number="${asset.msnNumber}"
data-registration-number="${asset.registrationNumber || ''}"
data-status="${asset.status}"
data-batches-assigned="${asset.batchesAssigned}">Edit</button>
<button class="text-red-600 hover:text-red-800 text-sm delete-asset-button" data-id="${assetId}" data-type="asset">Delete</button>
</div>
</li>
`;
assetsListDiv.insertAdjacentHTML('beforeend', assetElement);
});
document.querySelectorAll('.edit-asset-button').forEach(button => {
button.addEventListener('click', function() {
const id = this.dataset.id;
const name = this.dataset.name;
const type = this.dataset.type;
const clientName = this.dataset.clientName;
const msnNumber = this.dataset.msnNumber;
const registrationNumber = this.dataset.registrationNumber;
const status = this.dataset.status;
const batchesAssigned = this.dataset.batchesAssigned;
document.getElementById('edit-asset-id').value = id;
document.getElementById('edit-asset-name').value = name;
document.getElementById('edit-asset-type').value = type;
document.getElementById('edit-asset-client-name').value = clientName;
document.getElementById('edit-asset-msn-number').value = msnNumber;
document.getElementById('edit-asset-registration-number').value = registrationNumber;
document.getElementById('edit-asset-status').value = status;
document.getElementById('edit-asset-batches-assigned').value = batchesAssigned;
openModal('edit-asset-modal');
});
});
document.querySelectorAll('.delete-asset-button').forEach(button => {
button.addEventListener('click', function() {
const id = this.dataset.id;
openConfirmationModal('Are you sure you want to delete this asset?', async () => {
try {
await deleteDoc(doc(db, `artifacts/${appId}/users/${userId}/assets`, id));
console.log("Asset deleted successfully!");
} catch (error) {
console.error("Error deleting asset:", error);
}
});
});
});
}
}
}, (error) => {
console.error("Error fetching assets:", error);
});
}
// Function to display alerts (now based on documentation tasks)
function displayAlerts(documentationTasks) {
const alertsListDiv = document.getElementById('important-alerts-list');
alertsListDiv.innerHTML = ''; // Clear previous alerts
const today = new Date();
today.setHours(0, 0, 0, 0); // Normalize to start of day
let hasAlerts = false;
documentationTasks.forEach(task => {
const dueDate = new Date(task.dueDate);
dueDate.setHours(0, 0, 0, 0); // Normalize to start of day
// Overdue tasks
if (dueDate < today && task.status !== 'Completed') {
const alertElement = `
<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded-md shadow-sm">
<p class="font-bold">Overdue Task:</p>
<p>${task.assetName} - Stage: ${task.currentStage} - Due on ${task.dueDate}</p>
</div>
`;
alertsListDiv.insertAdjacentHTML('beforeend', alertElement);
hasAlerts = true;
}
// Tasks nearing deadline (within 7 days)
const sevenDaysFromNow = new Date(today);
sevenDaysFromNow.setDate(today.getDate() + 7);
if (dueDate >= today && dueDate <= sevenDaysFromNow && task.status !== 'Completed') {
const alertElement = `
<div class="bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 rounded-md shadow-sm">
<p class="font-bold">Task Nearing Deadline:</p>
<p>${task.assetName} - Stage: ${task.currentStage} - Due on ${task.dueDate}</p>
</div>
`;
alertsListDiv.insertAdjacentHTML('beforeend', alertElement);
hasAlerts = true;
}
});
if (!hasAlerts) {
alertsListDiv.innerHTML = '<p class="text-gray-500 text-center">No alerts at the moment.</p>';
}
}
// Load Documentation Tasks from Firestore with optional filters
function loadDocumentationTasks() {
if (!isAuthReady || !db || !userId) {
console.log("Firebase not ready to load documentation tasks.");
return;
}
const tasksListDiv = document.getElementById('documentation-tasks-list');
tasksListDiv.innerHTML = '<p class="text-gray-500 text-center">Loading documentation tasks...</p>';
const tasksCollectionRef = collection(db, `artifacts/${appId}/users/${userId}/documentationTasks`);
const q = query(tasksCollectionRef);
onSnapshot(q, (snapshot) => {
allDocumentationTasksData = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); // Store all tasks for dynamic filtering
let filteredTasks = allDocumentationTasksData;
// Apply filters from UI
const filterAssetType = document.getElementById('filter-asset-type').value;
const filterStage = document.getElementById('filter-stage').value;
const filterMember = document.getElementById('filter-member').value;
if (filterAssetType) {
filteredTasks = filteredTasks.filter(task => {
const asset = allAssets.find(a => a.id === task.assetId);
return asset && asset.type === filterAssetType;
});
}
if (filterStage) {
filteredTasks = filteredTasks.filter(task => task.currentStage === filterStage);
}
if (filterMember) {
filteredTasks = filteredTasks.filter(task => Array.isArray(task.assignedToMemberIds) && task.assignedToMemberIds.includes(filterMember));
}
document.getElementById('active-tasks-count').textContent = filteredTasks.length; // Update count based on filtered tasks
let tasksWithIssuesCount = 0;
let tasksNoUpdatesCount = 0;
let tasksCompletedCount = 0;
tasksListDiv.innerHTML = ''; // Clear previous content
if (filteredTasks.length === 0) {
tasksListDiv.innerHTML = '<p class="text-gray-500 text-center">No documentation tasks found with applied filters.</p>';
} else {
filteredTasks.forEach((task) => {
const taskId = task.id;
const asset = allAssets.find(a => a.id === task.assetId);
// Default values if asset is not found (shouldn't happen if data integrity is good)
const clientName = asset ? asset.clientName : 'N/A';
const assetType = asset ? asset.type : 'N/A';
const msnNumber = asset ? asset.msnNumber : 'N/A';
const registrationNumber = asset ? asset.registrationNumber : 'N/A';
const batchRange = asset ? asset.batchesAssigned : 'N/A';
// Determine classes for visual flags
let cardClasses = 'task-grid-row';
let badgesHtml = '';
// 1. Issue Alert
if (task.issueDescription && task.issueDescription.trim() !== '') {
cardClasses += ' issue-flag';
badgesHtml += `<span class="bg-red-500 text-white text-xs font-semibold px-2.5 py-0.5 rounded-full mr-2"><i class="fas fa-exclamation-triangle"></i> Issue</span>`;
tasksWithIssuesCount++;
}
// 2. Inactivity Alert
const today = new Date();
today.setHours(0, 0, 0, 0);
let lastUpdatedDate = null;
if (task.lastUpdated && task.lastUpdated.toDate) { // Check if it's a Firestore Timestamp
lastUpdatedDate = task.lastUpdated.toDate();
} else if (task.lastUpdated) { // If it's a string date
lastUpdatedDate = new Date(task.lastUpdated);
}
let formattedLastUpdated = 'N/A';
if (lastUpdatedDate) {
lastUpdatedDate.setHours(0, 0, 0, 0);
formattedLastUpdated = formatDate(lastUpdatedDate.toISOString().slice(0, 10)); // Format to YYYY-MM-DD for consistency
const diffTime = Math.abs(today.getTime() - lastUpdatedDate.getTime());
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
if (diffDays >= 2 && task.status !== 'Completed') { // Only flag if not completed
cardClasses += ' inactivity-flag';
badgesHtml += `<span class="bg-yellow-500 text-white text-xs font-semibold px-2.5 py-0.5 rounded-full mr-2"><i class="fas fa-clock"></i> No Update (${diffDays} days)</span>`;
tasksNoUpdatesCount++;
}
} else {
// If no lastUpdated field, and task is not completed, flag it as inactive
if (task.status !== 'Completed') {
cardClasses += ' inactivity-flag';
badgesHtml += `<span class="bg-yellow-500 text-white text-xs font-semibold px-2.5 py-0.5 rounded-full mr-2"><i class="fas fa-clock"></i> No Update (New Task)</span>`;
tasksNoUpdatesCount++;
}
}
// 3. Pending Batches Alert (if completed but batches remaining > 0)
if (task.batchesRemaining > 0 && task.status === 'Completed') {
cardClasses += ' batches-pending-flag';
badgesHtml += `<span class="bg-red-600 text-white text-xs font-semibold px-2.5 py-0.5 rounded-full mr-2"><i class="fas fa-exclamation-circle"></i> Batches Pending</span>`;
}
// Count completed tasks
if (task.status === 'Completed') {
tasksCompletedCount++;
}
// Ensure assignedToMemberNames is an array
const assignedMemberNames = Array.isArray(task.assignedToMemberNames) ? task.assignedToMemberNames.join(', ') : (task.assignedToMemberName || 'Unassigned');
const issueSection = task.issueDescription ? `
<p class="text-red-600 text-sm mt-1">Issue: ${task.issueDescription}</p>
<button class="text-green-600 hover:text-green-800 text-sm clear-issue-button" data-id="${taskId}">Clear Issue</button>
` : '';
const taskElement = `
<div class="${cardClasses}">
<div class="task-grid-cell">${clientName}</div>
<div class="task-grid-cell">${assetType}</div>
<div class="task-grid-cell">${msnNumber}</div>
<div class="task-grid-cell">${registrationNumber}</div>
<div class="task-grid-cell">${task.currentStage}</div>
<div class="task-grid-cell">${assignedMemberNames}</div>
<div class="task-grid-cell">${batchRange}</div>
<div class="task-grid-cell">${task.batchesRemaining}</div>
<div class="task-grid-cell">
<span class="${task.status === 'On Track' || task.status === 'Completed' ? 'text-green-600' : task.status === 'In Progress' ? 'text-blue-600' : 'text-red-600'} font-semibold">
${task.status}
</span>
</div>
<div class="task-grid-cell">
${task.issueDescription && task.issueDescription.trim() !== '' ? `<i class="fas fa-exclamation-circle text-red-500 mr-1"></i>Yes` : 'No'}
</div>
<div class="task-grid-cell text-right">${formattedLastUpdated}</div>
<div class="task-grid-actions">
<button class="text-blue-600 hover:text-blue-800 text-sm edit-documentation-task-button"
data-id="${taskId}"
data-asset-id="${task.assetId}"
data-current-stage="${task.currentStage}"
data-assigned-to-member-ids="${Array.isArray(task.assignedToMemberIds) ? task.assignedToMemberIds.join(',') : task.assignedToMemberId || ''}"
data-batches-remaining="${task.batchesRemaining}"
data-status="${task.status}"
data-due-date="${task.dueDate}"
data-issue-description="${task.issueDescription || ''}">Edit</button>
<button class="text-red-600 hover:text-red-800 text-sm delete-documentation-task-button" data-id="${taskId}" data-type="task">Delete</button>
</div>
</div>
`;
tasksListDiv.insertAdjacentHTML('beforeend', taskElement);
});
// Re-attach event listeners after rendering
document.querySelectorAll('.edit-documentation-task-button').forEach(button => {
button.addEventListener('click', function() {
const id = this.dataset.id;
const assetId = this.dataset.assetId;
const currentStage = this.dataset.currentStage;
const assignedToMemberIds = this.dataset.assignedToMemberIds ? this.dataset.assignedToMemberIds.split(',') : [];
const batchesRemaining = this.dataset.batchesRemaining;
const status = this.dataset.status;
const dueDate = this.dataset.dueDate;
const issueDescription = this.dataset.issueDescription;
document.getElementById('edit-task-id').value = id;
populateDropdown('edit-task-asset-id', allAssets, 'id', 'name', assetId);
// Manually trigger change to populate asset details in edit modal
updateAssetDetailsInTaskModal('edit-task-asset-id', 'edit-task-display-client-name', 'edit-task-display-asset-type', 'edit-task-display-msn-number', 'edit-task-display-registration-number', 'edit-task-display-batch-range');
document.getElementById('edit-task-current-stage').value = currentStage;
populateDropdown('edit-task-assigned-to-member-ids', allTeamMembers, 'id', 'name', assignedToMemberIds);
document.getElementById('edit-task-batches-remaining').value = batchesRemaining;
document.getElementById('edit-task-status').value = status;
document.getElementById('edit-task-due-date').value = dueDate;
document.getElementById('edit-task-issue-description').value = issueDescription;
openModal('edit-documentation-task-modal');
});
});
document.querySelectorAll('.delete-documentation-task-button').forEach(button => {
button.addEventListener('click', function() {
const id = this.dataset.id;
openConfirmationModal('Are you sure you want to delete this documentation task?', async () => {
try {
await deleteDoc(doc(db, `artifacts/${appId}/users/${userId}/documentationTasks`, id));
console.log("Documentation task deleted successfully!");
} catch (error) {
console.error("Error deleting documentation task:", error);
}
});
});
});
document.querySelectorAll('.clear-issue-button').forEach(button => {
button.addEventListener('click', async function() {
const id = this.dataset.id;
try {
await updateDoc(doc(db, `artifacts/${appId}/users/${userId}/documentationTasks`, id), {
issueDescription: ''
});
console.log("Issue cleared successfully!");
} catch (error) {
console.error("Error clearing issue:", error);
}
});
});
}
displayAlerts(allDocumentationTasksData); // Call existing alerts with all tasks
// Update new overview alert counts
document.getElementById('tasks-with-issues-count').textContent = allDocumentationTasksData.filter(task => task.issueDescription && task.issueDescription.trim() !== '').length;
document.getElementById('tasks-no-updates-count').textContent = allDocumentationTasksData.filter(task => {
const today = new Date();
today.setHours(0, 0, 0, 0);
let lastUpdatedDate = null;
if (task.lastUpdated && task.lastUpdated.toDate) {
lastUpdatedDate = task.lastUpdated.toDate();
} else if (task.lastUpdated) {
lastUpdatedDate = new Date(task.lastUpdated);
}
if (lastUpdatedDate) {
lastUpdatedDate.setHours(0, 0, 0, 0);
const diffTime = Math.abs(today.getTime() - lastUpdatedDate.getTime());
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return diffDays >= 2 && task.status !== 'Completed';
}
return task.status !== 'Completed'; // If no lastUpdated, and not completed, count as no update
}).length;
document.getElementById('tasks-completed-count').textContent = allDocumentationTasksData.filter(task => task.status === 'Completed').length;
}, (error) => {
console.error("Error fetching documentation tasks:", error);
});
}
// Load Daily Updates from Firestore
function loadDailyUpdates() {
if (!isAuthReady || !db || !userId) {
console.log("Firebase not ready to load daily updates.");
return;
}
const dailyLogListDiv = document.getElementById('daily-log-list');
dailyLogListDiv.innerHTML = '<p class="text-gray-500 text-center">Loading daily log entries...</p>';
const dailyUpdatesCollectionRef = collection(db, `artifacts/${appId}/users/${userId}/dailyUpdates`);
const q = query(dailyUpdatesCollectionRef);
onSnapshot(q, (snapshot) => {
allDailyUpdatesData = snapshot.docs.map(docEntry => ({ id: docEntry.id, ...docEntry.data() }));
let filteredDailyUpdates = allDailyUpdatesData;
// Apply date filters
const filterStartDate = document.getElementById('filter-log-start-date').value;
const filterEndDate = document.getElementById('filter-log-end-date').value;
if (filterStartDate) {
filteredDailyUpdates = filteredDailyUpdates.filter(log => new Date(log.date) >= new Date(filterStartDate));
}
if (filterEndDate) {
filteredDailyUpdates = filteredDailyUpdates.filter(log => new Date(log.date) <= new Date(filterEndDate));
}
// Calculate daily logs today
const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
const dailyLogsTodayCount = allDailyUpdatesData.filter(log => log.date === today).length;
document.getElementById('daily-logs-today-count').textContent = dailyLogsTodayCount;
if (document.getElementById('daily-log').classList.contains('active')) {
dailyLogListDiv.innerHTML = '';
if (filteredDailyUpdates.length === 0) { // Changed from .empty to .length === 0
dailyLogListDiv.innerHTML = '<p class="text-gray-500 text-center">No daily log entries found with applied filters.</p>';
} else {
// Sort in memory by createdAt (descending) to show most recent first
const sortedUpdates = filteredDailyUpdates.sort((a, b) => {
const createdAtA = a.createdAt ? a.createdAt.toDate() : new Date(0);
const createdAtB = b.createdAt ? b.createdAt.toDate() : new Date(0);
if (createdAtA > createdAtB) return -1;
if (createdAtA < createdAtB) return 1;
return 0;
});
sortedUpdates.forEach((logEntry) => {
const logElement = `
<div class="bg-white p-4 rounded-lg shadow">
<p class="text-gray-800 font-semibold">${formatDate(logEntry.date)} - ${logEntry.memberName}</p>
<p class="text-gray-700">Asset: ${logEntry.assetName} | Task Stage: ${logEntry.taskStage}</p>
<p class="text-gray-700">Batches Completed: ${logEntry.batchesCompleted}</p>
${logEntry.comments ? `<p class="text-gray-600 italic">Comments: "${logEntry.comments}"</p>` : ''}
</div>
`;
dailyLogListDiv.insertAdjacentHTML('beforeend', logElement);
});
}
}
}, (error) => {
console.error("Error fetching daily updates:", error);
});
}
// Add New Member functionality
document.getElementById('add-member-button').addEventListener('click', () => {
openModal('add-member-modal');
});
document.getElementById('add-member-form').addEventListener('submit', async (e) => {
e.preventDefault();
const name = document.getElementById('member-name').value;
const role = document.getElementById('member-role').value;
if (!name || !role) {
console.error("Member name and role are required.");
return;
}
if (!isAuthReady || !db || !userId) {
console.error("Firebase not ready to add member.");
return;
}
try {
await addDoc(collection(db, `artifacts/${appId}/users/${userId}/teamMembers`), {
name: name,
role: role,
createdAt: new Date()
});
console.log("Member added successfully!");
closeModal('add-member-modal');
document.getElementById('add-member-form').reset(); // Clear form
await loadAllStaticData(); // Reload members for dropdowns
} catch (error) {
console.error("Error adding member:", error);
}
});
// Edit Member functionality
document.getElementById('edit-member-form').addEventListener('submit', async (e) => {
e.preventDefault();
const id = document.getElementById('edit-member-id').value;
const name = document.getElementById('edit-member-name').value;
const role = document.getElementById('edit-member-role').value;
if (!id || !name || !role) {
console.error("Member ID, name, and role are required for editing.");
return;
}
if (!isAuthReady || !db || !userId) {
console.error("Firebase not ready to edit member.");
return;
}
try {
await updateDoc(doc(db, `artifacts/${appId}/users/${userId}/teamMembers`, id), {
name: name,
role: role
});
console.log("Member updated successfully!");
closeModal('edit-member-modal');
await loadAllStaticData(); // Reload members for dropdowns
} catch (error) {
console.error("Error updating member:", error);
}
});
// Function to parse asset details from a full asset string
window.parseAssetDetailsFromInput = function(inputElementId, clientNameId, assetTypeId, msnNumberId, registrationNumberId, batchesAssignedId) {
const fullAssetString = document.getElementById(inputElementId).value;
let clientName = '';
let assetType = '';
let msnNumber = '';
let registrationNumber = '';
let batchesAssigned = '';
// Attempt to parse based on the provided example format:
// ASU - A320ELGSS - MSN 5990 - Landing Gear - A320 Batches :- 173934-173939
// BRITAIR - Fitted Landing Gears to A/C G-EUUS 26.05.2025 MSN 3301 - Landing Gear - A320 Batches: - 173749-173924 ABBYY && Tasks: 14191-14345
// 1. Client Name: First part before " - "
const clientMatch = fullAssetString.match(/^([^-\s]+)\s*-/);
if (clientMatch) {
clientName = clientMatch[1].trim();
}
// 2. MSN Number: Look for "MSN " followed by digits
const msnMatch = fullAssetString.match(/MSN\s*(\d+)/i);
if (msnMatch) {
msnNumber = msnMatch[1].trim();
}
// 3. Registration Number: Look for "A/C ", "Reg ", "Reg No " followed by alphanumeric characters (case-insensitive)
// This regex now covers "A/C G-EUUS", "Reg N123", "Reg No: VT-ABC"
const regMatch = fullAssetString.match(/(?:A\/C|Reg(?:\s*No)?)\s*[:\s]*([A-Z0-9-]+)/i);
if (regMatch) {
registrationNumber = regMatch[1].trim();
}
// 4. Asset Type: Look for specific keywords
if (fullAssetString.includes('Landing Gear')) {
assetType = 'Landing Gear';
} else if (fullAssetString.includes('Engine')) {
assetType = 'Engine';
} else if (fullAssetString.includes('APU')) {
assetType = 'APU';
}
// 5. Batches Assigned: Look for "Batches :- " followed by the range
const batchesMatch = fullAssetString.match(/Batches\s*:\s*([0-9\s-]+)/i);
if (batchesMatch) {
batchesAssigned = batchesMatch[1].trim();
}
// Populate the fields
document.getElementById(clientNameId).value = clientName;
// Only update asset type if it was successfully extracted
if (assetType) {
document.getElementById(assetTypeId).value = assetType;
}
document.getElementById(msnNumberId).value = msnNumber;
document.getElementById(registrationNumberId).value = registrationNumber;
document.getElementById(batchesAssignedId).value = batchesAssigned;
}
// Add New Asset functionality
document.getElementById('add-asset-button').addEventListener('click', () => {
// Clear all fields when opening for a new asset
document.getElementById('add-asset-form').reset();
openModal('add-asset-modal');
});
document.getElementById('add-asset-form').addEventListener('submit', async (e) => {
e.preventDefault();
const name = document.getElementById('asset-name').value; // This is now the full string
const type = document.getElementById('asset-type').value;
const clientName = document.getElementById('asset-client-name').value;
const msnNumber = document.getElementById('asset-msn-number').value;
const registrationNumber = document.getElementById('asset-registration-number').value;
const status = document.getElementById('asset-status').value;
const batchesAssigned = document.getElementById('asset-batches-assigned').value;
if (!name || !type || !clientName || !msnNumber || !status || !batchesAssigned) {
console.error("All required asset fields must be filled.");
return;
}
if (!isAuthReady || !db || !userId) {
console.error("Firebase not ready to add asset.");
return;
}
try {
await addDoc(collection(db, `artifacts/${appId}/users/${userId}/assets`), {
name: name, // Store the full string as the asset name
type: type,
clientName: clientName,
msnNumber: msnNumber,
registrationNumber: registrationNumber,
status: status,
batchesAssigned: batchesAssigned,
createdAt: new Date()
});
console.log("Asset added successfully!");
closeModal('add-asset-modal');
document.getElementById('add-asset-form').reset(); // Clear form
await loadAllStaticData(); // Reload assets for dropdowns
} catch (error) {
console.error("Error adding asset:", error);
}
});
// Edit Asset functionality
document.getElementById('edit-asset-form').addEventListener('submit', async (e) => {
e.preventDefault();
const id = document.getElementById('edit-asset-id').value;
const name = document.getElementById('edit-asset-name').value; // This is now the full string
const type = document.getElementById('edit-asset-type').value;
const clientName = document.getElementById('edit-asset-client-name').value;
const msnNumber = document.getElementById('edit-asset-msn-number').value;
const registrationNumber = document.getElementById('edit-asset-registration-number').value;
const status = document.getElementById('edit-asset-status').value;
const batchesAssigned = document.getElementById('edit-asset-batches-assigned').value;
if (!id || !name || !type || !clientName || !msnNumber || !status || !batchesAssigned) {
console.error("All required asset fields must be filled for editing.");
return;
}
if (!isAuthReady || !db || !userId) {
console.error("Firebase not ready to edit asset.");
return;
}
try {
await updateDoc(doc(db, `artifacts/${appId}/users/${userId}/assets`, id), {
name: name, // Store the full string as the asset name
type: type,
clientName: clientName,
msnNumber: msnNumber,
registrationNumber: registrationNumber,
status: status,
batchesAssigned: batchesAssigned
});
console.log("Asset updated successfully!");
closeModal('edit-asset-modal');
await loadAllStaticData(); // Reload assets for dropdowns
} catch (error) {
console.error("Error updating asset:", error);
}
});
// Helper function to update asset details display in modals (for Documentation Task and Daily Log)
function updateAssetDetailsInTaskModal(assetSelectId, clientSpanId, typeSpanId, msnSpanId, regSpanId, batchSpanId) {
const assetSelect = document.getElementById(assetSelectId);
const selectedAssetId = assetSelect.value;
const clientSpan = document.getElementById(clientSpanId);
const typeSpan = document.getElementById(typeSpanId);
const msnSpan = document.getElementById(msnSpanId);
const regSpan = document.getElementById(regSpanId);
const batchSpan = document.getElementById(batchSpanId);
const asset = allAssets.find(a => a.id === selectedAssetId);
if (asset) {
clientSpan.textContent = asset.clientName || 'N/A';
typeSpan.textContent = asset.type || 'N/A';
msnSpan.textContent = asset.msnNumber || 'N/A';
regSpan.textContent = asset.registrationNumber || 'N/A';
batchSpan.textContent = asset.batchesAssigned || 'N/A';
} else {
clientSpan.textContent = 'N/A';
typeSpan.textContent = 'N/A';
msnSpan.textContent = 'N/A';
regSpan.textContent = 'N/A';
batchSpan.textContent = 'N/A';
}
}
// Create New Documentation Task functionality
document.getElementById('create-documentation-task-button').addEventListener('click', () => {
// Populate asset and member dropdowns before opening the modal
populateDropdown('task-asset-id', allAssets, 'id', 'name');
populateDropdown('task-assigned-to-member-ids', allTeamMembers, 'id', 'name'); // Use new ID and pass for multi-select
document.getElementById('task-issue-description').value = ''; // Clear issue field
// Clear and reset asset details display
document.getElementById('task-asset-id').value = ''; // Ensure no asset is pre-selected
updateAssetDetailsInTaskModal('task-asset-id', 'task-display-client-name', 'task-display-asset-type', 'task-display-msn-number', 'task-display-registration-number', 'task-display-batch-range');
openModal('create-documentation-task-modal');
});
// Event listener for asset selection in Create Task Modal
document.getElementById('task-asset-id').addEventListener('change', function() {
updateAssetDetailsInTaskModal('task-asset-id', 'task-display-client-name', 'task-display-asset-type', 'task-display-msn-number', 'task-display-registration-number', 'task-display-batch-range');
});
document.getElementById('create-documentation-task-form').addEventListener('submit', async (e) => {
e.preventDefault();
const assetId = document.getElementById('task-asset-id').value;
const selectedStage = document.getElementById('task-current-stage').value; // This is the key
const assignedToMemberSelect = document.getElementById('task-assigned-to-member-ids');
const assignedToMemberIds = Array.from(assignedToMemberSelect.selectedOptions).map(option => option.value);
const assignedToMemberNames = Array.from(assignedToMemberSelect.selectedOptions).map(option => option.textContent);
const batchesRemaining = parseInt(document.getElementById('task-batches-remaining').value);
const status = document.getElementById('task-status').value;
const dueDate = document.getElementById('task-due-date').value;
const issueDescription = document.getElementById('task-issue-description').value.trim();
const asset = allAssets.find(a => a.id === assetId);
const assetName = asset ? asset.name : 'Unknown Asset';
if (!assetId || !selectedStage || assignedToMemberIds.length === 0 || isNaN(batchesRemaining) || !status || !dueDate) {
console.error("All required documentation task fields must be filled, and at least one member must be assigned.");
return;
}
if (!isAuthReady || !db || !userId) {
console.error("Firebase not ready to create documentation task.");
return;
}
try {
// Fetch existing tasks for this asset
const existingTasksQuery = query(collection(db, `artifacts/${appId}/users/${userId}/documentationTasks`), where("assetId", "==", assetId));
const existingTasksSnapshot = await getDocs(existingTasksQuery);
const existingTasks = existingTasksSnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
let preProcessingTask = existingTasks.find(t => t.currentStage === "Pre-processing");
let verificationTask = existingTasks.find(t => t.currentStage === "Verification");
// --- Handle Pre-processing Task ---
if (selectedStage === "Pre-processing") {
// User is explicitly setting up Pre-processing
const taskData = {
assetId: assetId,
assetName: assetName,
currentStage: "Pre-processing",
assignedToMemberIds: assignedToMemberIds,
assignedToMemberNames: assignedToMemberNames,
batchesRemaining: batchesRemaining,
status: status,
dueDate: dueDate,
issueDescription: issueDescription,
lastUpdated: Timestamp.now(),
};
if (preProcessingTask) {
await updateDoc(doc(db, `artifacts/${appId}/users/${userId}/documentationTasks`, preProcessingTask.id), taskData);
console.log("Updated existing Pre-processing task.");
} else {
await addDoc(collection(db, `artifacts/${appId}/users/${userId}/documentationTasks`), { ...taskData, createdAt: new Date() });
console.log("Created new Pre-processing task.");
}
// Ensure Verification task exists and is "Not Started"
const defaultVerificationTaskData = {
assetId: assetId,
assetName: assetName,
currentStage: "Verification",
assignedToMemberIds: [], // Clear assigned members
assignedToMemberNames: [], // Clear assigned names
batchesRemaining: 0, // No batches remaining initially
status: "Not Started",
dueDate: "", // No due date initially
issueDescription: "", // No issue initially
lastUpdated: Timestamp.now(),
};
if (verificationTask) {
await updateDoc(doc(db, `artifacts/${appId}/users/${userId}/documentationTasks`, verificationTask.id), defaultVerificationTaskData);
console.log("Updated existing Verification task to Not Started.");
} else {
await addDoc(collection(db, `artifacts/${appId}/users/${userId}/documentationTasks`), { ...defaultVerificationTaskData, createdAt: new Date() });
console.log("Created new Verification task as Not Started.");
}
} else if (selectedStage === "Verification") {
// User is explicitly setting up Verification, so Pre-processing is considered completed
const completedPreProcessingTaskData = {
assetId: assetId,
assetName: assetName,
currentStage: "Pre-processing",
assignedToMemberIds: [], // Clear assigned members
assignedToMemberNames: [], // Clear assigned names
batchesRemaining: 0, // All batches completed
status: "Completed",
dueDate: "", // Due date might be irrelevant or set to completion date
issueDescription: "", // No issue
lastUpdated: Timestamp.now(),
};
if (preProcessingTask) {
await updateDoc(doc(db, `artifacts/${appId}/users/${userId}/documentationTasks`, preProcessingTask.id), completedPreProcessingTaskData);
console.log("Updated existing Pre-processing task to Completed.");
} else {
await addDoc(collection(db, `artifacts/${appId}/users/${userId}/documentationTasks`), { ...completedPreProcessingTaskData, createdAt: new Date() });
console.log("Created new Pre-processing task as Completed.");
}
// Handle Verification task with user-provided data
const taskData = {
assetId: assetId,
assetName: assetName,
currentStage: "Verification",
assignedToMemberIds: assignedToMemberIds,
assignedToMemberNames: assignedToMemberNames,
batchesRemaining: batchesRemaining,
status: status,
dueDate: dueDate,
issueDescription: issueDescription,
lastUpdated: Timestamp.now(),
};
if (verificationTask) {
await updateDoc(doc(db, `artifacts/${appId}/users/${userId}/documentationTasks`, verificationTask.id), taskData);
console.log("Updated existing Verification task.");
} else {
await addDoc(collection(db, `artifacts/${appId}/users/${userId}/documentationTasks`), { ...taskData, createdAt: new Date() });
console.log("Created new Verification task.");
}
}
console.log("Documentation tasks processed successfully!");
closeModal('create-documentation-task-modal');
document.getElementById('create-documentation-task-form').reset(); // Clear form
// Re-populate dropdowns and asset details display after reset
populateDropdown('task-asset-id', allAssets, 'id', 'name');
populateDropdown('task-assigned-to-member-ids', allTeamMembers, 'id', 'name');
updateAssetDetailsInTaskModal('task-asset-id', 'task-display-client-name', 'task-display-asset-type', 'task-display-msn-number', 'task-display-registration-number', 'task-display-batch-range');
} catch (error) {
console.error("Error processing documentation tasks:", error);
}
});
// Edit Documentation Task functionality
document.getElementById('edit-documentation-task-form').addEventListener('submit', async (e) => {
e.preventDefault();
const id = document.getElementById('edit-task-id').value;
const assetId = document.getElementById('edit-task-asset-id').value;
const currentStage = document.getElementById('edit-task-current-stage').value;
// Get selected member IDs from the multi-select
const assignedToMemberSelect = document.getElementById('edit-task-assigned-to-member-ids');
const assignedToMemberIds = Array.from(assignedToMemberSelect.selectedOptions).map(option => option.value);
const assignedToMemberNames = Array.from(assignedToMemberSelect.selectedOptions).map(option => option.textContent);
const batchesRemaining = parseInt(document.getElementById('edit-task-batches-remaining').value);
const status = document.getElementById('edit-task-status').value;
const dueDate = document.getElementById('edit-task-due-date').value;
const issueDescription = document.getElementById('edit-task-issue-description').value.trim();
// Get assetName for display
const asset = allAssets.find(a => a.id === assetId);
const assetName = asset ? asset.name : 'Unknown Asset';
if (!id || !assetId || !currentStage || assignedToMemberIds.length === 0 || isNaN(batchesRemaining) || !status || !dueDate) {
console.error("All required documentation task fields are required for editing, and at least one member must be assigned.");
return;
}
if (!isAuthReady || !db || !userId) {
console.error("Firebase not ready to edit documentation task.");
return;
}
try {
await updateDoc(doc(db, `artifacts/${appId}/users/${userId}/documentationTasks`, id), {
assetId: assetId,
assetName: assetName,
currentStage: currentStage,
assignedToMemberIds: assignedToMemberIds, // Update as array
assignedToMemberNames: assignedToMemberNames, // Update as array
batchesRemaining: batchesRemaining,
status: status,
dueDate: dueDate,
issueDescription: issueDescription // New field
});
console.log("Documentation task updated successfully!");
closeModal('edit-documentation-task-modal');
} catch (error) {
console.error("Error updating documentation task:", error);
}
});
// Add Daily Log Entry functionality
document.getElementById('add-daily-log-entry-button').addEventListener('click', () => {
// Set today's date
const today = new Date();
const yyyy = today.getFullYear();
const mm = String(today.getMonth() + 1).padStart(2, '0'); // Months start at 0!
const dd = String(today.getDate()).padStart(2, '0');
document.getElementById('log-date').value = `${yyyy}-${mm}-${dd}`;
// Populate member and asset dropdowns
populateDropdown('log-member-id', allTeamMembers, 'id', 'name');
populateDropdown('log-asset-id', allAssets, 'id', 'name');
// Clear and disable task dropdown initially
const logTaskSelect = document.getElementById('log-task-id');
logTaskSelect.innerHTML = '<option value="">Select Task</option>';
logTaskSelect.disabled = true;
// Clear and reset asset details display
document.getElementById('log-asset-id').value = ''; // Ensure no asset is pre-selected
updateAssetDetailsInTaskModal('log-asset-id', 'log-display-client-name', 'log-display-asset-type', 'log-display-msn-number', 'log-display-registration-number', 'log-display-batch-range');
openModal('daily-log-modal');
});
// Dynamic task dropdown population and asset details update in Daily Log Modal
document.getElementById('log-asset-id').addEventListener('change', function() {
const selectedAssetId = this.value;
const logTaskSelect = document.getElementById('log-task-id');
logTaskSelect.innerHTML = '<option value="">Select Task</option>'; // Clear previous options
logTaskSelect.disabled = true; // Disable until tasks are loaded
updateAssetDetailsInTaskModal('log-asset-id', 'log-display-client-name', 'log-display-asset-type', 'log-display-msn-number', 'log-display-registration-number', 'log-display-batch-range');
if (selectedAssetId && allDocumentationTasksData.length > 0) {
const filteredTasks = allDocumentationTasksData.filter(task => task.assetId === selectedAssetId);
if (filteredTasks.length > 0) {
// Sort tasks for display (e.g., by stage, then batches remaining)
filteredTasks.sort((a, b) => {
const stageOrder = { "Pre-processing": 1, "Verification": 2, "Traces": 3 };
if (stageOrder[a.currentStage] !== stageOrder[b.currentStage]) {
return stageOrder[a.currentStage] - stageOrder[b.currentStage];
}
return a.batchesRemaining - b.batchesRemaining;
});
filteredTasks.forEach(task => {
const option = document.createElement('option');
option.value = task.id;
const assignedNames = Array.isArray(task.assignedToMemberNames) ? task.assignedToMemberNames.join(', ') : 'Unassigned';
option.textContent = `${task.currentStage} (Assigned: ${assignedNames}, Batches: ${task.batchesRemaining}, Status: ${task.status})`;
logTaskSelect.appendChild(option);
});
logTaskSelect.disabled = false;
} else {
logTaskSelect.innerHTML = '<option value="">No tasks for this asset</option>';
}
}
});
document.getElementById('daily-log-form').addEventListener('submit', async (e) => {
e.preventDefault();
const date = document.getElementById('log-date').value;
const memberId = document.getElementById('log-member-id').value;
const assetId = document.getElementById('log-asset-id').value;
const taskId = document.getElementById('log-task-id').value;
const batchesCompleted = parseInt(document.getElementById('log-batches-completed').value);
const comments = document.getElementById('log-comments').value.trim();
// Get names for display
const member = allTeamMembers.find(m => m.id === memberId);
const asset = allAssets.find(a => a.id === assetId);
const task = allDocumentationTasksData.find(t => t.id === taskId);
const memberName = member ? member.name : 'Unknown Member';
const assetName = asset ? asset.name : 'Unknown Asset';
const taskStage = task ? task.currentStage : 'Unknown Stage';
if (!date || !memberId || !assetId || !taskId || isNaN(batchesCompleted) || batchesCompleted < 0) {
console.error("All required daily log fields must be filled.");
return;
}
if (!isAuthReady || !db || !userId) {
console.error("Firebase not ready to add daily log entry.");
return;
}
try {
// Add daily log entry
await addDoc(collection(db, `artifacts/${appId}/users/${userId}/dailyUpdates`), {
date: date,
memberId: memberId,
memberName: memberName,
assetId: assetId,
assetName: assetName,
taskId: taskId,
taskStage: taskStage,
batchesCompleted: batchesCompleted,
comments: comments,
createdAt: Timestamp.now() // Use Firestore Timestamp for consistency
});
// Update batchesRemaining and lastUpdated in the corresponding documentation task
if (task) {
const newBatchesRemaining = Math.max(0, task.batchesRemaining - batchesCompleted); // Ensure it doesn't go below zero
await updateDoc(doc(db, `artifacts/${appId}/users/${userId}/documentationTasks`, taskId), {
batchesRemaining: newBatchesRemaining,
lastUpdated: Timestamp.now() // Update lastUpdated field
});
console.log(`Updated batchesRemaining for task ${taskId} to ${newBatchesRemaining}`);
}
console.log("Daily log entry added successfully!");
closeModal('daily-log-modal');
document.getElementById('daily-log-form').reset(); // Clear form
} catch (error) {
console.error("Error adding daily log entry:", error);
}
});
// --- Export Functions ---
// Function to export tasks to CSV
document.getElementById('export-tasks-button').addEventListener('click', () => {
const dataToExport = [];
// Get the currently filtered tasks from the displayed list
// This assumes loadDocumentationTasks has already filtered allDocumentationTasksData
const filteredTasksElements = document.querySelectorAll('#documentation-tasks-list > div');
filteredTasksElements.forEach(taskElement => {
const taskId = taskElement.querySelector('.edit-documentation-task-button').dataset.id;
const taskData = allDocumentationTasksData.find(t => t.id === taskId);
if (taskData) {
const asset = allAssets.find(a => a.id === taskData.assetId);
dataToExport.push({
'Client': asset ? asset.clientName : 'N/A',
'Asset Type': asset ? asset.type : 'N/A',
'MSN': asset ? asset.msnNumber : 'N/A',
'Registration Number': asset ? asset.registrationNumber : 'N/A',
'Stage': taskData.currentStage,
'Assigned To': Array.isArray(taskData.assignedToMemberNames) ? taskData.assignedToMemberNames.join(', ') : 'Unassigned',
'Batch Range': asset ? asset.batchesAssigned : 'N/A',
'Batches Remaining': taskData.batchesRemaining,
'Status': taskData.status,
'Issue': taskData.issueDescription && taskData.issueDescription.trim() !== '' ? 'Yes' : 'No',
'Last Updated': taskData.lastUpdated ? formatDate(taskData.lastUpdated.toDate().toISOString().slice(0, 10)) : 'N/A'
});
}
});
if (dataToExport.length === 0) {
alert('No tasks to export. Apply filters or add tasks.');
return;
}
const ws = XLSX.utils.json_to_sheet(dataToExport);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Documentation Tasks");
XLSX.writeFile(wb, "Ardur_Documentation_Tasks.csv");
});
// Function to export daily log entries to CSV
document.getElementById('export-daily-log-button').addEventListener('click', () => {
const dataToExport = [];
// Use the already filtered and sorted data from loadDailyUpdates
// This assumes loadDailyUpdates has already populated allDailyUpdatesData and filtered it based on UI filters
const filteredLogElements = document.querySelectorAll('#daily-log-list > div');
filteredLogElements.forEach(logElement => {
// Extract data from the displayed HTML elements or find in allDailyUpdatesData
// For simplicity, let's re-map from allDailyUpdatesData based on displayed elements or re-filter
// A more robust way would be to pass the filteredDailyUpdates array from loadDailyUpdates
// For now, let's re-filter based on current UI filters to ensure consistency
const filterStartDate = document.getElementById('filter-log-start-date').value;
const filterEndDate = document.getElementById('filter-log-end-date').value;
let currentFilteredLogs = allDailyUpdatesData;
if (filterStartDate) {
currentFilteredLogs = currentFilteredLogs.filter(log => new Date(log.date) >= new Date(filterStartDate));
}
if (filterEndDate) {
currentFilteredLogs = currentFilteredLogs.filter(log => new Date(log.date) <= new Date(filterEndDate));
}
currentFilteredLogs.forEach(logEntry => {
dataToExport.push({
'Date': formatDate(logEntry.date),
'Member Name': logEntry.memberName,
'Asset Name': logEntry.assetName,
'Task Stage': logEntry.taskStage,
'Batches Completed': logEntry.batchesCompleted,
'Comments': logEntry.comments || ''
});
});
});
if (dataToExport.length === 0) {
alert('No daily log entries to export. Apply filters or add entries.');
return;
}
const ws = XLSX.utils.json_to_sheet(dataToExport);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Daily Updates");
XLSX.writeFile(wb, "Ardur_Daily_Updates.csv");
});
// --- Confirmation Modal Logic ---
let confirmCallback = null;
function openConfirmationModal(message, callback) {
document.getElementById('confirmation-message').textContent = message;
confirmCallback = callback;
openModal('confirmation-modal');
}
document.getElementById('confirm-proceed-button').addEventListener('click', () => {
if (confirmCallback) {
confirmCallback();
}
closeModal('confirmation-modal');
confirmCallback = null; // Clear the callback
});
document.getElementById('confirm-cancel-button').addEventListener('click', () => {
closeModal('confirmation-modal');
confirmCallback = null; // Clear the callback
});
// Event listeners for tab buttons and filters
document.addEventListener('DOMContentLoaded', function() {
const tabButtons = document.querySelectorAll('.tab-button');
tabButtons.forEach(button => {
button.addEventListener('click', function() {
const tabId = this.dataset.tab;
showTab(tabId);
});
});
// Task filter apply button
document.getElementById('apply-task-filters').addEventListener('click', () => {
loadDocumentationTasks(); // Reload tasks with current filters
});
// Daily log filter apply button
document.getElementById('apply-log-filters').addEventListener('click', () => {
loadDailyUpdates(); // Reload daily updates with current filters
});
// Initialize Firebase and Auth first
initializeFirebaseAndAuth();
// Set initial active tab after Firebase is ready
showTab('overview');
});
</script>
<!-- Footer (Optional) -->
<footer class="mt-8 text-gray-500 text-sm">
© 2025 Ardur. All rights reserved.
</footer>