Migration to astro #1
9 changed files with 187 additions and 31 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useRef } from "react";
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import "./contact-form.style.scss";
|
||||
|
|
@ -9,6 +9,28 @@ import YellowPlanet from "../../assets/images/main/contact-form/Planet Yellow.sv
|
|||
import FormInput from "../form-input/form-input.component";
|
||||
import CustomButton from "../custom-button/custom-button.component";
|
||||
|
||||
const verifyCaptcha = async (captchaToken) => {
|
||||
const keyId = "8e5272fb9c08";
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`https://cap.midcreative.eu/${keyId}/siteverify`,
|
||||
{
|
||||
response: captchaToken,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
return response.data.success;
|
||||
} catch (error) {
|
||||
console.error("Captcha verification failed:", error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const newPost = async (data) => {
|
||||
return axios
|
||||
.post("https://midcreative.eu/mail/", data, {
|
||||
|
|
@ -17,7 +39,10 @@ const newPost = async (data) => {
|
|||
},
|
||||
})
|
||||
.then((res) => true)
|
||||
.catch((err) => err);
|
||||
.catch((err) => {
|
||||
console.error("Form submission error:", err);
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
const ContactForm = () => {
|
||||
|
|
@ -34,6 +59,8 @@ const ContactForm = () => {
|
|||
|
||||
const [success, setSuccess] = useState();
|
||||
const [error, setError] = useState();
|
||||
const [captchaToken, setCaptchaToken] = useState(null);
|
||||
const [captchaError, setCaptchaError] = useState(false);
|
||||
|
||||
const setName = (name) => {
|
||||
setFormData({ ...formData, name: name });
|
||||
|
|
@ -53,21 +80,69 @@ const ContactForm = () => {
|
|||
phoneInputRef.current.clean();
|
||||
};
|
||||
|
||||
const handlePost = (e) => {
|
||||
useEffect(() => {
|
||||
const handleCaptchaCompleted = (event) => {
|
||||
if (event.detail && event.detail.token) {
|
||||
setCaptchaToken(event.detail.token);
|
||||
setCaptchaError(false);
|
||||
}
|
||||
};
|
||||
|
||||
const captchaWidget = document.querySelector('cap-widget');
|
||||
if (captchaWidget) {
|
||||
captchaWidget.addEventListener('cap-completed', handleCaptchaCompleted);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (captchaWidget) {
|
||||
captchaWidget.removeEventListener('cap-completed', handleCaptchaCompleted);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handlePost = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
newPost(formData).then((res) => {
|
||||
if (res === true) {
|
||||
if (!captchaToken) {
|
||||
setCaptchaError(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const isCaptchaValid = await verifyCaptcha(captchaToken);
|
||||
|
||||
if (!isCaptchaValid) {
|
||||
setCaptchaError(true);
|
||||
setCaptchaToken(null);
|
||||
const captchaWidget = document.querySelector('cap-widget');
|
||||
if (captchaWidget && captchaWidget.reset) {
|
||||
captchaWidget.reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await newPost(formData);
|
||||
|
||||
if (response === true) {
|
||||
cleanFields();
|
||||
setError(false);
|
||||
setSuccess(true);
|
||||
setCaptchaToken(null);
|
||||
setCaptchaError(false);
|
||||
const captchaWidget = document.querySelector('cap-widget');
|
||||
if (captchaWidget && captchaWidget.reset) {
|
||||
captchaWidget.reset();
|
||||
}
|
||||
setTimeout(() => setSuccess(false), 6000);
|
||||
} else {
|
||||
cleanFields();
|
||||
setError(true);
|
||||
setSuccess(false);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Form submission failed:", err);
|
||||
setError(true);
|
||||
setSuccess(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -113,6 +188,16 @@ const ContactForm = () => {
|
|||
required
|
||||
/>
|
||||
|
||||
<cap-widget
|
||||
data-cap-api-endpoint="https://cap.midcreative.eu/8e5272fb9c08/api/"
|
||||
></cap-widget>
|
||||
|
||||
{captchaError && (
|
||||
<p className="error" style={{ marginTop: '10px' }}>
|
||||
{t("contacts.captchaError") || "Please complete the captcha"}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<CustomButton text={t("contacts.send")} />
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import React from "react";
|
||||
import { Link } from "gatsby";
|
||||
import "./footer.style.scss";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getLocalizedPath } from "../../utils/navigation";
|
||||
|
||||
import Contacts from "../contacts/contacts.component";
|
||||
import TopLine from "../../assets/images/main/footer/top-line.png";
|
||||
|
|
@ -27,21 +29,21 @@ const MidFooter = () => {
|
|||
</div>
|
||||
<nav>
|
||||
<h6>{t("footer.navigation")}</h6>
|
||||
<a href="#top" className="gray mobile">
|
||||
<Link to={getLocalizedPath("/#top")} className="gray mobile">
|
||||
{t("menu.home")}
|
||||
</a>
|
||||
<a href="#services" className="gray">
|
||||
</Link>
|
||||
<Link to={getLocalizedPath("/#services")} className="gray">
|
||||
{t("menu.services")}
|
||||
</a>
|
||||
<a href="#about-us" className="gray">
|
||||
</Link>
|
||||
<Link to={getLocalizedPath("/#about-us")} className="gray">
|
||||
{t("menu.about_us")}
|
||||
</a>
|
||||
<a href="#projects" className="gray">
|
||||
</Link>
|
||||
<Link to={getLocalizedPath("/#projects")} className="gray">
|
||||
{t("menu.portfolio")}
|
||||
</a>
|
||||
<a href="#contact-us" className="gray">
|
||||
</Link>
|
||||
<Link to={getLocalizedPath("/#contact-us")} className="gray">
|
||||
{t("menu.contact_us")}
|
||||
</a>
|
||||
</Link>
|
||||
</nav>
|
||||
<div>
|
||||
<img src={Logo} alt="" />
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Link } from "gatsby";
|
|||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import LanguageSwitch from "../language-switch/language-switch.component";
|
||||
import { getLocalizedPath } from "../../utils/navigation";
|
||||
|
||||
import Logo from "../../assets/images/main/header/logo.svg";
|
||||
import Logo2 from "../../assets/images/main/footer/logo.svg";
|
||||
|
|
@ -64,10 +65,10 @@ const MidHeader = () => {
|
|||
<img src={Logo} alt="MID" />
|
||||
</div>
|
||||
<nav className="navigation">
|
||||
<Link to="/#services">{t("menu.services")}</Link>
|
||||
<Link to="/#about-us">{t("menu.about_us")}</Link>
|
||||
<Link to="/#projects">{t("menu.portfolio")}</Link>
|
||||
<Link to="/#contact-us">{t("menu.contact_us")}</Link>
|
||||
<Link to={getLocalizedPath("/#services")}>{t("menu.services")}</Link>
|
||||
<Link to={getLocalizedPath("/#about-us")}>{t("menu.about_us")}</Link>
|
||||
<Link to={getLocalizedPath("/#projects")}>{t("menu.portfolio")}</Link>
|
||||
<Link to={getLocalizedPath("/#contact-us")}>{t("menu.contact_us")}</Link>
|
||||
|
||||
<LanguageSwitch />
|
||||
</nav>
|
||||
|
|
@ -91,11 +92,11 @@ const MidHeader = () => {
|
|||
<img src={Logo2} alt="MID Creative Digital Agency" />
|
||||
|
||||
<nav className="navigation">
|
||||
<a href="#top">{t("menu.home")}</a>
|
||||
<a href="#services">{t("menu.services")}</a>
|
||||
<a href="#about-us">{t("menu.about_us")}</a>
|
||||
<a href="#projects">{t("menu.portfolio")}</a>
|
||||
<a href="#contact-us">{t("menu.contact_us")}</a>
|
||||
<Link to={getLocalizedPath("/#top")}>{t("menu.home")}</Link>
|
||||
<Link to={getLocalizedPath("/#services")}>{t("menu.services")}</Link>
|
||||
<Link to={getLocalizedPath("/#about-us")}>{t("menu.about_us")}</Link>
|
||||
<Link to={getLocalizedPath("/#projects")}>{t("menu.portfolio")}</Link>
|
||||
<Link to={getLocalizedPath("/#contact-us")}>{t("menu.contact_us")}</Link>
|
||||
</nav>
|
||||
|
||||
<div className="header-contacts">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React from "react";
|
||||
import { navigate } from "gatsby";
|
||||
import i18n from "../../i18n";
|
||||
|
||||
import "./language-switch.style.scss";
|
||||
|
|
@ -6,6 +7,13 @@ import "./language-switch.style.scss";
|
|||
const LanguageSwitch = ({ t }) => {
|
||||
const changeLanguage = (lng) => {
|
||||
i18n.changeLanguage(lng);
|
||||
|
||||
// Navigate to the appropriate route
|
||||
if (lng === "lv") {
|
||||
navigate("/lv");
|
||||
} else {
|
||||
navigate("/");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ export const Seo = ({ title, description, pathname, children }) => {
|
|||
gtag('config', 'G-8M9CZ79T4L');
|
||||
`}
|
||||
</Script>
|
||||
<Script
|
||||
src="https://cap.midcreative.eu/8e5272fb9c08/widget.js"
|
||||
strategy="idle"
|
||||
/>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -18,10 +18,13 @@ i18n
|
|||
.use(initReactI18next) // passes i18n down to react-i18next
|
||||
.init({
|
||||
resources,
|
||||
lng: "en", // language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources
|
||||
// you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage
|
||||
// if you're using a language detector, do not define the lng option
|
||||
lng: typeof window !== "undefined" && window.location.pathname.startsWith("/lv") ? "lv" : "en", // Detect language from URL
|
||||
fallbackLng: "en", // use en if detected lng is not available
|
||||
detection: {
|
||||
// Disable automatic detection to respect URL-based language
|
||||
order: ["path", "localStorage", "navigator"],
|
||||
caches: ["localStorage"],
|
||||
},
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // react already safes from xss
|
||||
|
|
|
|||
|
|
@ -1,11 +1,18 @@
|
|||
import * as React from "react";
|
||||
import { useEffect } from "react";
|
||||
import { Seo } from "../components/seo";
|
||||
import i18n from "../i18n";
|
||||
|
||||
import "../assets/stylesheets/style.scss";
|
||||
|
||||
import MidMainPage from "../main/main.component";
|
||||
|
||||
const IndexPage = () => {
|
||||
useEffect(() => {
|
||||
// Ensure English is set for the root route
|
||||
i18n.changeLanguage("en");
|
||||
}, []);
|
||||
|
||||
return <MidMainPage></MidMainPage>;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,8 @@
|
|||
"phone": "phone",
|
||||
"send": "Send",
|
||||
"success": "Thank you for sharing your contacts! We will get in touch!",
|
||||
"error": "Looks like something went wrong, please try to reach us directly "
|
||||
"error": "Looks like something went wrong, please try to reach us directly ",
|
||||
"captchaError": "Please complete the captcha verification"
|
||||
},
|
||||
"privacy": {
|
||||
"title": "Privacy Policy",
|
||||
|
|
@ -144,5 +145,27 @@
|
|||
"registration_nr": "Registration nr.",
|
||||
"address": "Address",
|
||||
"navigation": "Navigation"
|
||||
},
|
||||
"landingPromo": {
|
||||
"title": "Professional Landing Page Development in Just 2 Business Days – €150!",
|
||||
"subtitle": "Looking to attract new customers quickly and effectively? Our experienced team specializes in creating efficient and modern landing pages that will help achieve your business goals.",
|
||||
"whatYouGet": "What you get:",
|
||||
"benefit1": "Customized landing page tailored to your brand and needs",
|
||||
"benefit2": "Modern and user-friendly design",
|
||||
"benefit3": "Mobile-responsive solution",
|
||||
"benefit4": "Optimized page loading speed and SEO basics",
|
||||
"benefit5": "Free consultation on content and structure creation",
|
||||
"whyUs": "Why choose us:",
|
||||
"feature1Title": "Fast Development",
|
||||
"feature1": "Guaranteed delivery in just 2 business days",
|
||||
"feature2Title": "Fixed Price",
|
||||
"feature2": "€150 - no hidden costs",
|
||||
"feature3Title": "Experienced Team",
|
||||
"feature3": "Friendly and responsive team with website development experience",
|
||||
"feature4Title": "Long-term Support",
|
||||
"feature4": "Support even after page creation",
|
||||
"ctaTitle": "Contact us today and get your new landing page in just two days!",
|
||||
"ctaText": "Book a consultation – we'll help bring your ideas to life!",
|
||||
"ctaButton": "Contact Us"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,12 +91,35 @@
|
|||
"phone": "Telefons",
|
||||
"send": "Nosūtīt",
|
||||
"success": "Paldies ",
|
||||
"error": "Izskatās, ka kautkas nestrādā :( Uzrakstiet mums uz "
|
||||
"error": "Izskatās, ka kautkas nestrādā :( Uzrakstiet mums uz ",
|
||||
"captchaError": "Lūdzu, aizpildiet captcha verifikāciju"
|
||||
},
|
||||
"footer": {
|
||||
"company": "Kompānija",
|
||||
"registration_nr": "Reģistrācijas nr.",
|
||||
"address": "Adrese",
|
||||
"navigation": "Navigācija"
|
||||
},
|
||||
"landingPromo": {
|
||||
"title": "Profesionāla landing lapu izstrāde tikai 2 darba dienu laikā – 150 €!",
|
||||
"subtitle": "Vai vēlaties ātri un kvalitatīvi piesaistīt jaunus klientus? Mūsu pieredzējusī komanda specializējas efektīvu un modernu landing lapu izstrādē, kas palīdzēs sasniegt jūsu biznesa mērķus.",
|
||||
"whatYouGet": "Ko jūs iegūstat:",
|
||||
"benefit1": "Individuāli pielāgotu landing lapu atbilstoši jūsu zīmolam un vajadzībām",
|
||||
"benefit2": "Mūsdienīgu un lietotājam draudzīgu dizainu",
|
||||
"benefit3": "Mobilajām ierīcēm pielāgotu risinājumu",
|
||||
"benefit4": "Optimizētu lapas ielādes ātrumu un SEO pamatus",
|
||||
"benefit5": "Bezmaksas konsultāciju par satura un struktūras izveidi",
|
||||
"whyUs": "Kāpēc izvēlēties mūs:",
|
||||
"feature1Title": "Ātra izstrāde",
|
||||
"feature1": "Garantēta izstrāde tikai 2 darba dienu laikā",
|
||||
"feature2Title": "Fiksēta cena",
|
||||
"feature2": "150 € - bez slēptām izmaksām",
|
||||
"feature3Title": "Pieredzējuša komanda",
|
||||
"feature3": "Draudzīga un atsaucīga komanda ar pieredzi mājaslapu izstrādē",
|
||||
"feature4Title": "Ilgtermiņa atbalsts",
|
||||
"feature4": "Atbalsts arī pēc lapas izveides",
|
||||
"ctaTitle": "Sazinieties ar mums jau šodien un saņemiet savu jauno landing lapu tikai divu dienu laikā!",
|
||||
"ctaText": "Piesakieties konsultācijai – mēs palīdzēsim īstenot jūsu idejas!",
|
||||
"ctaButton": "Sazināties ar mums"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue