Stylizacja, zmiana mapy zasięgu

This commit is contained in:
dm
2025-11-26 14:37:07 +01:00
parent 8bf578e6d9
commit a0f4e8fbab
29 changed files with 339 additions and 557 deletions

View File

@@ -1,184 +1,125 @@
---
import yaml from "js-yaml";
import fs from "fs";
import MapGoogle from "../maps/MapGoogle.astro";
// import "../../styles/contact.css";
import MapGoogle from "../../components/maps/MapGoogle.astro";
const data = yaml.load(
fs.readFileSync("./src/content/contact/contact.yaml", "utf8"),
fs.readFileSync("./src/content/contact/contact.yaml", "utf8")
);
const apiKey = import.meta.env.PUBLIC_GOOGLE_MAPS_KEY;
const form = data.form;
---
<section id="contact" class="f-section">
<div class="f-contact-grid">
<!-- Lewa kolumna -->
<div class="f-contact-col-1">
<h2>{data.title}</h2>
<div class="f-contact-item" set:html={data.description} />
</div>
<!-- Formularz -->
<div class="f-contact-col-2">
<h2>{data.contactFormTitle}</h2>
<form id="contactForm" class="f-contact-form">
<div class="f-contact-form-inner">
<input
type="text"
name="firstName"
placeholder={form.firstName.placeholder}
class="f-input"
required
/>
<input
type="text"
name="lastName"
placeholder={form.lastName.placeholder}
class="f-input"
required
/>
</div>
<div class="grid grid-cols-2 gap-4">
<input
type="email"
name="email"
placeholder={form.email.placeholder}
class="f-input"
required
autocomplete="email"
/>
<input
type="tel"
name="phone"
placeholder={form.phone.placeholder}
class="f-input"
required
autocomplete="tel"
/>
</div>
<input
type="text"
name="subject"
placeholder={form.subject.placeholder}
class="f-input"
required
/>
<textarea
name="message"
rows={form.message.rows}
placeholder={form.message.placeholder}
class="f-input"
required></textarea>
<label class="f-rodo">
<input type="checkbox" name="rodo" required />
<span>
{form.rodo.label}
<a
href={form.rodo.policyLink}
title={form.rodo.policyTitle}
>{form.rodo.policyText}</a
>.
</span>
</label>
<button title={form.submit.title}>
{form.submit.label}
</button>
</form>
</div>
<section id="kontakt" class="f-section">
<div class="f-contact-grid">
<!-- Kolumna lewa -->
<div class="f-contact-col-1">
<h2>{data.title}</h2>
<div class="f-contact-item" set:html={data.description}></div>
</div>
<div class="f-contact-map">
<MapGoogle
apiKey={apiKey}
lat={data.lat}
lon={data.lng}
zoom={16}
title={data.markerTitle}
description={data.markerAddress}
showMarker={true}
mode="contact"
mapStyleId={data.maps.mapId}
/>
<div class="f-contact-col-2">
<h2>{data.contactFormTitle}</h2>
<form id="contactForm" class="f-contact-form">
<div class="f-contact-form-inner">
<input type="text" name="firstName" placeholder={form.firstName.placeholder} class="f-input" required />
<input type="text" name="lastName" placeholder={form.lastName.placeholder} class="f-input" required />
</div>
<div class="grid grid-cols-2 gap-4">
<input type="email" name="email" placeholder={form.email.placeholder} class="f-input" required autocomplete="email" />
<input type="tel" name="phone" placeholder={form.phone.placeholder} class="f-input" required autocomplete="tel" />
</div>
<input type="text" name="subject" placeholder={form.subject.placeholder} class="f-input" required />
<textarea name="message" rows={form.message.rows} placeholder={form.message.placeholder} class="f-input" required></textarea>
<label class="f-rodo">
<input type="checkbox" name="rodo" required />
<span>
{form.rodo.label}
<a href={form.rodo.policyLink} title={form.rodo.policyTitle}>{form.rodo.policyText}</a>.
</span>
</label>
<button title={form.submit.title}>{form.submit.label}</button>
</form>
</div>
<!-- TODO: Poprawić obslugę błedu wysyłania wiadomości by pojawiał sie toast i ulozyć css -->
<div id="toast" class="f-toast"></div>
</div>
<div class="f-contact-map">
<MapGoogle
apiKey={apiKey}
lat={data.lat}
lon={data.lng}
zoom={16}
title={data.markerTitle}
description={data.markerAddress}
showMarker={true}
mode="contact"
mapStyleId={data.maps.mapId}
/>
</div>
<div id="toast" class="f-toast"></div>
</section>
<!-- ReCaptcha -->
<script
is:inline
define:vars={{ siteKey: import.meta.env.PUBLIC_RECAPTCHA_SITE_KEY }}
>
window.FUZ_RECAPTCHA_KEY = siteKey;
<!-- ReCaptcha v3 -->
<script is:inline define:vars={{ siteKey: import.meta.env.PUBLIC_RECAPTCHA_SITE_KEY }}>
window.FUZ_RECAPTCHA_KEY = siteKey;
const recaptchaScript = document.createElement("script");
recaptchaScript.src =
"https://www.google.com/recaptcha/api.js?render=" + siteKey;
document.head.appendChild(recaptchaScript);
const s = document.createElement("script");
s.src = "https://www.google.com/recaptcha/api.js?render=" + siteKey;
s.async = true;
document.head.appendChild(s);
</script>
<script
is:inline
define:vars={{
successMsg: JSON.stringify(form.submit.successMessage),
errorMsg: JSON.stringify(form.submit.errorMessage),
}}
is:inline
define:vars={{
successMsg: form.successMessage,
errorMsg: form.errorMessage
}}
>
document.addEventListener("DOMContentLoaded", () => {
const form = document.getElementById("contactForm");
const toast = document.getElementById("toast");
document.addEventListener("DOMContentLoaded", () => {
const form = document.getElementById("contactForm");
const toast = document.getElementById("toast");
if (!form) return;
if (!form) return;
form.addEventListener("submit", async (e) => {
if (!form.reportValidity()) return;
form.addEventListener("submit", async (e) => {
if (!form.reportValidity()) return;
e.preventDefault();
e.preventDefault();
const data = Object.fromEntries(new FormData(form).entries());
data.rodo = form.rodo.checked;
const formData = new FormData(form);
const token = await grecaptcha.execute(window.FUZ_RECAPTCHA_KEY, { action: "submit" });
data.recaptcha = token;
const payload = Object.fromEntries(formData.entries());
payload.rodo = formData.get("rodo") === "on";
const resp = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
const token = await grecaptcha.execute(window.FUZ_RECAPTCHA_KEY, {
action: "submit",
});
payload.recaptcha = token;
const json = await resp.json();
const resp = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
showToast(json.ok ? successMsg : errorMsg, json.ok ? "success" : "error");
const json = await resp.json();
showToast(
json.ok ? JSON.parse(successMsg) : JSON.parse(errorMsg),
json.ok ? "success" : "error",
);
if (json.ok) form.reset();
});
function showToast(message, type = "success") {
toast.innerHTML = `
<div class="fuz-toast-msg ${type}">
${message}
</div>
`;
toast.classList.remove("visible");
void toast.offsetWidth;
toast.classList.add("visible");
setTimeout(() => {
toast.classList.remove("visible");
}, 3000);
}
if (json.ok) form.reset();
});
function showToast(msg, type) {
toast.innerHTML = `<div class="f-toast-msg ${type}">${msg}</div>`;
toast.classList.remove("visible");
void toast.offsetWidth;
toast.classList.add("visible");
setTimeout(() => toast.classList.remove("visible"), 3000);
}
});
</script>