Superior Bay Marine of Alabama
CONFIDENTIAL • ADMINISTRATIVE USE
Employee Records & Credential Management
Superior Bay Marine of Alabama
Chickasaw Tankerman Services • Chickasaw Marine Services
EMPLOYEE MANAGEMENT

Employee Dashboard

Credential status and employee records at a glance.

Action Required

All credential items needing attention. Scroll to see the full list.

Employees by Boat / Group

Current active assignments.

Employee Updates Awaiting Review

Employee-submitted DL, TWIC, and MMC files. Verify original documents before approving; approval updates the existing employee folder and credential dates.

Checking…
Checking submissions…

Missing Paperwork ReviewCredential records and employee paperwork that need attention appear here.

Employee Directory

Quick access to current employee records.

⌕

Crew Change Sheet

Complete and save the actual crew change information for John Austin and Deirdre Ann each time a crew changes.

WHERE THIS SAVESEmployee Management → Crew Change Sheet → Saved Crew Changes. Shared office history and included in Backup & Recovery.
Not saved yet. Complete the crew change, then click Save Crew Change.
Who is getting ON and OFF? Current crew loads from the last saved crew change (or the employee boat assignment before the first one). Mark each person Getting ON, Getting OFF, or Staying On, and add incoming employees as needed.
Information required from your crew-change master: Position, Name, SS# Last 4, Date of Birth, TWIC Number, TWIC Expiration, Driver License Expiration, Hire Date, Date of Last Drug Test, ECS Trained, Location, Date On, and Date Off. Employee master information is reused automatically once it is saved.

Saved Crew Changes

Permanent history for this vessel. Open an earlier crew change to review, correct, or print it.

Credential & Annual Test Alerts

Expired credentials first, followed by items due within 90 days.

Missing Paperwork

Tracks credential paperwork and office documents without deleting employee history.

⌕

Active Employee Roster

Printable current employee list with contact, assignment, position, and overall credential status.

Credential Alert Report

Lists each specific expired or upcoming credential with expiration date and days remaining.

Missing Paperwork Report

Lists missing or unreviewed paperwork by employee so the office can follow up.

Boat / Group Employee Report

Choose a boat or department and print its current employee roster and credential status.

Former Employee Archive

Printable archive of employees who no longer work here. Their records remain retained and can be reactivated.

Data Backup

Export the current employee data to CSV for an additional office backup or spreadsheet review.

Former Employees

Records are retained here permanently and can be reactivated if an employee returns.

⌕
`);w.document.close()} function exportCSV(){const headers=["Employment Status","Employee Name","Phone Number","Email Address","Group / Boat","Position","Last 4 SSN","Date of Birth","Hire Date","ECS Trained","DL Expiration","DL Number","DL Issuing State","TWIC Expiration","TWIC Number","MMC Expiration","MMC Number","Medical Expiration","Last Drug Test","Next Drug Test Due","Last Alcohol Test","Next Alcohol Test Due","Credential / Testing Status","Former Date","Former Reason","Missing Paperwork","Notes"];const rows=employees.map(e=>{const s=calcStatus(e),p=paperworkSummary(e);return[e.status,e.name,e.phone,e.email,e.group,e.position,e.last4,e.dateOfBirth,e.hireDate,e.ecsTrained,e.dlExpiration,splitDlParts(e.dlNumber,e.dlState).number,splitDlParts(e.dlNumber,e.dlState).state,e.twicExpiration,e.twicNumber,e.mmcExpiration,e.mmcNumber,e.medicalExpiration,e.lastDrugTest,isAnnualTestingBoat(e)?addOneYearIso(e.lastDrugTest):"",e.lastAlcoholTest,"",s.code,e.formerDate,e.formerReason,p.missing.map(k=>DOC_LABEL[k]).join("; "),e.notes]});const csv=[headers,...rows].map(row=>row.map(v=>`"${String(v??"").replace(/"/g,'""')}"`).join(",")).join("\n");const blob=new Blob([csv],{type:"text/csv;charset=utf-8"}),a=document.createElement("a");a.href=URL.createObjectURL(blob);a.download="SBM_Employee_Export.csv";a.click();URL.revokeObjectURL(a.href)} function asInputDate(v){const normalized=normalizeEmployeeIsoDate(v);return validDateString(normalized)?normalized:""}function blankOrDate(id){const raw=val(id);return raw?normalizeEmployeeIsoDate(raw):null}function val(id){return document.getElementById(id).value}function setVal(id,v){document.getElementById(id).value=v??""}function openModal(id){document.getElementById(id).classList.add("open");document.body.classList.add("employee-modal-open")}function closeModal(id){document.getElementById(id).classList.remove("open");if(!document.querySelector(".modal-backdrop.open"))document.body.classList.remove("employee-modal-open")}function showFormSection(name){document.querySelectorAll(".form-section").forEach(s=>s.classList.toggle("active",s.dataset.section===name));document.querySelectorAll(".form-tab").forEach(s=>s.classList.toggle("active",s.dataset.formSection===name));if(name==="documents"&¤tEditIndex!==null)renderDocuments(employees[currentEditIndex])} let toastTimer;function toast(msg){const el=document.getElementById("toast");el.textContent=msg;el.classList.add("show");clearTimeout(toastTimer);toastTimer=setTimeout(()=>el.classList.remove("show"),2800)} document.querySelectorAll('input[type="date"]').forEach(input=>{input.min="1900-01-01";input.max="2099-12-31";}); document.querySelectorAll(".nav-item").forEach(b=>b.addEventListener("click",()=>setView(b.dataset.view))); document.querySelectorAll("[data-go]").forEach(b=>b.addEventListener("click",()=>setView(b.dataset.go))); document.querySelectorAll("[data-close]").forEach(b=>b.addEventListener("click",()=>closeModal(b.dataset.close))); document.querySelectorAll(".form-tab").forEach(b=>b.addEventListener("click",()=>showFormSection(b.dataset.formSection))); document.getElementById("activeSearch").addEventListener("input",renderActive);document.getElementById("activeGroupFilter").addEventListener("change",renderActive);document.getElementById("activeStatusFilter").addEventListener("change",renderActive);document.getElementById("formerSearch").addEventListener("input",renderFormer);document.getElementById("paperworkSearch").addEventListener("input",renderPaperwork);document.getElementById("paperworkGroupFilter").addEventListener("change",renderPaperwork);document.getElementById("paperworkFilter").addEventListener("change",renderPaperwork); document.getElementById("employeeForm").addEventListener("submit",saveEmployeeFromForm);document.getElementById("addBtn").addEventListener("click",newEmployee);document.getElementById("formerBtn").addEventListener("click",handleFormerButton);document.getElementById("confirmFormerBtn").addEventListener("click",confirmFormer);document.getElementById("formerConfirmCheck").addEventListener("change",e=>document.getElementById("confirmFormerBtn").disabled=!e.target.checked);document.getElementById("exportBtn").addEventListener("click",exportCSV);document.getElementById("printEmployeeBtn").addEventListener("click",printEmployeeRecord);document.getElementById("printViewedEmployeeBtn").addEventListener("click",printViewedEmployee);document.getElementById("editViewedEmployeeBtn").addEventListener("click",editViewedEmployee);document.getElementById("openViewedFilesBtn").addEventListener("click",()=>{const employee=employees[currentViewIndex];if(employee?._key)window.open('/my-documents.html?employeeKey='+encodeURIComponent(employee._key),'_blank','noopener');});document.getElementById("uploadDocBtn").addEventListener("click",uploadDocument);const employeeDocFile=document.getElementById("docUploadFile"),employeeDropzone=document.getElementById("employeeDropzone"),employeeDocType=document.getElementById("docUploadType"); if(employeeDocFile)employeeDocFile.addEventListener("change",()=>handleEmployeeUploadFile(employeeDocFile.files?.[0]||null)); if(employeeDocType)employeeDocType.addEventListener("change",()=>{renderDocumentTypeChoices();if(employeeDocType.value)employeeUploadStatus(`${DOC_LABEL[employeeDocType.value]||"Document type"} selected.`,"info")}); if(employeeDropzone){["dragenter","dragover"].forEach(type=>employeeDropzone.addEventListener(type,e=>{e.preventDefault();employeeDropzone.classList.add("dragover")}));["dragleave","drop"].forEach(type=>employeeDropzone.addEventListener(type,e=>{e.preventDefault();employeeDropzone.classList.remove("dragover")}));employeeDropzone.addEventListener("drop",e=>{const file=e.dataTransfer?.files?.[0];if(!file||!employeeDocFile)return;try{const dt=new DataTransfer();dt.items.add(file);employeeDocFile.files=dt.files}catch{}handleEmployeeUploadFile(file)})}document.querySelectorAll(".crew-boat-btn").forEach(b=>b.addEventListener("click",()=>setCrewChangeBoat(b.dataset.crewBoat)));document.getElementById("newCrewChangeBtn").addEventListener("click",()=>newCrewDraft(true));document.getElementById("saveCrewChangeBtn").addEventListener("click",saveCrewChange);document.getElementById("viewSavedCrewChangesBtn").addEventListener("click",showSavedCrewChanges);document.getElementById("printCrewChangeBtn").addEventListener("click",printCrewChangeReview);document.getElementById("addCrewPersonBtn").addEventListener("click",addCrewPerson);document.getElementById("reloadCurrentCrewBtn").addEventListener("click",reloadCurrentCrew);for(const id of ["crewChangeDate","crewChangeLocation"]){document.getElementById(id).addEventListener("change",()=>{syncCrewDraftFromInputs();renderCrewChange()});document.getElementById(id).addEventListener("input",()=>syncCrewDraftFromInputs())} document.getElementById("employeeModal").addEventListener("click",e=>{if(e.target.id==="employeeModal")closeModal("employeeModal")});document.getElementById("employeeViewModal").addEventListener("click",e=>{if(e.target.id==="employeeViewModal")closeModal("employeeViewModal")});document.getElementById("formerModal").addEventListener("click",e=>{if(e.target.id==="formerModal")closeModal("formerModal")}); document.getElementById("employeeLogoutBtn").addEventListener("click",async()=>{try{await employeeApi("/api/logout",{method:"POST"})}catch{}location.replace("/")}); async function bootstrapEmployeePortal(){ try{ const session=await employeeApi("/api/session"); if(!session.authenticated){location.replace("/?return="+encodeURIComponent(location.pathname+location.search));return} if(session.role!=="office"){location.replace("/operations/fleet/");return} document.getElementById("employeeUserBadge").textContent=`${session.name||"Office"} • OFFICE`; employees=await loadEmployees(); await loadCrewChanges(); newCrewDraft(true); renderAll(); }catch(err){console.error(err);location.replace("/?return="+encodeURIComponent(location.pathname+location.search))} } bootstrapEmployeePortal();