From 12813293608b0dcf1ae52fade1c5acf1378c0e34 Mon Sep 17 00:00:00 2001 From: selvi Date: Sat, 18 Apr 2026 17:12:58 +0530 Subject: [PATCH] contact integration updated --- app/about/AboutClient.tsx | 178 ++++++++++++++ app/about/page.tsx | 184 +-------------- app/blog/[slug]/page.tsx | 3 + app/blog/page.tsx | 5 +- app/contact/ContactClient.tsx | 276 ++++++++++++++++++++++ app/contact/page.tsx | 136 +---------- app/globals.css | 2 +- app/layout.tsx | 4 + app/login/LoginClient.tsx | 125 ++++++++++ app/login/page.tsx | 133 +---------- app/manufacturing/ManufacturingClient.tsx | 145 ++++++++++++ app/manufacturing/page.tsx | 151 +----------- app/page.tsx | 9 + app/products/ProductsClient.tsx | 118 +++++++++ app/products/[slug]/page.tsx | 17 ++ app/products/page.tsx | 126 +--------- app/rentals/RentalsClient.tsx | 189 +++++++++++++++ app/rentals/page.tsx | 193 +-------------- components/Hero.tsx | 193 ++++++++++++++- package-lock.json | 203 ++++++++++++++-- package.json | 7 +- 21 files changed, 1504 insertions(+), 893 deletions(-) create mode 100644 app/about/AboutClient.tsx create mode 100644 app/contact/ContactClient.tsx create mode 100644 app/login/LoginClient.tsx create mode 100644 app/manufacturing/ManufacturingClient.tsx create mode 100644 app/products/ProductsClient.tsx create mode 100644 app/rentals/RentalsClient.tsx diff --git a/app/about/AboutClient.tsx b/app/about/AboutClient.tsx new file mode 100644 index 0000000..daa6ff4 --- /dev/null +++ b/app/about/AboutClient.tsx @@ -0,0 +1,178 @@ +"use client"; + +import Link from 'next/link'; +import Image from 'next/image'; +import { useState, useEffect } from 'react'; + +export default function AboutPage() { + const [testIndex, setTestIndex] = useState(0); + const [openFaq, setOpenFaq] = useState(null); + + const testimonials = [ + { quote: "VG Fence consistently delivers high-quality materials on time. Their contractor pricing allows me to stay competitive, and their inventory is unmatched.", author: "Mark S.", role: "Local Fence Contractor" }, + { quote: "Their galvanized and black finish railings are top-notch. It's rare to find a supplier that combines durability with such an aesthetic appeal.", author: "Sarah L.", role: "Property Manager" }, + { quote: "The team at VG Fence is incredibly knowledgeable. They helped us select the right ornamental fencing for our latest residential development.", author: "David K.", role: "Construction Manager" } + ]; + + const nextTestimonial = () => setTestIndex((prev) => (prev + 1) % testimonials.length); + const prevTestimonial = () => setTestIndex((prev) => (prev - 1 + testimonials.length) % testimonials.length); + + const faqs = [ + { question: "Do you offer contractor pricing?", answer: "Yes! We provide dedicated contractor accounts with specialized pricing. You need to create an account and verify your business details to unlock these rates." }, + { question: "Do you offer installation services?", answer: "While our primary focus is supplying high-quality materials, we do provide installation support for select projects based on their size and scope." }, + { question: "Where are your materials sourced from?", answer: "We source professional-grade materials focused on durability, specifically suited for Canada's diverse climate, including robust galvanized and black finish options." }, + { question: "Do you offer delivery across Ontario?", answer: "Yes, we provide reliable delivery services across Ontario for both residential and commercial projects. Delivery times depend on the order size and location." }, + { question: "Can I order custom gate sizes?", answer: "Absolutely. We specialize in custom gate fabrication. You can provide us with your specific dimensions and requirements, and we will manufacture them to fit your project perfectly." }, + ]; + + // Auto-slide effect + useEffect(() => { + const interval = setInterval(() => { + nextTestimonial(); + }, 5000); // Change slide every 5 seconds + + return () => clearInterval(interval); + }, [testIndex]); // Reset timer when index changes (manual navigation) + + return ( +
+ {/* Inner Banner */} +
+
+

Your Trusted Fencing Partner

+
+ + + Home + + / + About Us +
+
+
+ + {/* Our Story Section */} +
+
+
+
+
Our Story
+

Your Trusted Fencing Partner

+
+

+ At VG Fence, we are dedicated to providing high-quality fencing and railing materials to contractors, property managers, event organizers, and homeowners. With years of experience in the industry, we have built a reputation as a reliable supplier for both commercial and residential projects. +

+

+ We stock and supply a comprehensive range of fence products, including chain link fences, temporary fencing, ornamental fences, wood fence hardware, gates, railings, deck products, and privacy screens. All our materials are available in galvanized and black finishes, ensuring durability and long-lasting performance. +

+

+ While our focus is primarily on material supply, we also provide installation support for select projects, based on size and scope. +

+
+ Learn More +
+
+
+ +
+
+ Quality modern fencing in a beautiful garden +
+
+
Our Mission
+
+ To supply premium fencing and railing materials that combine quality, durability, and convenience, helping every project — large or small — succeed. +
+
+
+
+
+
+ + {/* Mission Section */} + {/*
+
+
Our Mission
+

Empowering Success.

+
+

+ "To supply premium fencing and railing materials that combine quality, durability, and convenience, + helping every project — large or small — succeed." +

+
+
+
*/} + + {/* Testimonial Section (Slider) */} +
+
+
Testimonials
+

What Our Partners Say.

+ +
+
+ {testimonials.map((test, idx) => ( +
+
+
{test.quote}
+
{test.author}
+
{test.role}
+
+
+ ))} +
+
+ + +
+
+
+
+ + {/* FAQ Section (Accordion) */} +
+
+
FAQ
+

Frequently Asked Questions.

+ +
+ {faqs.map((faq, idx) => ( +
+
setOpenFaq(openFaq === idx ? null : idx)} + > +
{faq.question}
+
+ ▾ +
+
+
+
+ {faq.answer} +
+
+
+ ))} +
+
+
+ + {/* CTA Section */} + {/*
+

Ready to Build?

+

Explore Ontario's most reliable inventory of fencing products.

+
+ View Full Catalog + Contact Us +
+
*/} +
+ ); +} + diff --git a/app/about/page.tsx b/app/about/page.tsx index daa6ff4..4bb18a1 100644 --- a/app/about/page.tsx +++ b/app/about/page.tsx @@ -1,178 +1,14 @@ -"use client"; +import type { Metadata } from "next"; +import AboutClient from "./AboutClient"; -import Link from 'next/link'; -import Image from 'next/image'; -import { useState, useEffect } from 'react'; +export const metadata: Metadata = { + title: "About Us | VG Fence Products", + description: "Learn about Ontario's leading B2B supplier for fencing and railing products. Quality, durability, and expert service.", + alternates: { + canonical: "/about" + } +}; export default function AboutPage() { - const [testIndex, setTestIndex] = useState(0); - const [openFaq, setOpenFaq] = useState(null); - - const testimonials = [ - { quote: "VG Fence consistently delivers high-quality materials on time. Their contractor pricing allows me to stay competitive, and their inventory is unmatched.", author: "Mark S.", role: "Local Fence Contractor" }, - { quote: "Their galvanized and black finish railings are top-notch. It's rare to find a supplier that combines durability with such an aesthetic appeal.", author: "Sarah L.", role: "Property Manager" }, - { quote: "The team at VG Fence is incredibly knowledgeable. They helped us select the right ornamental fencing for our latest residential development.", author: "David K.", role: "Construction Manager" } - ]; - - const nextTestimonial = () => setTestIndex((prev) => (prev + 1) % testimonials.length); - const prevTestimonial = () => setTestIndex((prev) => (prev - 1 + testimonials.length) % testimonials.length); - - const faqs = [ - { question: "Do you offer contractor pricing?", answer: "Yes! We provide dedicated contractor accounts with specialized pricing. You need to create an account and verify your business details to unlock these rates." }, - { question: "Do you offer installation services?", answer: "While our primary focus is supplying high-quality materials, we do provide installation support for select projects based on their size and scope." }, - { question: "Where are your materials sourced from?", answer: "We source professional-grade materials focused on durability, specifically suited for Canada's diverse climate, including robust galvanized and black finish options." }, - { question: "Do you offer delivery across Ontario?", answer: "Yes, we provide reliable delivery services across Ontario for both residential and commercial projects. Delivery times depend on the order size and location." }, - { question: "Can I order custom gate sizes?", answer: "Absolutely. We specialize in custom gate fabrication. You can provide us with your specific dimensions and requirements, and we will manufacture them to fit your project perfectly." }, - ]; - - // Auto-slide effect - useEffect(() => { - const interval = setInterval(() => { - nextTestimonial(); - }, 5000); // Change slide every 5 seconds - - return () => clearInterval(interval); - }, [testIndex]); // Reset timer when index changes (manual navigation) - - return ( -
- {/* Inner Banner */} -
-
-

Your Trusted Fencing Partner

-
- - - Home - - / - About Us -
-
-
- - {/* Our Story Section */} -
-
-
-
-
Our Story
-

Your Trusted Fencing Partner

-
-

- At VG Fence, we are dedicated to providing high-quality fencing and railing materials to contractors, property managers, event organizers, and homeowners. With years of experience in the industry, we have built a reputation as a reliable supplier for both commercial and residential projects. -

-

- We stock and supply a comprehensive range of fence products, including chain link fences, temporary fencing, ornamental fences, wood fence hardware, gates, railings, deck products, and privacy screens. All our materials are available in galvanized and black finishes, ensuring durability and long-lasting performance. -

-

- While our focus is primarily on material supply, we also provide installation support for select projects, based on size and scope. -

-
- Learn More -
-
-
- -
-
- Quality modern fencing in a beautiful garden -
-
-
Our Mission
-
- To supply premium fencing and railing materials that combine quality, durability, and convenience, helping every project — large or small — succeed. -
-
-
-
-
-
- - {/* Mission Section */} - {/*
-
-
Our Mission
-

Empowering Success.

-
-

- "To supply premium fencing and railing materials that combine quality, durability, and convenience, - helping every project — large or small — succeed." -

-
-
-
*/} - - {/* Testimonial Section (Slider) */} -
-
-
Testimonials
-

What Our Partners Say.

- -
-
- {testimonials.map((test, idx) => ( -
-
-
{test.quote}
-
{test.author}
-
{test.role}
-
-
- ))} -
-
- - -
-
-
-
- - {/* FAQ Section (Accordion) */} -
-
-
FAQ
-

Frequently Asked Questions.

- -
- {faqs.map((faq, idx) => ( -
-
setOpenFaq(openFaq === idx ? null : idx)} - > -
{faq.question}
-
- ▾ -
-
-
-
- {faq.answer} -
-
-
- ))} -
-
-
- - {/* CTA Section */} - {/*
-

Ready to Build?

-

Explore Ontario's most reliable inventory of fencing products.

-
- View Full Catalog - Contact Us -
-
*/} -
- ); + return ; } - diff --git a/app/blog/[slug]/page.tsx b/app/blog/[slug]/page.tsx index 68821dc..3edacdb 100644 --- a/app/blog/[slug]/page.tsx +++ b/app/blog/[slug]/page.tsx @@ -21,6 +21,9 @@ export async function generateMetadata({ params }: Props) { return { title: `${blog.title} | VG Fence Products`, description: blog.excerpt, + alternates: { + canonical: `/blog/${blog.slug}` + } }; } diff --git a/app/blog/page.tsx b/app/blog/page.tsx index 96988aa..1d30174 100644 --- a/app/blog/page.tsx +++ b/app/blog/page.tsx @@ -4,7 +4,10 @@ import { blogs } from "@/data/blogs"; export const metadata = { title: "Blog | VG Fence Products", - description: "Read our latest articles about fencing installation, maintenance, and product choices.", + description: "Read our latest articles about fencing installation, maintenance, and product choices for your next project.", + alternates: { + canonical: "/blog" + } }; export default function BlogPage() { diff --git a/app/contact/ContactClient.tsx b/app/contact/ContactClient.tsx new file mode 100644 index 0000000..2686122 --- /dev/null +++ b/app/contact/ContactClient.tsx @@ -0,0 +1,276 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import ReCAPTCHA from "react-google-recaptcha"; +import axios from "axios"; +import Link from 'next/link'; + +const ContactClient = () => { + const [formData, setFormData] = useState({ + name: "", + phone: "", + email: "", + service: "Commercial", + message: "", + }); + + const [formErrors, setFormErrors] = useState({}); + const [captchaToken, setCaptchaToken] = useState(null); + const [alert, setAlert] = useState({ show: false, type: "", message: "" }); + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData((prev) => ({ ...prev, [name]: value })); + }; + + const handleCaptchaChange = (token: string | null) => { + setCaptchaToken(token); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + const errors: any = {}; + if (!formData.name.trim()) errors.name = "Name is required."; + if (!formData.phone.trim()) errors.phone = "Phone is required."; + if (!formData.email.trim()) errors.email = "Email is required."; + if (!formData.service.trim()) errors.service = "Please select a service."; + if (!formData.message.trim()) errors.message = "Message is required."; + if (!captchaToken) errors.captcha = "Please verify the CAPTCHA."; + + setFormErrors(errors); + if (Object.keys(errors).length > 0) return; + + const emailData = { + ...formData, + message: `Project Type: ${formData.service}

Message: ${formData.message}`, + to: "info@vgfenceproducts.com", + senderName: "VG Fence Contact Page", + recaptchaToken: captchaToken, + }; + + setAlert({ show: true, type: "info", message: "Sending your message..." }); + + try { + await axios.post("https://mailserver.metatronnest.com/send", emailData, { + headers: { "Content-Type": "application/json" }, + }); + + setAlert({ + show: true, + type: "success", + message: "Thank you! Your message has been sent successfully.", + }); + + setFormData({ + name: "", + phone: "", + email: "", + service: "Commercial", + message: "", + }); + setCaptchaToken(null); + setFormErrors({}); + } catch (error) { + console.error("❌ Error sending email:", error); + setAlert({ + show: true, + type: "danger", + message: "Failed to send message. Please try again later.", + }); + } + }; + + useEffect(() => { + if (alert.show && alert.type !== "info") { + const timer = setTimeout(() => { + setAlert((prev) => ({ ...prev, show: false })); + }, 5000); + return () => clearTimeout(timer); + } + }, [alert.show, alert.type]); + + return ( +
+ {/* Inner Banner */} +
+
+

How to Contact Us

+
+ + + Home + + / + Contact +
+
+
+ + {/* Contact Content */} +
+
+
+ + {/* Left Col: Info & Map */} +
+
+

Office Details.

+ +
+
+
+ +
+
+
Our Location
+
125 Earl Thompson Rd, Ayr, ON N0B 1E0, Canada
+
+
+ +
+
+ +
+
+
Direct Line Numbers
+
+1 226-888-7999
+
+
+ +
+
+ +
+
+
Our Email
+
info@vgfenceproducts.com
+
+
+
+
+ + {/* Map Placeholder */} +
+ +
+
+ +
+
+

Send us a Message.

+

Fill out the form below and our team will get back to you within 24 hours.

+
+ +
+ {alert.show && ( +
+ {alert.message} +
+ )} + +
+
+ + + {formErrors.name && {formErrors.name}} +
+
+ + + {formErrors.email && {formErrors.email}} +
+
+ +
+
+ + + {formErrors.phone && {formErrors.phone}} +
+
+ + +
+
+ +
+ + + {formErrors.message && {formErrors.message}} +
+ +
+ + {formErrors.captcha && {formErrors.captcha}} +
+ + +
+
+ +
+
+
+ +
+ ); +}; + +export default ContactClient; diff --git a/app/contact/page.tsx b/app/contact/page.tsx index c9c1297..6babf71 100644 --- a/app/contact/page.tsx +++ b/app/contact/page.tsx @@ -1,128 +1,14 @@ -import Link from 'next/link'; -import Navbar from '@/components/Navbar'; -import Footer from '@/components/Footer'; +import type { Metadata } from "next"; +import ContactClient from "./ContactClient"; + +export const metadata: Metadata = { + title: "Contact Us | VG Fence Products", + description: "Get in touch with our expert team for quotes, product inquiries, or technical support. We are located in Ayr, Ontario and serve the entire province.", + alternates: { + canonical: "/contact" + } +}; export default function ContactPage() { - return ( -
- {/* Inner Banner */} -
-
-

How to Contact Us

-
- - - Home - - / - Contact -
-
-
- - {/* Contact Content */} -
-
-
- - {/* Left Col: Info & Map */} -
-
-

Office Details.

- -
-
-
- -
-
-
Our Location
-
125 Earl Thompson Rd, Ayr, ON N0B 1E0, Canada
-
-
- -
-
- -
-
-
Direct Line Numbers
-
+1 226-888-7999
-
-
- -
-
- -
-
-
Our Email
-
info@vgfenceproducts.com
-
-
-
-
- - {/* Map Placeholder */} -
- -
-
- -
-
-

Send us a Message.

-

Fill out the form below and our team will get back to you within 24 hours.

-
- -
-
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- -
- - -
- - -
-
- -
-
-
- -
- ); + return ; } diff --git a/app/globals.css b/app/globals.css index dbd29da..6225ddf 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1469,7 +1469,7 @@ footer { background: rgba(255, 255, 255, .03); border: 1px solid rgba(255, 255, 255, .1); border-radius: 16px; - padding: 48px; + padding: 35px; backdrop-filter: blur(20px); position: relative; z-index: 2; diff --git a/app/layout.tsx b/app/layout.tsx index 76b3442..d690268 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -17,11 +17,15 @@ const barlowCondensed = Barlow_Condensed({ }); export const metadata: Metadata = { + metadataBase: new URL("https://vgfenceproducts.ca"), title: "VG Fence Products — Ontario's B2B Fence Supply Partner", description: "Supplying contractors, builders, and property managers across Ontario with chain link, ornamental, composite, glass railing, and stain products.", icons: { icon: "/assets/favicon.webp", }, + alternates: { + canonical: "/", + }, }; export default function RootLayout({ diff --git a/app/login/LoginClient.tsx b/app/login/LoginClient.tsx new file mode 100644 index 0000000..1a4a354 --- /dev/null +++ b/app/login/LoginClient.tsx @@ -0,0 +1,125 @@ +"use client"; +import Link from 'next/link'; +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; + +export default function LoginPage() { + const router = useRouter(); + + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + + const [loading, setLoading] = useState(false); + const [err, setErr] = useState(null); + const [msg, setMsg] = useState(null); + + const submitForm = async (e: React.FormEvent) => { + e.preventDefault(); + setErr(null); + setMsg(null); + + if (!email || !password) { + setErr('Please enter username/email and password.'); + return; + } + + setLoading(true); + try { + const res = await fetch('http://localhost:3050/api/auth/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, password }), + }); + + const contentType = res.headers.get('content-type') || ''; + const data = contentType.includes('application/json') ? await res.json() : await res.text(); + + if (!res.ok) { + throw new Error((typeof data === 'object' && (data?.message || data?.error)) || `Login failed (${res.status})`); + } + + try { + sessionStorage.setItem('USERID', data.userid); + localStorage.setItem('vgproducts_uid', data.userid); + localStorage.setItem('d4a_email', data.email); + } catch { + console.log('Error setting storage'); + } + + setMsg('Login successful!'); + setTimeout(() => router.push('/'), 500); // Redirect to homepage or dashboard after login + } catch (e: any) { + setErr(e?.message || 'Something went wrong. Please try again.'); + } finally { + setLoading(false); + } + }; + + return ( +
+ {/* ── INNER BANNER ── */} + {/*
+
+

Customer Login

+
+ + + Home + + / + Login +
+
+
*/} + +
+
+

Access Your Account

+ + {err &&
{err}
} + {msg &&
{msg}
} + +
+
+ + setEmail(e.target.value)} + /> +
+
+ + setPassword(e.target.value)} + /> +
+ +
+
+ + +
+ Forgot Password? +
+ + +
+ + {/*
+ Don't have a contractor account?
+ Apply for contractor pricing +
*/} +
+
+
+ ); +} diff --git a/app/login/page.tsx b/app/login/page.tsx index 1a4a354..63dd8cd 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -1,125 +1,14 @@ -"use client"; -import Link from 'next/link'; -import { useState } from 'react'; -import { useRouter } from 'next/navigation'; +import type { Metadata } from "next"; +import LoginClient from "./LoginClient"; + +export const metadata: Metadata = { + title: "Customer Login | VG Fence Products", + description: "Access your contractor portal to view exclusive pricing, manage orders, and track deliveries. Secure B2B portal for Ontario fencing professionals.", + alternates: { + canonical: "/login" + } +}; export default function LoginPage() { - const router = useRouter(); - - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - - const [loading, setLoading] = useState(false); - const [err, setErr] = useState(null); - const [msg, setMsg] = useState(null); - - const submitForm = async (e: React.FormEvent) => { - e.preventDefault(); - setErr(null); - setMsg(null); - - if (!email || !password) { - setErr('Please enter username/email and password.'); - return; - } - - setLoading(true); - try { - const res = await fetch('http://localhost:3050/api/auth/login', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ email, password }), - }); - - const contentType = res.headers.get('content-type') || ''; - const data = contentType.includes('application/json') ? await res.json() : await res.text(); - - if (!res.ok) { - throw new Error((typeof data === 'object' && (data?.message || data?.error)) || `Login failed (${res.status})`); - } - - try { - sessionStorage.setItem('USERID', data.userid); - localStorage.setItem('vgproducts_uid', data.userid); - localStorage.setItem('d4a_email', data.email); - } catch { - console.log('Error setting storage'); - } - - setMsg('Login successful!'); - setTimeout(() => router.push('/'), 500); // Redirect to homepage or dashboard after login - } catch (e: any) { - setErr(e?.message || 'Something went wrong. Please try again.'); - } finally { - setLoading(false); - } - }; - - return ( -
- {/* ── INNER BANNER ── */} - {/*
-
-

Customer Login

-
- - - Home - - / - Login -
-
-
*/} - -
-
-

Access Your Account

- - {err &&
{err}
} - {msg &&
{msg}
} - -
-
- - setEmail(e.target.value)} - /> -
-
- - setPassword(e.target.value)} - /> -
- -
-
- - -
- Forgot Password? -
- - -
- - {/*
- Don't have a contractor account?
- Apply for contractor pricing -
*/} -
-
-
- ); + return ; } diff --git a/app/manufacturing/ManufacturingClient.tsx b/app/manufacturing/ManufacturingClient.tsx new file mode 100644 index 0000000..0c6911a --- /dev/null +++ b/app/manufacturing/ManufacturingClient.tsx @@ -0,0 +1,145 @@ +"use client"; + +import Link from 'next/link'; +import Image from 'next/image'; + +export default function ManufacturingPage() { + return ( +
+ {/* Inner Banner */} +
+
+

Job Order Manufacturing

+
+ + + Home + + / + Manufacturing +
+
+
+ + {/* Intro Section */} +
+
+
+
+
Our Capabilities
+

Bulk & Job Order Manufacturing

+
+

+ VG Fence specializes in bulk and job order manufacturing for contractors, developers, and businesses. +

+

+ From residential and commercial gates to brackets or custom metal components, we handle production from start to finish — ensuring high-quality, durable products delivered on time. +

+
+ + Get a custom quote today for your next project → + +
+
+
+ +
+
+ Industrial metal fabrication and manufacturing +
+
+
Fence Solutions
+ {/*
Fence Solutions
*/} +
+
+
+
+
+ + {/* Services Grid */} +
+
+
+
What We Build
+

Specialized Production.

+
+ +
+ {/* Gates */} +
+
+ +
+

Residential & Commercial Gates

+
    +
  • Sliding, swing, and decorative gates
  • +
  • Custom sizes and finishes
  • +
  • Galvanized or powder-coated for durability
  • +
  • Factory-fabricated for precise fit and fast installation
  • +
+
+ + {/* Components */} +
+
+ +
+

Brackets, Rails & Metal Components

+
    +
  • Fence brackets, mounting hardware, rail supports
  • +
  • Custom metal components for all types of projects
  • +
  • Bulk manufacturing for contractors and developers
  • +
  • High-quality steel with consistent dimensions
  • +
+
+ + {/* Bulk Orders */} +
+
+ +
+

Bulk & Job Orders

+
    +
  • Large-scale production for commercial or municipal projects
  • +
  • Pre-assembled material bundles for faster execution
  • +
  • Optional customization for dimensions and finishes
  • +
  • Competitive pricing for bulk quantities
  • +
+
+ + {/* Fabrication */} +
+
+ +
+

Full Fabrication Works

+
    +
  • Cutting, bending, welding, and finishing
  • +
  • Custom fabrication for any size requirement
  • +
  • Assistance with finishing touches for panels and accessories
  • +
  • Ensures every product meets durability standards
  • +
+
+
+
+
+ + {/* CTA Section */} +
+

Get Your Custom Quote Today

+

+ Fill out our job order & fabrication request form or call 226-888-7999 to discuss your bulk and custom fabrication needs. +

+
+ Contact Us Now + Call Us Today +
+
+
+ ); +} diff --git a/app/manufacturing/page.tsx b/app/manufacturing/page.tsx index 0c6911a..8217c81 100644 --- a/app/manufacturing/page.tsx +++ b/app/manufacturing/page.tsx @@ -1,145 +1,14 @@ -"use client"; +import type { Metadata } from "next"; +import ManufacturingClient from "./ManufacturingClient"; -import Link from 'next/link'; -import Image from 'next/image'; +export const metadata: Metadata = { + title: "Bulk & Job Order Manufacturing | VG Fence Products", + description: "Specialized metal fabrication for fencing components, gates, and custom brackets. Bulk manufacturing solutions for contractors in Ontario.", + alternates: { + canonical: "/manufacturing" + } +}; export default function ManufacturingPage() { - return ( -
- {/* Inner Banner */} -
-
-

Job Order Manufacturing

-
- - - Home - - / - Manufacturing -
-
-
- - {/* Intro Section */} -
-
-
-
-
Our Capabilities
-

Bulk & Job Order Manufacturing

-
-

- VG Fence specializes in bulk and job order manufacturing for contractors, developers, and businesses. -

-

- From residential and commercial gates to brackets or custom metal components, we handle production from start to finish — ensuring high-quality, durable products delivered on time. -

-
- - Get a custom quote today for your next project → - -
-
-
- -
-
- Industrial metal fabrication and manufacturing -
-
-
Fence Solutions
- {/*
Fence Solutions
*/} -
-
-
-
-
- - {/* Services Grid */} -
-
-
-
What We Build
-

Specialized Production.

-
- -
- {/* Gates */} -
-
- -
-

Residential & Commercial Gates

-
    -
  • Sliding, swing, and decorative gates
  • -
  • Custom sizes and finishes
  • -
  • Galvanized or powder-coated for durability
  • -
  • Factory-fabricated for precise fit and fast installation
  • -
-
- - {/* Components */} -
-
- -
-

Brackets, Rails & Metal Components

-
    -
  • Fence brackets, mounting hardware, rail supports
  • -
  • Custom metal components for all types of projects
  • -
  • Bulk manufacturing for contractors and developers
  • -
  • High-quality steel with consistent dimensions
  • -
-
- - {/* Bulk Orders */} -
-
- -
-

Bulk & Job Orders

-
    -
  • Large-scale production for commercial or municipal projects
  • -
  • Pre-assembled material bundles for faster execution
  • -
  • Optional customization for dimensions and finishes
  • -
  • Competitive pricing for bulk quantities
  • -
-
- - {/* Fabrication */} -
-
- -
-

Full Fabrication Works

-
    -
  • Cutting, bending, welding, and finishing
  • -
  • Custom fabrication for any size requirement
  • -
  • Assistance with finishing touches for panels and accessories
  • -
  • Ensures every product meets durability standards
  • -
-
-
-
-
- - {/* CTA Section */} -
-

Get Your Custom Quote Today

-

- Fill out our job order & fabrication request form or call 226-888-7999 to discuss your bulk and custom fabrication needs. -

-
- Contact Us Now - Call Us Today -
-
-
- ); + return ; } diff --git a/app/page.tsx b/app/page.tsx index 208cef7..ad07ac2 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,3 +1,4 @@ +import type { Metadata } from "next"; import Hero from "@/components/Hero"; import TrustBar from "@/components/TrustBar"; import Products from "@/components/Products"; @@ -7,6 +8,14 @@ import WhoWeServe from "@/components/WhoWeServe"; import StainPromo from "@/components/StainPromo"; import CTA from "@/components/CTA"; +export const metadata: Metadata = { + title: "VG Fence Products | Ontario's B2B Fence Supply Partner", + description: "Premier supplier of professional-grade fencing and railing materials for contractors, builders, and property managers across Ontario.", + alternates: { + canonical: "/" + } +}; + export default function Home() { return ( <> diff --git a/app/products/ProductsClient.tsx b/app/products/ProductsClient.tsx new file mode 100644 index 0000000..c8b7c8c --- /dev/null +++ b/app/products/ProductsClient.tsx @@ -0,0 +1,118 @@ +"use client"; +import { useState } from 'react'; +import Link from 'next/link'; +import Image from 'next/image'; +import { products } from '@/data/products'; + +export default function ProductsPage() { + const [currentPage, setCurrentPage] = useState(1); + const productsPerPage = 12; + + // Calculate pagination + const indexOfLastProduct = currentPage * productsPerPage; + const indexOfFirstProduct = indexOfLastProduct - productsPerPage; + const currentProducts = products.slice(indexOfFirstProduct, indexOfLastProduct); + const totalPages = Math.ceil(products.length / productsPerPage); + const paginate = (pageNumber: number) => setCurrentPage(pageNumber); + + return ( +
+ {/* Inner Banner */} +
+
+

Our Product Catalog

+
+ + + Home + + / + Products +
+
+
+ + {/* Grid Content */} +
+
+ {currentProducts.map((product) => ( +
+
+ {product.name} +
+
{product.name}
+
+ {product.description} +
+
+
+ {product.price} +
+ + View Product → + +
+
+ ))} +
+ + {/* Pagination */} + {totalPages > 1 && ( +
+ + +
+ {Array.from({ length: totalPages }, (_, i) => i + 1).map((num) => ( + + ))} +
+ + +
+ )} +
+
+ ); +} diff --git a/app/products/[slug]/page.tsx b/app/products/[slug]/page.tsx index 06986c9..8310ed1 100644 --- a/app/products/[slug]/page.tsx +++ b/app/products/[slug]/page.tsx @@ -3,6 +3,8 @@ import Link from 'next/link'; import { products } from '@/data/products'; import { notFound } from 'next/navigation'; +import type { Metadata } from "next"; + // Pre-generate all product detail pages at build time export function generateStaticParams() { return products.map((product) => ({ @@ -10,6 +12,21 @@ export function generateStaticParams() { })); } +export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise { + const { slug } = await params; + const product = products.find((p) => p.slug === slug); + + if (!product) return { title: "Product Not Found" }; + + return { + title: `${product.name} | VG Fence Products`, + description: product.description, + alternates: { + canonical: `/products/${product.slug}` + } + }; +} + // In Next.js 15+/16, params is a Promise and must be awaited export default async function ProductDetailPage({ params, diff --git a/app/products/page.tsx b/app/products/page.tsx index c8b7c8c..21945e0 100644 --- a/app/products/page.tsx +++ b/app/products/page.tsx @@ -1,118 +1,14 @@ -"use client"; -import { useState } from 'react'; -import Link from 'next/link'; -import Image from 'next/image'; -import { products } from '@/data/products'; +import type { Metadata } from "next"; +import ProductsClient from "./ProductsClient"; + +export const metadata: Metadata = { + title: "Product Catalog | VG Fence Products", + description: "Explore our extensive range of professional-grade fencing and railing materials. From chain link to ornamental steel and composite panels.", + alternates: { + canonical: "/products" + } +}; export default function ProductsPage() { - const [currentPage, setCurrentPage] = useState(1); - const productsPerPage = 12; - - // Calculate pagination - const indexOfLastProduct = currentPage * productsPerPage; - const indexOfFirstProduct = indexOfLastProduct - productsPerPage; - const currentProducts = products.slice(indexOfFirstProduct, indexOfLastProduct); - const totalPages = Math.ceil(products.length / productsPerPage); - const paginate = (pageNumber: number) => setCurrentPage(pageNumber); - - return ( -
- {/* Inner Banner */} -
-
-

Our Product Catalog

-
- - - Home - - / - Products -
-
-
- - {/* Grid Content */} -
-
- {currentProducts.map((product) => ( -
-
- {product.name} -
-
{product.name}
-
- {product.description} -
-
-
- {product.price} -
- - View Product → - -
-
- ))} -
- - {/* Pagination */} - {totalPages > 1 && ( -
- - -
- {Array.from({ length: totalPages }, (_, i) => i + 1).map((num) => ( - - ))} -
- - -
- )} -
-
- ); + return ; } diff --git a/app/rentals/RentalsClient.tsx b/app/rentals/RentalsClient.tsx new file mode 100644 index 0000000..6054fff --- /dev/null +++ b/app/rentals/RentalsClient.tsx @@ -0,0 +1,189 @@ +import Link from "next/link"; +import Image from "next/image"; + +export const metadata = { + title: "Fence Rentals | VG Fence Products", + description: "Temporary fence rentals for construction sites, events, and security perimeters. Flexible rental terms and quick delivery.", +}; + +export default function RentalsPage() { + const products = [ + { name: "Temporary Fence Panels", desc: "Durable and easy to assemble", img: "/assets/temp-fence-panel.png" }, + { name: "Temporary Fence Gates", desc: "Single or double swing gates", img: "/assets/about-fencing.png" }, + { name: "Fence Wheels", desc: "Smooth movement for gate panels", img: "/assets/manufacturing-hero.png" }, + { name: "Wind Bracing", desc: "Prevent fence panels from tipping", img: "/assets/about-fencing.png" }, + { name: "Fence Bases", desc: "Concrete, rubber, or plastic bases for stability", img: "/assets/manufacturing-hero.png" }, + { name: "Fence Clamps & Accessories", desc: "Secure connections for panels", img: "/assets/about-fencing.png" }, + { name: "Sandbags / Ballast", desc: "Extra stability for windy or busy areas", img: "/assets/manufacturing-hero.png" }, + { name: "Debris & Safety Netting", desc: "Optional add-ons for construction sites", img: "/assets/about-fencing.png" }, + ]; + + const applications = [ + { id: 1, title: "Construction Sites", desc: "Keep workers and equipment secure" }, + { id: 2, title: "Events and Festivals", desc: "Crowd control and perimeter fencing" }, + { id: 3, title: "Sports and Film Production", desc: "Temporary barriers for staff and public safety" }, + { id: 4, title: "Short-Term and Long-Term Projects", desc: "Flexible rental periods" }, + ]; + + return ( +
+ {/* ── INNER BANNER ── */} +
+
+

Fence Rentals

+
+ + + Home + + / + Rentals +
+
+
+ + {/* ── INTRO SECTION (REDESIGNED) ── */} +
+
+
+
+
FENCE RENTALS
+

+ Temporary Fence Rentals for Construction, Events, and Security +

+

+ VG Fence provides high-quality temporary fencing solutions for contractors, event organizers, and property managers. Our rentals are flexible, reliable, and designed to secure construction sites, public events, and temporary perimeters. +

+ +
+ {/* */} + Installation support for temporary fences is available for select projects +
+ + {/*
+
+ 85% +
+
+

Satisfied Customer

+

Reliable support for contractors and event managers across Ontario projects.

+
+
*/} +
+ +
+
+ Industrial fencing installation experts +
+
+
+
+
+ + {/* ── PRODUCTS SECTION ── */} +
+
+
+
Equipment List
+

Products & Equipment Available

+
+ +
+ {products.map((p, i) => ( +
+
+ {p.name} +
+
+

{p.name}

+

{p.desc}

+
+
+ ))} +
+
+
+ + {/* ── APPLICATIONS SECTION ── */} +
+
+
+
Tailored Solutions
+

Applications

+
+ +
+ {applications.map((app) => ( +
+
{app.id}
+
+

{app.title}

+

{app.desc}

+
+
+ ))} +
+
+
+ + {/* ── WHY CHOOSE SECTION (IMAGE + TIMELINE) ── */} +
+
+ VG Fence Temporary Fencing +
+
+

Why Choose VG Fence Rentals

+ +
    +
  • +
    + +
    +
    Flexible rental terms (daily, weekly, monthly)
    +
  • +
  • +
    + +
    +
    Durable and professional-grade materials
    +
  • +
  • +
    + +
    +
    Quick delivery & setup support available
    +
  • +
  • +
    + +
    +
    Galvanized or black finishes
    +
  • +
  • +
    + +
    +
    Reliable support for contractors and event managers
    +
  • +
+ +
+

Secure Your Temporary Fencing Today

+ Request a Quote +
+
+
+ +
+ ); +} diff --git a/app/rentals/page.tsx b/app/rentals/page.tsx index 6054fff..ea5fee5 100644 --- a/app/rentals/page.tsx +++ b/app/rentals/page.tsx @@ -1,189 +1,14 @@ -import Link from "next/link"; -import Image from "next/image"; +import type { Metadata } from "next"; +import RentalsClient from "./RentalsClient"; -export const metadata = { - title: "Fence Rentals | VG Fence Products", - description: "Temporary fence rentals for construction sites, events, and security perimeters. Flexible rental terms and quick delivery.", +export const metadata: Metadata = { + title: "Temporary Fence Rentals | VG Fence Products", + description: "Reliable temporary fencing solutions for construction sites and public events in Ontario. Daily, weekly, and monthly rental terms available.", + alternates: { + canonical: "/rentals" + } }; export default function RentalsPage() { - const products = [ - { name: "Temporary Fence Panels", desc: "Durable and easy to assemble", img: "/assets/temp-fence-panel.png" }, - { name: "Temporary Fence Gates", desc: "Single or double swing gates", img: "/assets/about-fencing.png" }, - { name: "Fence Wheels", desc: "Smooth movement for gate panels", img: "/assets/manufacturing-hero.png" }, - { name: "Wind Bracing", desc: "Prevent fence panels from tipping", img: "/assets/about-fencing.png" }, - { name: "Fence Bases", desc: "Concrete, rubber, or plastic bases for stability", img: "/assets/manufacturing-hero.png" }, - { name: "Fence Clamps & Accessories", desc: "Secure connections for panels", img: "/assets/about-fencing.png" }, - { name: "Sandbags / Ballast", desc: "Extra stability for windy or busy areas", img: "/assets/manufacturing-hero.png" }, - { name: "Debris & Safety Netting", desc: "Optional add-ons for construction sites", img: "/assets/about-fencing.png" }, - ]; - - const applications = [ - { id: 1, title: "Construction Sites", desc: "Keep workers and equipment secure" }, - { id: 2, title: "Events and Festivals", desc: "Crowd control and perimeter fencing" }, - { id: 3, title: "Sports and Film Production", desc: "Temporary barriers for staff and public safety" }, - { id: 4, title: "Short-Term and Long-Term Projects", desc: "Flexible rental periods" }, - ]; - - return ( -
- {/* ── INNER BANNER ── */} -
-
-

Fence Rentals

-
- - - Home - - / - Rentals -
-
-
- - {/* ── INTRO SECTION (REDESIGNED) ── */} -
-
-
-
-
FENCE RENTALS
-

- Temporary Fence Rentals for Construction, Events, and Security -

-

- VG Fence provides high-quality temporary fencing solutions for contractors, event organizers, and property managers. Our rentals are flexible, reliable, and designed to secure construction sites, public events, and temporary perimeters. -

- -
- {/* */} - Installation support for temporary fences is available for select projects -
- - {/*
-
- 85% -
-
-

Satisfied Customer

-

Reliable support for contractors and event managers across Ontario projects.

-
-
*/} -
- -
-
- Industrial fencing installation experts -
-
-
-
-
- - {/* ── PRODUCTS SECTION ── */} -
-
-
-
Equipment List
-

Products & Equipment Available

-
- -
- {products.map((p, i) => ( -
-
- {p.name} -
-
-

{p.name}

-

{p.desc}

-
-
- ))} -
-
-
- - {/* ── APPLICATIONS SECTION ── */} -
-
-
-
Tailored Solutions
-

Applications

-
- -
- {applications.map((app) => ( -
-
{app.id}
-
-

{app.title}

-

{app.desc}

-
-
- ))} -
-
-
- - {/* ── WHY CHOOSE SECTION (IMAGE + TIMELINE) ── */} -
-
- VG Fence Temporary Fencing -
-
-

Why Choose VG Fence Rentals

- -
    -
  • -
    - -
    -
    Flexible rental terms (daily, weekly, monthly)
    -
  • -
  • -
    - -
    -
    Durable and professional-grade materials
    -
  • -
  • -
    - -
    -
    Quick delivery & setup support available
    -
  • -
  • -
    - -
    -
    Galvanized or black finishes
    -
  • -
  • -
    - -
    -
    Reliable support for contractors and event managers
    -
  • -
- -
-

Secure Your Temporary Fencing Today

- Request a Quote -
-
-
- -
- ); + return ; } diff --git a/components/Hero.tsx b/components/Hero.tsx index f7ca884..ffd92a4 100644 --- a/components/Hero.tsx +++ b/components/Hero.tsx @@ -1,7 +1,105 @@ "use client"; + +import React, { useState, useEffect } from "react"; +import ReCAPTCHA from "react-google-recaptcha"; +import axios from "axios"; import Link from 'next/link'; export default function Hero() { + const [formData, setFormData] = useState({ + company: "", + name: "", + phone: "", + email: "", + product: "", + city: "", + quantity: "" + }); + + const [formErrors, setFormErrors] = useState({}); + const [captchaToken, setCaptchaToken] = useState(null); + const [alert, setAlert] = useState({ show: false, type: "", message: "" }); + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData((prev) => ({ ...prev, [name]: value })); + }; + + const handleCaptchaChange = (token: string | null) => { + setCaptchaToken(token); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + const errors: any = {}; + if (!formData.name.trim()) errors.name = "Name is required."; + if (!formData.phone.trim()) errors.phone = "Phone is required."; + if (!formData.email.trim()) errors.email = "Email is required."; + if (!formData.product.trim()) errors.product = "Please select a product."; + if (!captchaToken) errors.captcha = "Please verify the CAPTCHA."; + + setFormErrors(errors); + if (Object.keys(errors).length > 0) return; + + const emailData = { + name: formData.name, + email: formData.email, + phone: formData.phone, + message: ` + Company: ${formData.company}
+ Product: ${formData.product}
+ Job Site City: ${formData.city}
+ Quantity: ${formData.quantity} + `, + to: "info@vgfenceproducts.com", + senderName: "VG Fence Hero Form", + recaptchaToken: captchaToken, + }; + + setAlert({ show: true, type: "info", message: "Sending your request..." }); + + try { + await axios.post("https://mailserver.metatronnest.com/send", emailData, { + headers: { "Content-Type": "application/json" }, + }); + + setAlert({ + show: true, + type: "success", + message: "Thank you! Your quote request has been sent successfully.", + }); + + setFormData({ + company: "", + name: "", + phone: "", + email: "", + product: "", + city: "", + quantity: "" + }); + setCaptchaToken(null); + setFormErrors({}); + } catch (error) { + console.error("❌ Error sending email:", error); + setAlert({ + show: true, + type: "danger", + message: "Failed to send request. Please try again later.", + }); + } + }; + + useEffect(() => { + if (alert.show && alert.type !== "info") { + const timer = setTimeout(() => { + setAlert((prev) => ({ ...prev, show: false })); + }, 5000); + return () => clearTimeout(timer); + } + }, [alert.show, alert.type]); + return (
@@ -48,30 +146,82 @@ export default function Hero() {
Request a quote
Response within 2 business hours · Contractor pricing available
-
+ + {alert.show && ( +
+ {alert.message} +
+ )} + +
- +
- +
- +
- +
- @@ -88,14 +238,39 @@ export default function Hero() {
- +
- +
- + +
+ + {formErrors.captcha && {formErrors.captcha}} +
+ +
Or call us directly · info@vgfenceproducts.com
diff --git a/package-lock.json b/package-lock.json index 9268052..1fbe97b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,14 +8,19 @@ "name": "vgfence-website", "version": "0.1.0", "dependencies": { + "axios": "^1.15.0", "next": "16.2.4", "react": "19.2.4", - "react-dom": "19.2.4" + "react-dom": "19.2.4", + "react-google-recaptcha": "^3.1.0", + "react-icons": "^5.6.0", + "swiper": "^12.1.3" }, "devDependencies": { "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "@types/react-google-recaptcha": "^2.1.9", "eslint": "^9", "eslint-config-next": "16.2.4", "typescript": "^5" @@ -1289,6 +1294,16 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/react-google-recaptcha": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@types/react-google-recaptcha/-/react-google-recaptcha-2.1.9.tgz", + "integrity": "sha512-nT31LrBDuoSZJN4QuwtQSF3O89FVHC4jLhM+NtKEmVF5R1e8OY0Jo4//x2Yapn2aNHguwgX5doAq8Zo+Ehd0ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.58.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz", @@ -2103,6 +2118,12 @@ "node": ">= 0.4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2129,6 +2150,17 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", + "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -2239,7 +2271,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2339,6 +2370,18 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2497,6 +2540,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -2524,7 +2576,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -2622,7 +2673,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2632,7 +2682,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2670,7 +2719,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -2683,7 +2731,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3280,6 +3327,26 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -3296,11 +3363,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3361,7 +3443,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -3386,7 +3467,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -3474,7 +3554,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3539,7 +3618,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3552,7 +3630,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -3568,7 +3645,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3594,6 +3670,15 @@ "hermes-estree": "0.25.1" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4097,7 +4182,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -4247,7 +4331,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -4270,7 +4353,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4300,6 +4382,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -4454,7 +4557,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4752,7 +4854,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -4760,6 +4861,15 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4800,6 +4910,19 @@ "node": ">=0.10.0" } }, + "node_modules/react-async-script": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz", + "integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==", + "license": "MIT", + "dependencies": { + "hoist-non-react-statics": "^3.3.0", + "prop-types": "^15.5.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, "node_modules/react-dom": { "version": "19.2.4", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", @@ -4812,11 +4935,32 @@ "react": "^19.2.4" } }, + "node_modules/react-google-recaptcha": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz", + "integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.5.0", + "react-async-script": "^1.2.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, + "node_modules/react-icons": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.6.0.tgz", + "integrity": "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, "node_modules/reflect.getprototypeof": { @@ -5434,6 +5578,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swiper": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-12.1.3.tgz", + "integrity": "sha512-XcWlVmkHFICI4fuoJKgbp8PscDcS4i7pBH8nwJRBi3dpQvhCySwsWRYm4bOf/BzKVWkHOYaFw7qz9uBSrY3oug==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/swiperjs" + }, + { + "type": "open_collective", + "url": "http://opencollective.com/swiper" + } + ], + "license": "MIT", + "engines": { + "node": ">= 4.7.0" + } + }, "node_modules/tinyglobby": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", diff --git a/package.json b/package.json index 0b98ee6..359d283 100644 --- a/package.json +++ b/package.json @@ -9,14 +9,19 @@ "lint": "eslint" }, "dependencies": { + "axios": "^1.15.0", "next": "16.2.4", "react": "19.2.4", - "react-dom": "19.2.4" + "react-dom": "19.2.4", + "react-google-recaptcha": "^3.1.0", + "react-icons": "^5.6.0", + "swiper": "^12.1.3" }, "devDependencies": { "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "@types/react-google-recaptcha": "^2.1.9", "eslint": "^9", "eslint-config-next": "16.2.4", "typescript": "^5"