121 lines
3.5 KiB
TypeScript
121 lines
3.5 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { getPublicPageRendererMessages } from "@/features/i18n/messages/publicPageRenderer";
|
|
|
|
interface PublicProjectPageClientProps {
|
|
html: string;
|
|
canSubmit?: boolean;
|
|
}
|
|
|
|
export default function PublicProjectPageClient({ html, canSubmit = true }: PublicProjectPageClientProps) {
|
|
const m = getPublicPageRendererMessages(null);
|
|
const [toastMessage, setToastMessage] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
|
|
let toastTimeout: number | null = null;
|
|
|
|
const showToast = (message: string) => {
|
|
setToastMessage(message);
|
|
if (toastTimeout !== null) {
|
|
window.clearTimeout(toastTimeout);
|
|
}
|
|
toastTimeout = window.setTimeout(() => {
|
|
setToastMessage(null);
|
|
}, 4000);
|
|
};
|
|
|
|
const forms = document.querySelectorAll<HTMLFormElement>(
|
|
'form.pb-form-controller, form[action="/api/forms/submit"]',
|
|
);
|
|
if (!forms || forms.length === 0) return;
|
|
|
|
forms.forEach((form) => {
|
|
if (form.dataset.pbInitialized === "1") return;
|
|
form.dataset.pbInitialized = "1";
|
|
|
|
form.addEventListener("submit", (event) => {
|
|
if (!form) return;
|
|
if (event && typeof event.preventDefault === "function") {
|
|
event.preventDefault();
|
|
}
|
|
|
|
const formData = new FormData(form);
|
|
const configInput = form.querySelector<HTMLInputElement>('input[name="__config"]');
|
|
let config: any = null;
|
|
if (configInput && configInput.value) {
|
|
try {
|
|
config = JSON.parse(configInput.value);
|
|
} catch {
|
|
config = null;
|
|
}
|
|
}
|
|
|
|
if (!canSubmit) {
|
|
const errorMsg = (config && config.errorMessage) || m.submitErrorDefault;
|
|
showToast(errorMsg);
|
|
return;
|
|
}
|
|
|
|
const action = form.getAttribute("action") || "/api/forms/submit";
|
|
|
|
fetch(action, { method: "POST", body: formData })
|
|
.then((res) => res.json().catch(() => ({})))
|
|
.then((data: any) => {
|
|
const ok = data && data.ok;
|
|
const message = data && data.message;
|
|
if (ok) {
|
|
const success = message || (config && config.successMessage) || m.submitSuccessDefault;
|
|
showToast(success);
|
|
try {
|
|
form.reset();
|
|
} catch {}
|
|
} else {
|
|
const errorMsg = message || (config && config.errorMessage) || m.submitErrorDefault;
|
|
showToast(errorMsg);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
const errorMsg = (config && config.errorMessage) || m.submitErrorDefault;
|
|
showToast(errorMsg);
|
|
});
|
|
});
|
|
});
|
|
|
|
return () => {
|
|
if (toastTimeout !== null) {
|
|
window.clearTimeout(toastTimeout);
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<>
|
|
<div dangerouslySetInnerHTML={{ __html: html }} />
|
|
{toastMessage ? (
|
|
<div
|
|
style={{
|
|
position: "fixed",
|
|
right: "16px",
|
|
bottom: "16px",
|
|
zIndex: 50,
|
|
maxWidth: "320px",
|
|
padding: "12px 16px",
|
|
borderRadius: "8px",
|
|
backgroundColor: "rgba(15, 23, 42, 0.95)",
|
|
color: "#e5e7eb",
|
|
fontSize: "14px",
|
|
lineHeight: "1.4",
|
|
boxShadow: "0 10px 25px rgba(15, 23, 42, 0.6)",
|
|
pointerEvents: "none",
|
|
}}
|
|
>
|
|
{toastMessage}
|
|
</div>
|
|
) : null}
|
|
</>
|
|
);
|
|
}
|