Files
Ty Clifford 03348cad79 - Implemented the full order lifecycle, secure customer tracker, automatic acceptance emails, configurable status templates, and guest/account CRM management.
Key areas: 
[OrderStatusService.php](/Users/tyemeclifford/Documents/GH/fatbottomgrille/app/Services/OrderStatusService.php), 
[AdminController.php](/Users/tyemeclifford/Documents/GH/fatbottomgrille/app/Controllers/AdminController.php), 
and 
[Database.php](/Users/tyemeclifford/Documents/GH/fatbottomgrille/app/Core/Database.php).
SQLite migration applied successfully. PHP/JS syntax, integration 
workflows, rendered pages, database integrity, and tracker authorization 
all passed. Invalid tracker tokens correctly return 403.
2026-06-14 13:35:31 -04:00

104 lines
3.8 KiB
JavaScript

(() => {
const savedTheme = window.localStorage.getItem("fatbottom-theme");
const preferredTheme = window.matchMedia?.("(prefers-color-scheme: light)").matches ? "light" : "dark";
document.documentElement.dataset.theme = savedTheme || preferredTheme;
const syncThemeButtons = () => {
document.querySelectorAll("[data-theme-toggle]").forEach((button) => {
button.textContent = document.documentElement.dataset.theme === "light" ? "Dark Mode" : "Light Mode";
});
};
syncThemeButtons();
document.querySelectorAll("[data-theme-toggle]").forEach((button) => {
button.addEventListener("click", () => {
const next = document.documentElement.dataset.theme === "light" ? "dark" : "light";
document.documentElement.dataset.theme = next;
window.localStorage.setItem("fatbottom-theme", next);
syncThemeButtons();
});
});
const navToggle = document.querySelector(".nav-toggle");
const nav = document.querySelector(".site-nav");
if (navToggle && nav) {
navToggle.addEventListener("click", () => {
const open = nav.classList.toggle("is-open");
navToggle.setAttribute("aria-expanded", String(open));
});
}
document.querySelectorAll(".flash-close").forEach((button) => {
button.addEventListener("click", () => button.closest(".flash")?.remove());
});
window.setTimeout(() => {
document.querySelectorAll(".flash").forEach((flash) => {
flash.style.opacity = "0";
window.setTimeout(() => flash.remove(), 220);
});
}, 7000);
document.querySelectorAll("[data-confirm]").forEach((element) => {
element.addEventListener("click", (event) => {
if (!window.confirm(element.dataset.confirm || "Continue?")) {
event.preventDefault();
}
});
});
const checkoutForm = document.querySelector("#checkout-form");
if (!checkoutForm || checkoutForm.dataset.squareEnabled !== "true") {
return;
}
const appId = checkoutForm.dataset.squareAppId;
const locationId = checkoutForm.dataset.squareLocationId;
const submit = document.querySelector("#checkout-submit");
const errors = document.querySelector("#card-errors");
let card;
const initializeSquare = async () => {
if (!window.Square || !appId || !locationId) {
return;
}
try {
const payments = window.Square.payments(appId, locationId);
card = await payments.card();
await card.attach("#card-container");
} catch (error) {
if (errors) {
errors.textContent = "Payment form could not be loaded. Please refresh and try again.";
}
if (submit) {
submit.disabled = true;
}
}
};
checkoutForm.addEventListener("submit", async (event) => {
if (!card || checkoutForm.dataset.tokenized === "true") {
return;
}
event.preventDefault();
submit.disabled = true;
submit.textContent = "Securing Payment...";
errors.textContent = "";
try {
const result = await card.tokenize();
if (result.status !== "OK") {
throw new Error(result.errors?.[0]?.message || "Card details could not be verified.");
}
document.querySelector("#square-token").value = result.token;
checkoutForm.dataset.tokenized = "true";
checkoutForm.submit();
} catch (error) {
errors.textContent = error.message || "Payment could not be processed.";
submit.disabled = false;
submit.textContent = "Try Payment Again";
}
});
initializeSquare();
})();