Podmiana image w hero, Kontakt pole readonly dla danych z ofert dodanie do tresci maila
This commit is contained in:
@@ -5,8 +5,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview"
|
||||||
"update:jambox:base": "node src/scripts/update-jambox-base.js"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/node": "^9.5.1",
|
"@astrojs/node": "^9.5.1",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ subtitle:
|
|||||||
|
|
||||||
description: |
|
description: |
|
||||||
|
|
||||||
imageUrl: "fiber.webp"
|
imageUrl: "section-tv.webp"
|
||||||
ctas:
|
ctas:
|
||||||
- label: "Zobacz ofertę Internetu"
|
- label: "Zobacz ofertę Internetu"
|
||||||
href: "/internet-swiatlowodowy"
|
href: "/internet-swiatlowodowy"
|
||||||
|
|||||||
@@ -512,7 +512,7 @@ export default function InternetAddonsModal({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="/kontakt"
|
href="/kontakt#form"
|
||||||
class="btn btn-primary w-full mt-4"
|
class="btn btn-primary w-full mt-4"
|
||||||
onClick={() => saveOfferToLocalStorage()}
|
onClick={() => saveOfferToLocalStorage()}
|
||||||
>
|
>
|
||||||
@@ -523,7 +523,6 @@ export default function InternetAddonsModal({
|
|||||||
</SectionAccordion>
|
</SectionAccordion>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ✅ pływająca suma (ten sam styl co w Jambox, jeśli już masz CSS) */}
|
|
||||||
<div class="f-floating-total" onClick={(e) => e.stopPropagation()}>
|
<div class="f-floating-total" onClick={(e) => e.stopPropagation()}>
|
||||||
<div class="f-floating-total-circle" role="status" aria-label="Cena miesięczna">
|
<div class="f-floating-total-circle" role="status" aria-label="Cena miesięczna">
|
||||||
<span class="f-floating-total-unit">Razem</span>
|
<span class="f-floating-total-unit">Razem</span>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ function esc(str = "") {
|
|||||||
|
|
||||||
function buildHtmlMail(form) {
|
function buildHtmlMail(form) {
|
||||||
const when = new Date().toLocaleString("pl-PL");
|
const when = new Date().toLocaleString("pl-PL");
|
||||||
|
const offerText = (form.offerSummary || "").trim()
|
||||||
|
|
||||||
return `<!DOCTYPE html>
|
return `<!DOCTYPE html>
|
||||||
<html lang="pl">
|
<html lang="pl">
|
||||||
@@ -79,6 +80,17 @@ function buildHtmlMail(form) {
|
|||||||
${esc(form.message)}
|
${esc(form.message)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
${offerText ? `
|
||||||
|
<div style="font-size:14px;color:#111827;margin:8px 0;font-weight:700;">
|
||||||
|
Wybrana oferta:
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="font-size:14px;color:#111827;white-space:pre-line;line-height:1.6;
|
||||||
|
background:#f8fafc;border:1px solid #e5e7eb;border-radius:12px;padding:14px;">
|
||||||
|
${esc(offerText)}
|
||||||
|
</div>
|
||||||
|
` : ``}
|
||||||
|
|
||||||
<div style="margin-top:16px;font-size:12px;color:#6b7280;">
|
<div style="margin-top:16px;font-size:12px;color:#6b7280;">
|
||||||
Wysłano: ${when}
|
Wysłano: ${when}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
---
|
---
|
||||||
|
import path from "node:path";
|
||||||
import DefaultLayout from "../../layouts/DefaultLayout.astro";
|
import DefaultLayout from "../../layouts/DefaultLayout.astro";
|
||||||
import MapGoogle from "../../components/maps/MapGoogle.astro";
|
import MapGoogle from "../../components/maps/MapGoogle.astro";
|
||||||
|
import { loadYamlFile } from "../../lib/loadYaml";
|
||||||
|
|
||||||
import yaml from "js-yaml";
|
type SeoYaml = any;
|
||||||
import fs from "fs";
|
const seo = loadYamlFile<SeoYaml>(
|
||||||
|
path.join(process.cwd(), "src", "content", "contact", "seo.yaml"),
|
||||||
const data = yaml.load(
|
|
||||||
fs.readFileSync("./src/content/contact/contact.yaml", "utf8"),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const seo = yaml.load(
|
type ContactData = any;
|
||||||
fs.readFileSync("./src/content/internet-swiatlowodowy/seo.yaml", "utf8"),
|
const data = loadYamlFile<ContactData>(
|
||||||
|
path.join(process.cwd(), "src", "content", "contact", "contact.yaml"),
|
||||||
);
|
);
|
||||||
|
|
||||||
const apiKey = import.meta.env.PUBLIC_GOOGLE_MAPS_KEY;
|
const apiKey = import.meta.env.PUBLIC_GOOGLE_MAPS_KEY;
|
||||||
@@ -25,8 +26,9 @@ const form = data.form;
|
|||||||
<div class="f-contact-item" set:html={data.description} />
|
<div class="f-contact-item" set:html={data.description} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div id="form">
|
||||||
<h2 class="f-section-title">{data.contactFormTitle}</h2>
|
<h2 class="f-section-title">{data.contactFormTitle}</h2>
|
||||||
|
|
||||||
<form id="contactForm" class="f-contact-form">
|
<form id="contactForm" class="f-contact-form">
|
||||||
<div class="f-contact-form-inner">
|
<div class="f-contact-form-inner">
|
||||||
<input
|
<input
|
||||||
@@ -77,7 +79,20 @@ const form = data.form;
|
|||||||
rows={form.message.rows}
|
rows={form.message.rows}
|
||||||
placeholder={form.message.placeholder}
|
placeholder={form.message.placeholder}
|
||||||
class="f-input"
|
class="f-input"
|
||||||
required></textarea>
|
required
|
||||||
|
></textarea>
|
||||||
|
|
||||||
|
<!-- widoczne tylko gdy jest oferta -->
|
||||||
|
<div id="offerSummaryWrap" class="hidden">
|
||||||
|
<textarea
|
||||||
|
id="offerSummary"
|
||||||
|
name="offerSummary"
|
||||||
|
rows="6"
|
||||||
|
class="f-input"
|
||||||
|
readonly
|
||||||
|
placeholder="Wybrana oferta pojawi się tutaj."
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
<label class="f-rodo">
|
<label class="f-rodo">
|
||||||
<input type="checkbox" name="rodo" required />
|
<input type="checkbox" name="rodo" required />
|
||||||
@@ -117,8 +132,6 @@ const form = data.form;
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="toast" class="f-toast"></div>
|
<div id="toast" class="f-toast"></div>
|
||||||
|
|
||||||
<input type="hidden" name="offerConfig" id="offerConfig" />
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ReCaptcha v3 -->
|
<!-- ReCaptcha v3 -->
|
||||||
@@ -142,59 +155,32 @@ const form = data.form;
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const form = document.getElementById("contactForm");
|
const formEl = document.getElementById("contactForm");
|
||||||
const toast = document.getElementById("toast");
|
const toast = document.getElementById("toast");
|
||||||
|
if (!formEl) return;
|
||||||
|
|
||||||
if (!form) return;
|
const LS_KEY = "fuz_offer_config_v1";
|
||||||
|
const SS_INJECTED_KEY = "fuz_offer_injected_v1";
|
||||||
form.addEventListener("submit", async (e) => {
|
|
||||||
if (!form.reportValidity()) return;
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
const data = Object.fromEntries(new FormData(form).entries());
|
|
||||||
data.rodo = form.rodo.checked;
|
|
||||||
|
|
||||||
const token = await grecaptcha.execute(window.FUZ_RECAPTCHA_KEY, {
|
|
||||||
action: "submit",
|
|
||||||
});
|
|
||||||
data.recaptcha = token;
|
|
||||||
|
|
||||||
const resp = await fetch("/api/contact/contact", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
});
|
|
||||||
|
|
||||||
const json = await resp.json();
|
|
||||||
|
|
||||||
showToast(
|
|
||||||
json.ok ? successMsg : errorMsg,
|
|
||||||
json.ok ? "success" : "error",
|
|
||||||
);
|
|
||||||
|
|
||||||
if (json.ok) form.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
function showToast(msg, type) {
|
function showToast(msg, type) {
|
||||||
|
if (!toast) return;
|
||||||
toast.innerHTML = `<div class="f-toast-msg ${type}">${msg}</div>`;
|
toast.innerHTML = `<div class="f-toast-msg ${type}">${msg}</div>`;
|
||||||
toast.classList.remove("visible");
|
toast.classList.remove("visible");
|
||||||
void toast.offsetWidth;
|
void toast.offsetWidth;
|
||||||
toast.classList.add("visible");
|
toast.classList.add("visible");
|
||||||
|
|
||||||
setTimeout(() => toast.classList.remove("visible"), 3000);
|
setTimeout(() => toast.classList.remove("visible"), 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------
|
|
||||||
const LS_KEY = "fuz_offer_config_v1";
|
|
||||||
const SS_INJECTED_KEY = "fuz_offer_injected_v1";
|
|
||||||
|
|
||||||
function clearOfferDraft() {
|
function clearOfferDraft() {
|
||||||
try {
|
try {
|
||||||
localStorage.removeItem(LS_KEY);
|
localStorage.removeItem(LS_KEY);
|
||||||
sessionStorage.removeItem(SS_INJECTED_KEY);
|
sessionStorage.removeItem(SS_INJECTED_KEY);
|
||||||
|
|
||||||
const hidden = document.getElementById("offerConfig");
|
const wrap = document.getElementById("offerSummaryWrap");
|
||||||
if (hidden) hidden.value = "";
|
if (wrap) wrap.classList.add("hidden");
|
||||||
|
|
||||||
|
const offerSummary = document.getElementById("offerSummary");
|
||||||
|
if (offerSummary) offerSummary.value = "";
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,9 +193,9 @@ const form = data.form;
|
|||||||
|
|
||||||
const payload = JSON.parse(raw);
|
const payload = JSON.parse(raw);
|
||||||
|
|
||||||
const msg = document.querySelector('textarea[name="message"]');
|
const subject = formEl.querySelector('input[name="subject"]');
|
||||||
const subject = document.querySelector('input[name="subject"]');
|
const wrap = document.getElementById("offerSummaryWrap");
|
||||||
const hidden = document.getElementById("offerConfig");
|
const offerSummary = document.getElementById("offerSummary");
|
||||||
|
|
||||||
const offerText =
|
const offerText =
|
||||||
typeof payload?.message === "string" && payload.message.trim()
|
typeof payload?.message === "string" && payload.message.trim()
|
||||||
@@ -218,19 +204,13 @@ const form = data.form;
|
|||||||
|
|
||||||
if (!offerText) return;
|
if (!offerText) return;
|
||||||
|
|
||||||
|
if (wrap) wrap.classList.remove("hidden");
|
||||||
|
|
||||||
if (subject && !subject.value) {
|
if (subject && !subject.value) {
|
||||||
subject.value = `Zapytanie: ${payload?.pkg?.name || "Oferta"}`;
|
subject.value = `Zapytanie: ${payload?.pkg?.name || "Oferta"}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg) {
|
if (offerSummary) offerSummary.value = offerText;
|
||||||
const marker = "Wybrana oferta:";
|
|
||||||
const alreadyHas = msg.value && msg.value.includes(marker);
|
|
||||||
if (!alreadyHas) {
|
|
||||||
msg.value = (msg.value ? msg.value + "\n\n" : "") + offerText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hidden) hidden.value = offerText;
|
|
||||||
|
|
||||||
sessionStorage.setItem(SS_INJECTED_KEY, "1");
|
sessionStorage.setItem(SS_INJECTED_KEY, "1");
|
||||||
} catch {}
|
} catch {}
|
||||||
@@ -238,21 +218,43 @@ const form = data.form;
|
|||||||
|
|
||||||
hydrateOfferIntoForm();
|
hydrateOfferIntoForm();
|
||||||
|
|
||||||
function onSubmitSuccessCleanup() {
|
window.addEventListener("pagehide", clearOfferDraft);
|
||||||
clearOfferDraft();
|
window.addEventListener("beforeunload", clearOfferDraft);
|
||||||
}
|
|
||||||
|
|
||||||
function onLeaveCleanup() {
|
formEl.addEventListener("submit", async (e) => {
|
||||||
clearOfferDraft();
|
if (!formEl.reportValidity()) return;
|
||||||
}
|
e.preventDefault();
|
||||||
|
|
||||||
window.addEventListener("pagehide", onLeaveCleanup);
|
try {
|
||||||
window.addEventListener("beforeunload", onLeaveCleanup);
|
const data = Object.fromEntries(new FormData(formEl).entries());
|
||||||
if (json.ok) {
|
data.rodo = formEl.rodo?.checked === true;
|
||||||
form.reset();
|
|
||||||
onSubmitSuccessCleanup();
|
const token = await grecaptcha.execute(window.FUZ_RECAPTCHA_KEY, {
|
||||||
}
|
action: "submit",
|
||||||
// ----------
|
});
|
||||||
|
data.recaptcha = token;
|
||||||
|
|
||||||
|
const resp = await fetch("/api/contact/contact", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
});
|
||||||
|
|
||||||
|
const json = await resp.json().catch(() => ({}));
|
||||||
|
|
||||||
|
showToast(
|
||||||
|
json?.ok ? successMsg : errorMsg,
|
||||||
|
json?.ok ? "success" : "error",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (json?.ok) {
|
||||||
|
formEl.reset();
|
||||||
|
clearOfferDraft();
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
showToast(errorMsg, "error");
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.btn {
|
.btn {
|
||||||
@apply inline-flex items-center justify-center gap-2 font-semibold rounded-lg px-6 py-3 text-base transition-all duration-200 cursor-pointer select-none focus:outline-none focus-visible:ring-2 focus-visible:ring-[--f-header] focus-visible:ring-offset-2 focus-visible:ring-offset-[--f-background];
|
@apply inline-flex items-center justify-center gap-2 font-semibold rounded-lg px-6 py-3 mt-3 text-base transition-all duration-200 cursor-pointer select-none focus:outline-none focus-visible:ring-2 focus-visible:ring-[--f-header] focus-visible:ring-offset-2 focus-visible:ring-offset-[--f-background];
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
|
|||||||
Reference in New Issue
Block a user