707 lines
27 KiB
JavaScript
707 lines
27 KiB
JavaScript
(() => {
|
|
// ── API Config ──
|
|
const API_ENDPOINT = window.PROLOGY_CONFIG.API_URL;
|
|
|
|
// ── Per-field error messages ──
|
|
const FIELD_ERROR_MSG = {
|
|
name: "Please enter a valid name.",
|
|
email: "Please enter a valid email address.",
|
|
phone: "Please enter a valid Australian mobile number (04xx xxx xxx).",
|
|
message: "Please enter at least 10 characters.",
|
|
email_or_phone_required: "Please provide an email or phone number.",
|
|
};
|
|
|
|
// ── Client-side validators (mirror server rules) ──
|
|
const RX_EMAIL = /^[\w.+-]+@[\w-]+(\.[\w-]+)+$/;
|
|
const RX_NAME = /^[\p{L}\p{M}'\-\s.]{2,80}$/u;
|
|
|
|
function isValidAuMobile(phone) {
|
|
const raw = (phone || "").trim();
|
|
if (!raw || !/^[\d\s\-()]+$/.test(raw)) return false;
|
|
const digits = raw.replace(/\D/g, "");
|
|
return /^04\d{8}$/.test(digits);
|
|
}
|
|
|
|
function validateClient(fields) {
|
|
const bad = [];
|
|
if ("name" in fields && (!fields.name || !RX_NAME.test(fields.name.trim())))
|
|
bad.push("name");
|
|
if ("email" in fields && (!fields.email || !RX_EMAIL.test(fields.email.trim())))
|
|
bad.push("email");
|
|
if ("phone" in fields && !isValidAuMobile(fields.phone))
|
|
bad.push("phone");
|
|
if ("message" in fields && (!fields.message || fields.message.trim().length < 10))
|
|
bad.push("message");
|
|
return bad;
|
|
}
|
|
|
|
function clearFieldErrors(sr) {
|
|
if (!sr) return;
|
|
sr.querySelectorAll(".field-error").forEach((el) => el.remove());
|
|
sr.querySelectorAll(".invalid").forEach((el) => el.classList.remove("invalid"));
|
|
}
|
|
|
|
function showFieldErrors(sr, badKeys, fieldMap) {
|
|
if (!sr || !fieldMap) return false;
|
|
let shown = 0;
|
|
badKeys.forEach((key) => {
|
|
let names = fieldMap[key];
|
|
if (!names) return;
|
|
if (!Array.isArray(names)) names = [names];
|
|
names.forEach((name) => {
|
|
const input = sr.querySelector('[name="' + name + '"]');
|
|
if (!input) return;
|
|
input.classList.add("invalid");
|
|
const field = input.closest(".field");
|
|
if (field && !field.querySelector(".field-error")) {
|
|
const err = document.createElement("div");
|
|
err.className = "field-error";
|
|
err.textContent = FIELD_ERROR_MSG[key] || "Invalid value.";
|
|
field.appendChild(err);
|
|
shown++;
|
|
}
|
|
});
|
|
});
|
|
return shown > 0;
|
|
}
|
|
|
|
function bindClearOnInput(sr) {
|
|
if (!sr || sr._clearBound) return;
|
|
sr._clearBound = true;
|
|
sr.querySelectorAll("input, textarea, select").forEach((el) => {
|
|
el.addEventListener("input", () => {
|
|
if (!el.classList.contains("invalid")) return;
|
|
el.classList.remove("invalid");
|
|
const field = el.closest(".field");
|
|
const err = field && field.querySelector(".field-error");
|
|
if (err) err.remove();
|
|
});
|
|
});
|
|
}
|
|
|
|
function showButtonError(btnEl, msg) {
|
|
const origHTML = btnEl.dataset.origHtml || btnEl.innerHTML;
|
|
btnEl.dataset.origHtml = origHTML;
|
|
btnEl.innerHTML = "<span>⚠ " + msg + "</span>";
|
|
setTimeout(() => {
|
|
btnEl.innerHTML = origHTML;
|
|
btnEl.disabled = false;
|
|
delete btnEl.dataset.origHtml;
|
|
}, 3000);
|
|
}
|
|
|
|
// ── Shared submit function ──
|
|
async function submitToAPI(payload, btnEl, onSuccess, opts) {
|
|
const { sr, validateFields, fieldMap } = opts || {};
|
|
|
|
if (sr) {
|
|
clearFieldErrors(sr);
|
|
bindClearOnInput(sr);
|
|
}
|
|
|
|
// Client-side validation — avoid burning an API call on obvious errors
|
|
if (validateFields) {
|
|
const bad = validateClient(validateFields);
|
|
if (bad.length) {
|
|
const shown = showFieldErrors(sr, bad, fieldMap);
|
|
if (!shown) showButtonError(btnEl, "Please check your inputs and try again.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
btnEl.disabled = true;
|
|
const origHTML = btnEl.innerHTML;
|
|
btnEl.innerHTML = "<span>Sending...</span>";
|
|
|
|
const restore = () => {
|
|
btnEl.innerHTML = origHTML;
|
|
btnEl.disabled = false;
|
|
};
|
|
|
|
try {
|
|
const res = await fetch(API_ENDPOINT, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"X-Requested-With": "XMLHttpRequest",
|
|
},
|
|
body: JSON.stringify({
|
|
...payload,
|
|
source: window.location.href,
|
|
ts: Date.now(),
|
|
nonce: Math.random().toString(36).slice(2),
|
|
}),
|
|
});
|
|
|
|
const data = await res.json();
|
|
|
|
if (res.ok && data.ok) {
|
|
// Google Ads conversion — fires only on a successful submit
|
|
if (typeof gtag_report_conversion === "function") {
|
|
gtag_report_conversion();
|
|
}
|
|
onSuccess(restore);
|
|
} else {
|
|
let msg = data.error || "Something went wrong. Please try again.";
|
|
if (res.status === 429) {
|
|
msg = "Too many requests. Please try again later.";
|
|
} else if (/validation failed/i.test(msg)) {
|
|
const shown = showFieldErrors(sr, data.fields || [], fieldMap);
|
|
if (shown) {
|
|
restore();
|
|
return;
|
|
}
|
|
msg = "Please check your inputs and try again.";
|
|
}
|
|
btnEl.innerHTML = "<span>⚠ " + msg + "</span>";
|
|
setTimeout(restore, 3000);
|
|
}
|
|
} catch (e) {
|
|
btnEl.innerHTML =
|
|
"<span>⚠ Network error. Please try again.</span>";
|
|
setTimeout(restore, 3000);
|
|
}
|
|
}
|
|
|
|
// ── Shared Shadow DOM CSS ──
|
|
const MODAL_CSS = `
|
|
:host { display: none; }
|
|
:host(.open) { display: block; }
|
|
|
|
.overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(0,0,0,0.75);
|
|
backdrop-filter: blur(12px);
|
|
z-index: 9999;
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: center;
|
|
padding: 20px;
|
|
overflow-y: auto;
|
|
}
|
|
.box {
|
|
background: linear-gradient(145deg, #2A3B54, #1E293B);
|
|
border: 1px solid rgba(255,255,255,0.15);
|
|
border-radius: 24px;
|
|
width: 100%;
|
|
max-width: 580px;
|
|
max-height: calc(100vh - 40px);
|
|
overflow-y: auto;
|
|
padding: 36px;
|
|
margin: auto;
|
|
position: relative;
|
|
box-shadow: 0 40px 100px rgba(0,0,0,0.8);
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
color: white;
|
|
box-sizing: border-box;
|
|
}
|
|
.badge {
|
|
display: inline-block;
|
|
font-size: 0.7rem;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.12em;
|
|
color: #1A73E8;
|
|
background: rgba(26,115,232,0.15);
|
|
padding: 4px 12px;
|
|
border-radius: 4px;
|
|
margin-bottom: 10px;
|
|
}
|
|
h3 { font-size: 1.75rem; font-weight: 800; margin: 0 0 8px; color: white; }
|
|
.intro { font-size: 0.95rem; color: #94A3B8; line-height: 1.6; margin-bottom: 28px; }
|
|
.close {
|
|
position: absolute; top: 20px; right: 20px;
|
|
background: none; border: none; color: #94A3B8;
|
|
font-size: 1.4rem; cursor: pointer; line-height: 1;
|
|
padding: 4px 8px; border-radius: 6px; transition: color 0.2s;
|
|
}
|
|
.close:hover { color: white; }
|
|
.field { margin-bottom: 20px; }
|
|
.field-row { display: flex; gap: 16px; margin-bottom: 20px; }
|
|
.field-row .field { flex: 1; margin-bottom: 0; }
|
|
label {
|
|
display: block; font-size: 0.85rem; font-weight: 600;
|
|
color: #E2E8F0; margin-bottom: 8px;
|
|
}
|
|
.req { color: #f87171; font-size: 0.8em; margin-left: 2px; font-weight: 700; }
|
|
input, textarea, select {
|
|
width: 100%; box-sizing: border-box;
|
|
background: rgba(255,255,255,0.06);
|
|
border: 1px solid rgba(255,255,255,0.12);
|
|
border-radius: 12px;
|
|
padding: 12px 16px;
|
|
color: white;
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
font-size: 0.95rem;
|
|
outline: none;
|
|
transition: border-color 0.2s;
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
}
|
|
input:focus, textarea:focus, select:focus {
|
|
border-color: #1A73E8;
|
|
background: rgba(255,255,255,0.09);
|
|
}
|
|
input.invalid, textarea.invalid, select.invalid {
|
|
border-color: #f87171;
|
|
background: rgba(248,113,113,0.06);
|
|
}
|
|
.field-error {
|
|
color: #f87171;
|
|
font-size: 0.78rem;
|
|
margin-top: 6px;
|
|
line-height: 1.35;
|
|
}
|
|
input::placeholder, textarea::placeholder { color: #64748B; }
|
|
textarea { min-height: 120px; resize: vertical; }
|
|
select {
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%2394A3B8' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
|
|
background-repeat: no-repeat;
|
|
background-position: right 14px center;
|
|
padding-right: 36px;
|
|
cursor: pointer;
|
|
}
|
|
select option { background: #1E293B; color: white; }
|
|
.btn-submit {
|
|
width: 100%; padding: 16px;
|
|
background: #1A73E8; color: white;
|
|
border: none; border-radius: 14px;
|
|
font-size: 1rem; font-weight: 700;
|
|
cursor: pointer; display: flex;
|
|
align-items: center; justify-content: center;
|
|
gap: 10px; transition: background 0.2s, transform 0.2s;
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
margin-top: 8px;
|
|
}
|
|
.btn-submit:hover { background: #174ea6; transform: translateY(-2px); }
|
|
.btn-submit svg { flex-shrink: 0; }
|
|
.btn-submit:disabled {
|
|
opacity: 0.7;
|
|
cursor: not-allowed;
|
|
transform: none !important;
|
|
}
|
|
`;
|
|
|
|
const ARROW_SVG = `<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"/></svg>`;
|
|
|
|
// ── Base class ──
|
|
class ProloModal extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
this.attachShadow({ mode: "open" });
|
|
}
|
|
|
|
open(prefillEmail = "") {
|
|
this.classList.add("open");
|
|
document.body.style.overflow = "hidden";
|
|
if (prefillEmail) {
|
|
const emailInput = this.shadowRoot.querySelector(
|
|
'input[type="email"]',
|
|
);
|
|
if (emailInput) emailInput.value = prefillEmail;
|
|
}
|
|
requestAnimationFrame(() => {
|
|
const first = this.shadowRoot.querySelector("input, textarea");
|
|
if (first) first.focus();
|
|
});
|
|
}
|
|
|
|
close() {
|
|
this.classList.remove("open");
|
|
document.body.style.overflow = "";
|
|
const form = this.shadowRoot.querySelector("form");
|
|
if (form) form.reset();
|
|
}
|
|
|
|
_bindClose() {
|
|
this.shadowRoot
|
|
.querySelector(".close")
|
|
.addEventListener("click", () => this.close());
|
|
document.addEventListener("keydown", (e) => {
|
|
if (e.key === "Escape" && this.classList.contains("open"))
|
|
this.close();
|
|
});
|
|
}
|
|
}
|
|
|
|
// ── 1. Sell / Valuation Modal ──
|
|
class PrologySellModal extends ProloModal {
|
|
connectedCallback() {
|
|
this.shadowRoot.innerHTML = `
|
|
<style>${MODAL_CSS}</style>
|
|
<div class="overlay">
|
|
<div class="box">
|
|
<button class="close">✕</button>
|
|
<span class="badge">Asset Recovery</span>
|
|
<h3>Get a Valuation</h3>
|
|
<p class="intro">Tell us what hardware you have. Our team will provide a competitive buy-back offer within 4 business hours.</p>
|
|
<form id="f">
|
|
<div class="field">
|
|
<label>Contact Email <span class="req">*</span></label>
|
|
<input type="email" name="email" placeholder="name@company.com" required>
|
|
</div>
|
|
<div class="field">
|
|
<label>Inventory List / Part Numbers <span class="req">*</span></label>
|
|
<textarea name="inventory" placeholder="e.g. 20x WS-C3850-24P-S, 10x Nexus 9k..." required></textarea>
|
|
</div>
|
|
<div class="field">
|
|
<label>Condition</label>
|
|
<select name="condition">
|
|
<option value="used">Used / Working</option>
|
|
<option value="nib">New In Box</option>
|
|
<option value="no-power">Decommissioned / Faulty</option>
|
|
</select>
|
|
</div>
|
|
<button type="submit" class="btn-submit">
|
|
<span>Request Buy-Back Quote</span>${ARROW_SVG}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>`;
|
|
this._bindClose();
|
|
this.shadowRoot
|
|
.querySelector("form")
|
|
.addEventListener("submit", (e) => {
|
|
e.preventDefault();
|
|
const sr = this.shadowRoot;
|
|
const email = sr.querySelector('[name="email"]').value;
|
|
if (!email) return;
|
|
const inventory = sr.querySelector('[name="inventory"]').value;
|
|
const condition = sr.querySelector('[name="condition"]').value;
|
|
const btn = sr.querySelector(".btn-submit");
|
|
submitToAPI(
|
|
{
|
|
email,
|
|
message: `[Asset Recovery]\nCondition: ${condition}\n\n${inventory || "(no inventory listed)"}`,
|
|
channel: "sell",
|
|
},
|
|
btn,
|
|
(restore) => {
|
|
btn.innerHTML = "<span>✓ Request Sent!</span>";
|
|
setTimeout(() => {
|
|
this.close();
|
|
restore();
|
|
}, 1800);
|
|
},
|
|
{
|
|
sr,
|
|
validateFields: { email, message: inventory },
|
|
fieldMap: { email: "email", message: "inventory" },
|
|
},
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
// ── 2. Source / Quote Modal ──
|
|
class PrologySourceModal extends ProloModal {
|
|
connectedCallback() {
|
|
this.shadowRoot.innerHTML = `
|
|
<style>${MODAL_CSS}</style>
|
|
<div class="overlay">
|
|
<div class="box">
|
|
<button class="close">✕</button>
|
|
<span class="badge">Hardware Sourcing</span>
|
|
<h3>Request a Quote</h3>
|
|
<p class="intro">Looking for specific equipment? We source authorized and refurbished Cisco, HP, and more.</p>
|
|
<form id="f">
|
|
<div class="field">
|
|
<label>Contact Email <span class="req">*</span></label>
|
|
<input type="email" name="email" placeholder="name@company.com" required>
|
|
</div>
|
|
<div class="field">
|
|
<label>Specific Models / Requirements</label>
|
|
<textarea name="parts" placeholder="e.g. 5x C9200L-24T-4G, 2x ASA5506-X..."></textarea>
|
|
</div>
|
|
<div class="field">
|
|
<label>Required Timing</label>
|
|
<select name="timing">
|
|
<option value="urgent">Urgent (ASAP)</option>
|
|
<option value="1week">Within 1 Week</option>
|
|
<option value="budgeting">Budgeting / Future Project</option>
|
|
</select>
|
|
</div>
|
|
<button type="submit" class="btn-submit">
|
|
<span>Check Availability & Price</span>${ARROW_SVG}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>`;
|
|
this._bindClose();
|
|
this.shadowRoot
|
|
.querySelector("form")
|
|
.addEventListener("submit", (e) => {
|
|
e.preventDefault();
|
|
const sr = this.shadowRoot;
|
|
const email = sr.querySelector('[name="email"]').value;
|
|
if (!email) return;
|
|
const parts = sr.querySelector('[name="parts"]').value;
|
|
const timing = sr.querySelector('[name="timing"]').value;
|
|
const btn = sr.querySelector(".btn-submit");
|
|
submitToAPI(
|
|
{
|
|
email,
|
|
message: `[Hardware Sourcing]\nTiming: ${timing}\n\n${parts || "(no parts listed)"}`,
|
|
channel: "source",
|
|
},
|
|
btn,
|
|
(restore) => {
|
|
btn.innerHTML = "<span>✓ Request Sent!</span>";
|
|
setTimeout(() => {
|
|
this.close();
|
|
restore();
|
|
}, 1800);
|
|
},
|
|
{
|
|
sr,
|
|
validateFields: { email },
|
|
fieldMap: { email: "email", message: "parts" },
|
|
},
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
// ── 3. Quote Modal (Request a Quote from Who We Serve) ──
|
|
class PrologyQuoteModal extends ProloModal {
|
|
connectedCallback() {
|
|
this.shadowRoot.innerHTML = `
|
|
<style>${MODAL_CSS}</style>
|
|
<div class="overlay">
|
|
<div class="box">
|
|
<button class="close">✕</button>
|
|
<span class="badge">IT Procurement</span>
|
|
<h3>Request a Quote</h3>
|
|
<p class="intro">Please provide details about your project or hardware requirements. Our team will verify stock and get back to you within 24 hours.</p>
|
|
<form id="f">
|
|
<div class="field-row">
|
|
<div class="field"><label>Name <span class="req">*</span></label><input type="text" name="name" placeholder="John Doe" required></div>
|
|
<div class="field"><label>Email <span class="req">*</span></label><input type="email" name="email" placeholder="john@company.com" required></div>
|
|
</div>
|
|
<div class="field-row">
|
|
<div class="field"><label>Company (Optional)</label><input type="text" name="company" placeholder="Company Ltd."></div>
|
|
<div class="field"><label>Phone Number <span class="req">*</span></label><input type="tel" name="phone" placeholder="04xx xxx xxx" inputmode="tel" autocomplete="tel" required></div>
|
|
</div>
|
|
<div class="field">
|
|
<label>Equipment Requirements <span class="req">*</span></label>
|
|
<textarea name="requirements" placeholder="e.g. 10x Cisco C9200L-48P-4G..." required></textarea>
|
|
</div>
|
|
<div class="field">
|
|
<label>Shipping Destination</label>
|
|
<input type="text" name="destination" placeholder="City, Country (Optional)">
|
|
</div>
|
|
<button type="submit" class="btn-submit"><span>Submit Request</span>${ARROW_SVG}</button>
|
|
</form>
|
|
</div>
|
|
</div>`;
|
|
this._bindClose();
|
|
this.shadowRoot
|
|
.querySelector("form")
|
|
.addEventListener("submit", (e) => {
|
|
e.preventDefault();
|
|
const sr = this.shadowRoot;
|
|
const name = sr.querySelector('[name="name"]').value;
|
|
const email = sr.querySelector('[name="email"]').value;
|
|
const phone = sr.querySelector('[name="phone"]').value;
|
|
const company = sr.querySelector('[name="company"]').value;
|
|
const requirements = sr.querySelector(
|
|
'[name="requirements"]',
|
|
).value;
|
|
const destination = sr.querySelector(
|
|
'[name="destination"]',
|
|
).value;
|
|
const btn = sr.querySelector(".btn-submit");
|
|
submitToAPI(
|
|
{
|
|
name,
|
|
email,
|
|
phone,
|
|
message: `[Quote Request]\nCompany: ${company || "N/A"}\nDestination: ${destination || "N/A"}\n\n${requirements}`,
|
|
channel: "quote",
|
|
},
|
|
btn,
|
|
(restore) => {
|
|
btn.innerHTML = "<span>✓ Request Sent!</span>";
|
|
setTimeout(() => {
|
|
this.close();
|
|
restore();
|
|
}, 1800);
|
|
},
|
|
{
|
|
sr,
|
|
validateFields: { name, email, phone, message: requirements },
|
|
fieldMap: {
|
|
name: "name",
|
|
email: "email",
|
|
phone: "phone",
|
|
message: "requirements",
|
|
email_or_phone_required: ["email", "phone"],
|
|
},
|
|
},
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
// ── 4. Enquire Modal (Gov & Corp) ──
|
|
class PrologyEnquireModal extends ProloModal {
|
|
connectedCallback() {
|
|
this.shadowRoot.innerHTML = `
|
|
<style>${MODAL_CSS}</style>
|
|
<div class="overlay">
|
|
<div class="box">
|
|
<button class="close">✕</button>
|
|
<span class="badge">Government & Corporate</span>
|
|
<h3>Corporate Enquiry</h3>
|
|
<p class="intro">Get in touch with our specialized team to establish customized procurement workflows for your organization.</p>
|
|
<form id="f">
|
|
<div class="field-row">
|
|
<div class="field"><label>Name <span class="req">*</span></label><input type="text" name="name" placeholder="Jane Doe" required></div>
|
|
<div class="field"><label>Work Email <span class="req">*</span></label><input type="email" name="email" placeholder="jane@agency.gov" required></div>
|
|
</div>
|
|
<div class="field-row">
|
|
<div class="field"><label>Organization / Agency <span class="req">*</span></label><input type="text" name="org" placeholder="Organization Name" required></div>
|
|
<div class="field"><label>Contact Number <span class="req">*</span></label><input type="tel" name="phone" placeholder="04xx xxx xxx" inputmode="tel" autocomplete="tel" required></div>
|
|
</div>
|
|
<div class="field">
|
|
<label>Enquiry Details <span class="req">*</span></label>
|
|
<textarea name="details" placeholder="Please describe your procurement needs..." required></textarea>
|
|
</div>
|
|
<button type="submit" class="btn-submit"><span>Submit Enquiry</span>${ARROW_SVG}</button>
|
|
</form>
|
|
</div>
|
|
</div>`;
|
|
this._bindClose();
|
|
this.shadowRoot
|
|
.querySelector("form")
|
|
.addEventListener("submit", (e) => {
|
|
e.preventDefault();
|
|
const sr = this.shadowRoot;
|
|
const name = sr.querySelector('[name="name"]').value;
|
|
const email = sr.querySelector('[name="email"]').value;
|
|
const phone = sr.querySelector('[name="phone"]').value;
|
|
const org = sr.querySelector('[name="org"]').value;
|
|
const details = sr.querySelector('[name="details"]').value;
|
|
const btn = sr.querySelector(".btn-submit");
|
|
submitToAPI(
|
|
{
|
|
name,
|
|
email,
|
|
phone,
|
|
message: `[Corporate Enquiry]\nOrganization: ${org}\n\n${details}`,
|
|
channel: "enquire",
|
|
},
|
|
btn,
|
|
(restore) => {
|
|
btn.innerHTML = "<span>✓ Enquiry Sent!</span>";
|
|
setTimeout(() => {
|
|
this.close();
|
|
restore();
|
|
}, 1800);
|
|
},
|
|
{
|
|
sr,
|
|
validateFields: { name, email, phone, message: details },
|
|
fieldMap: {
|
|
name: "name",
|
|
email: "email",
|
|
phone: "phone",
|
|
message: "details",
|
|
email_or_phone_required: ["email", "phone"],
|
|
},
|
|
},
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
customElements.define("prology-sell-modal", PrologySellModal);
|
|
customElements.define("prology-source-modal", PrologySourceModal);
|
|
customElements.define("prology-quote-modal", PrologyQuoteModal);
|
|
customElements.define("prology-enquire-modal", PrologyEnquireModal);
|
|
|
|
// ── Global API (backward compatible with onclick calls) ──
|
|
window.openValuationModal = (email = "") =>
|
|
document.getElementById("wc-sell-modal").open(email);
|
|
window.openSourcingModal = () =>
|
|
document.getElementById("wc-source-modal").open();
|
|
window.openQuoteModal = () =>
|
|
document.getElementById("wc-quote-modal").open();
|
|
window.openEnquireModal = () =>
|
|
document.getElementById("wc-enquire-modal").open();
|
|
|
|
// ── Store picker modal ──
|
|
const cfg = window.PROLOGY_CONFIG || {};
|
|
|
|
// Builds a store URL, optionally appending a category slug.
|
|
// e.g. ("https://prology.net/au", "compute") → "https://prology.net/au/compute"
|
|
const buildStoreUrl = (base, slug) => {
|
|
if (!base) return "";
|
|
const trimmed = base.replace(/\/+$/, "");
|
|
return slug ? `${trimmed}/${slug}` : base;
|
|
};
|
|
|
|
const applyStoreHrefs = (slug) => {
|
|
document.querySelectorAll('[data-store="au"]').forEach((el) => {
|
|
el.setAttribute("href", buildStoreUrl(cfg.STORE_AU_URL, slug));
|
|
});
|
|
document.querySelectorAll('[data-store="us"]').forEach((el) => {
|
|
el.setAttribute("href", buildStoreUrl(cfg.STORE_US_URL, slug));
|
|
});
|
|
};
|
|
|
|
window.openStoreModal = function (categorySlug) {
|
|
applyStoreHrefs(categorySlug || "");
|
|
const overlay = document.getElementById("store-modal-overlay");
|
|
if (overlay) overlay.classList.add("open");
|
|
};
|
|
window.closeStoreModal = function () {
|
|
const overlay = document.getElementById("store-modal-overlay");
|
|
if (overlay) overlay.classList.remove("open");
|
|
};
|
|
document
|
|
.getElementById("store-modal-overlay")
|
|
.addEventListener("click", function (e) {
|
|
if (e.target === this) closeStoreModal();
|
|
});
|
|
document.addEventListener("keydown", function (e) {
|
|
if (e.key === "Escape") closeStoreModal();
|
|
});
|
|
|
|
// Initial wire-up (no category)
|
|
applyStoreHrefs("");
|
|
|
|
// Intercept any /shop link → open store picker instead
|
|
document.addEventListener("click", function (e) {
|
|
const a = e.target.closest('a[href="/shop"]');
|
|
if (!a) return;
|
|
e.preventDefault();
|
|
window.openStoreModal();
|
|
});
|
|
|
|
window.goToSellStep2 = function () {
|
|
const emailInput = document.getElementById("sell-email-init");
|
|
const email = emailInput ? emailInput.value : "";
|
|
if (!email || !email.includes("@")) {
|
|
alert("Please input a valid email address.");
|
|
return;
|
|
}
|
|
window.openValuationModal(email);
|
|
};
|
|
|
|
// ── Auto-open a modal from the URL (e.g. ?_open=wc-quote-modal) ──
|
|
(function openModalFromUrl() {
|
|
const target = new URLSearchParams(window.location.search).get("_open");
|
|
if (!target) return;
|
|
|
|
if (target === "store-modal-overlay") {
|
|
window.openStoreModal();
|
|
return;
|
|
}
|
|
|
|
const el = document.getElementById(target);
|
|
if (el && typeof el.open === "function") el.open();
|
|
})();
|
|
})();
|