import { initializeApp } from "https://www.gstatic.com/firebasejs/10.14.0/firebase-app.js"; import { getDatabase, ref, push, set, remove, onValue, update } from "https://www.gstatic.com/firebasejs/10.14.0/firebase-database.js"; import { getAuth, GoogleAuthProvider, signInWithPopup, signOut, onAuthStateChanged, createUserWithEmailAndPassword, signInWithEmailAndPassword, sendPasswordResetEmail, sendEmailVerification, sendSignInLinkToEmail, isSignInWithEmailLink, signInWithEmailLink } from "https://www.gstatic.com/firebasejs/10.14.0/firebase-auth.js"; const firebaseConfig = { apiKey: "AIzaSyBUNW64TvBVS-FCBzY0KAGcqdq0gOrQhQw", authDomain: "companytracker-c2ed8.firebaseapp.com", databaseURL: "https://companytracker-c2ed8-default-rtdb.firebaseio.com", projectId: "companytracker-c2ed8", storageBucket: "companytracker-c2ed8.appspot.com", messagingSenderId: "472397366408", appId: "1:472397366408:web:4396f672863e73c7cdcb3d" }; const app = initializeApp(firebaseConfig); const db = getDatabase(app); const auth = getAuth(app); const provider = new GoogleAuthProvider(); let companyData = []; let currentUser = null; let isJenelle = false; let chartInstance = null; /* ------- Admin & allowed emails ------- */ const adminEmails = [ "gl2993@wayne.edu", "larava@wayne.edu", "ho5502@wayne.edu", "anushkaeggadi2006@gmail.com" ].map(x => x.toLowerCase()); // Only these emails can use Email/Password (or magic link); others must use Google const allowedEmailPasswordLogins = [ "ho5502@wayne.edu", "larava@wayne.edu", "gl2993@wayne.edu", ].map(x => x.toLowerCase()); /* ------- Filters state ------- */ let currentFilter = "all"; // 'all' | 'overdue' | 'week' | 'mine' let textSearch = ""; let industrySel = ""; let regionSel = ""; let typeSel = ""; /* ---------- Auth state ---------- */ onAuthStateChanged(auth, (user) => { if (user) { currentUser = user; document.getElementById("greeting").textContent = `π Welcome back, ${user.displayName || user.email}!`; document.getElementById("loginBtn").textContent = "Logout"; const email = (user.email || "").toLowerCase(); isJenelle = adminEmails.includes(email); if (isJenelle) { document.getElementById("adminBadge").classList.remove("hidden"); document.getElementById("addCompanyBtn").classList.remove("hidden"); } else { document.getElementById("adminBadge").classList.add("hidden"); document.getElementById("addCompanyBtn").classList.add("hidden"); } // Auto-send verification if logged in via Email/Password and not verified yet const usedPasswordProvider = (user.providerData || []).some(p => p.providerId === "password"); if (usedPasswordProvider && !user.emailVerified) { (async () => { try { await sendEmailVerification(user); } catch {} })(); } render(); } else { currentUser = null; isJenelle = false; document.getElementById("greeting").textContent = "π Welcome! Please sign in to start tracking companies π"; document.getElementById("loginBtn").textContent = "π Login with Google"; document.getElementById("adminBadge").classList.add("hidden"); document.getElementById("addCompanyBtn").classList.add("hidden"); render(); } }); /* ---------- Google login/logout ---------- */ document.getElementById("loginBtn").addEventListener("click", async () => { if (currentUser) { await signOut(auth); return; } try { await signInWithPopup(auth, provider); } catch (error) { alert("Login failed: " + error.message); } }); /* ---------- Email Login modal controls ---------- */ const emailModal = document.getElementById("emailModal"); const emailLoginBtn = document.getElementById("emailLoginBtn"); const closeEmailModal = document.getElementById("closeEmailModal"); const emailLoginForm = document.getElementById("emailLoginForm"); const emailRegisterBtn = document.getElementById("emailRegisterBtn"); const forgotBtn = document.getElementById("forgotBtn"); const magicLinkBtn = document.getElementById("magicLinkBtn"); // Remove resend if present const maybeResend = document.getElementById("resendVerifyBtn"); if (maybeResend) maybeResend.remove(); emailLoginBtn.addEventListener("click", () => emailModal.style.display = "block"); closeEmailModal?.addEventListener("click", () => emailModal.style.display = "none"); window.addEventListener("click", (e) => { if (e.target === emailModal) emailModal.style.display = "none"; }); /* ---------- Email/Password: Login (whitelisted only) ---------- */ emailLoginForm.addEventListener("submit", async (e) => { e.preventDefault(); const email = document.getElementById("emailInput").value.trim().toLowerCase(); const password = document.getElementById("passwordInput").value; if (!allowedEmailPasswordLogins.includes(email)) { alert("Please use 'Login with Google' for this email."); return; } try { await signInWithEmailAndPassword(auth, email, password); emailModal.style.display = "none"; } catch (err) { alert("Login failed: " + err.message); } }); /* ---------- Email/Password: Register (whitelisted only) ---------- */ emailRegisterBtn.addEventListener("click", async () => { const email = document.getElementById("emailInput").value.trim().toLowerCase(); const password = document.getElementById("passwordInput").value; if (!allowedEmailPasswordLogins.includes(email)) { alert("Registration with password is restricted. Please use 'Login with Google'."); return; } if (!email || !password) { alert("Enter email & password first."); return; } try { const cred = await createUserWithEmailAndPassword(auth, email, password); await sendEmailVerification(cred.user); alert("Account created. We sent a verification email β check your inbox/spam."); emailModal.style.display = "none"; } catch (err) { alert("Registration failed: " + err.message); } }); /* ---------- Forgot password (whitelisted only) ---------- */ forgotBtn.addEventListener("click", async () => { const email = document.getElementById("emailInput").value.trim().toLowerCase(); if (!email) { alert("Enter your email above first."); return; } if (!allowedEmailPasswordLogins.includes(email)) { alert("Password reset is only available for approved emails."); return; } try { await sendPasswordResetEmail(auth, email); alert("Password reset link sent. Check your inbox/spam."); } catch (err) { alert("Could not send reset email: " + err.message); } }); /* ---------- Magic Link (passwordless) for ho5502@wayne.edu ---------- */ const actionCodeSettings = { // π΄ REPLACE with your **exact** deployed URL url: "https://YOUR-SITE.netlify.app", handleCodeInApp: true }; magicLinkBtn.addEventListener("click", async () => { try { const email = document.getElementById("emailInput").value.trim().toLowerCase(); if (!email) { alert("Enter your email first."); return; } if (email !== "ho5502@wayne.edu") { alert("Magic link is only available for the approved .edu account."); return; } await sendSignInLinkToEmail(auth, email, actionCodeSettings); window.localStorage.setItem("emailForSignIn", email); alert("Magic sign-in link sent. Open it on this device."); } catch (e) { alert("Could not send magic link: " + e.message); } }); // If the user opened the magic link if (isSignInWithEmailLink(auth, window.location.href)) { let email = window.localStorage.getItem("emailForSignIn"); if (!email) email = prompt("Confirm your email to finish sign-in:"); if (email) { signInWithEmailLink(auth, email, window.location.href) .then(() => { window.localStorage.removeItem("emailForSignIn"); alert("Signed in via magic link."); }) .catch(err => alert("Magic link sign-in failed: " + err.message)); } } /* ---------- Load companies ---------- */ function loadCompanies() { const dbRef = ref(db, "companies"); onValue(dbRef, (snapshot) => { const data = snapshot.val() || {}; companyData = Object.entries(data).map(([id, v]) => ({ id, ...v })); render(); }); } /* ---------- Helpers: dates & filters ---------- */ function toDateOnly(d) { const dt = new Date(d); dt.setHours(0,0,0,0); return dt; } function isOverdue(followUp) { if (!followUp) return false; const today = toDateOnly(new Date()); const f = toDateOnly(new Date(followUp)); return f < today; } function isWithinThisWeek(followUp) { if (!followUp) return false; const today = toDateOnly(new Date()); const f = toDateOnly(new Date(followUp)); const end = toDateOnly(new Date()); end.setDate(today.getDate() + 7); return f >= today && f <= end; } function applyAllFilters(arr) { let out = [...arr]; // text search if (textSearch.trim()) { const q = textSearch.toLowerCase(); out = out.filter(c => (c.company||"").toLowerCase().includes(q) || (c.industry||"").toLowerCase().includes(q) || (c.region||"").toLowerCase().includes(q) || (c.type||"").toLowerCase().includes(q) || (c.description||"").toLowerCase().includes(q) ); } // dropdowns if (industrySel) out = out.filter(c => (c.industry||"") === industrySel); if (regionSel) out = out.filter(c => (c.region||"") === regionSel); if (typeSel) out = out.filter(c => (c.type||"") === typeSel); // pill filter if (currentFilter === "overdue") { out = out.filter(c => isOverdue(c.followUp)); } else if (currentFilter === "week") { out = out.filter(c => isWithinThisWeek(c.followUp)); } else if (currentFilter === "mine") { const me = (currentUser?.email || "").toLowerCase(); out = out.filter(c => (c.createdByEmail || "").toLowerCase() === me); } return out; } /* ---------- UI wiring for filters ---------- */ document.getElementById("filterAll").addEventListener("click", () => setPill("all")); document.getElementById("filterOverdue").addEventListener("click", () => setPill("overdue")); document.getElementById("filterWeek").addEventListener("click", () => setPill("week")); document.getElementById("filterMine").addEventListener("click", () => setPill("mine")); function setPill(name) { currentFilter = name; document.querySelectorAll(".pill").forEach(p => p.classList.remove("active")); const btn = document.querySelector(`.pill[data-filter="${name}"]`); if (btn) btn.classList.add("active"); render(); } document.getElementById("searchInput").addEventListener("input", (e) => { textSearch = e.target.value; render(); }); document.getElementById("industryFilter").addEventListener("change", (e) => { industrySel = e.target.value === "Industry" ? "" : e.target.value; render(); }); document.getElementById("regionFilter").addEventListener("change", (e) => { regionSel = e.target.value === "Region" ? "" : e.target.value; render(); }); document.getElementById("typeFilter").addEventListener("change", (e) => { typeSel = e.target.value === "Type" ? "" : e.target.value; render(); }); /* ---------- Reminders (browser-only) ---------- */ document.getElementById("enableNotifsBtn").addEventListener("click", async () => { if (!("Notification" in window)) { alert("Notifications not supported in this browser."); return; } if (Notification.permission === "granted") { alert("Notifications already enabled."); return; } const perm = await Notification.requestPermission(); if (perm === "granted") alert("Browser alerts enabled!"); }); function maybeNotifyOverdue(list) { // soft notifications (no backend): only if user allowed notifications if (!("Notification" in window) || Notification.permission !== "granted") return; const overdue = list.filter(c => isOverdue(c.followUp)); if (overdue.length === 0) return; const names = overdue.slice(0, 3).map(c => c.company).join(", ") + (overdue.length > 3 ? "β¦" : ""); new Notification("Overdue follow-ups", { body: names || "Check your dashboard." }); } /* ---------- Populate dropdowns ---------- */ function populateFilters() { const industries = [...new Set(companyData.map(c => c.industry).filter(Boolean))]; const regions = [...new Set(companyData.map(c => c.region).filter(Boolean))]; const types = [...new Set(companyData.map(c => c.type).filter(Boolean))]; fillDropdown("industryFilter", "Industry", industries); fillDropdown("regionFilter", "Region", regions); fillDropdown("typeFilter", "Type", types); } function fillDropdown(id, label, items) { const select = document.getElementById(id); select.innerHTML = ``; items.forEach(item => { const opt = document.createElement("option"); opt.value = item; opt.textContent = item; select.appendChild(opt); }); } /* ---------- Tracking count & reminders summary ---------- */ function updateTrackingCount(filtered) { document.getElementById("trackCount").textContent = `Showing ${filtered.length} of ${companyData.length} companies π`; } function updateReminderSummary() { const overdue = companyData.filter(c => isOverdue(c.followUp)); if (overdue.length === 0) { document.getElementById("reminderSummary").textContent = "No overdue items."; return; } const top = overdue.slice(0, 3).map(c => `${c.company} (${c.followUp})`).join(" β’ "); document.getElementById("reminderSummary").innerHTML = `Overdue: ${overdue.length} ${top}`; } /* ---------- Chart ---------- */ function drawChart(data) { const ctx = document.getElementById("industryChart").getContext("2d"); const grouped = data.reduce((acc, c) => { const key = c.industry || "Unspecified"; acc[key] = (acc[key] || 0) + 1; return acc; }, {}); if (chartInstance) chartInstance.destroy(); chartInstance = new Chart(ctx, { type: "pie", data: { labels: Object.keys(grouped), datasets: [{ data: Object.values(grouped), backgroundColor: ["#c084fc", "#fbc8d4", "#b3e5fc", "#a78bfa", "#fde68a", "#bbf7d0", "#fecaca"] }] } }); } /*- hgftffffggf the and the ssanders fgfr the fun sat rug u egg abcde fghijk lmnopqrstuvwxyz ethtrhthrrreththerhtttttttttttttttttt th --*/ /* ---------- Display table ---------- */ function displayTable(data) { const tbody = document.querySelector("#companyTable tbody"); tbody.innerHTML = ""; if (data.length === 0) { tbody.innerHTML = "