sitemap, contact, selenium updated
This commit is contained in:
parent
70871d179f
commit
8332e5764d
@ -1,12 +1,33 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect, ChangeEvent, FormEvent } from 'react';
|
||||
import type { Metadata } from 'next';
|
||||
import { Phone, Mail, MapPin, Instagram, Facebook, Twitter, Youtube } from 'lucide-react';
|
||||
import ReCAPTCHA from "react-google-recaptcha";
|
||||
import axios from "axios";
|
||||
import styles from './contact.module.css';
|
||||
|
||||
interface FormData {
|
||||
name: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
subject: string;
|
||||
message: string;
|
||||
company: string;
|
||||
}
|
||||
|
||||
interface FormErrors {
|
||||
name?: string;
|
||||
phone?: string;
|
||||
email?: string;
|
||||
subject?: string;
|
||||
message?: string;
|
||||
company?: string;
|
||||
captcha?: string;
|
||||
}
|
||||
|
||||
export default function ContactPage() {
|
||||
const [formData, setFormData] = useState({
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
name: '',
|
||||
email: '',
|
||||
company: '',
|
||||
@ -15,20 +36,85 @@ export default function ContactPage() {
|
||||
phone: '',
|
||||
});
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
||||
const [formErrors, setFormErrors] = useState<FormErrors>({});
|
||||
const [captchaToken, setCaptchaToken] = useState<string | null>(null);
|
||||
const [alert, setAlert] = useState<{ show: boolean; type: string; message: string }>({
|
||||
show: false,
|
||||
type: "",
|
||||
message: "",
|
||||
});
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
// Handle form submission
|
||||
console.log('Form submitted:', formData);
|
||||
alert('Thank you for your message! We\'ll get back to you soon.');
|
||||
const handleCaptchaChange = (token: string | null) => {
|
||||
setCaptchaToken(token);
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
const errors: FormErrors = {};
|
||||
if (!formData.name.trim()) errors.name = "Name is required.";
|
||||
if (!formData.phone.trim()) errors.phone = "Phone number is required.";
|
||||
if (!formData.email.trim()) errors.email = "Email is required.";
|
||||
if (!formData.subject.trim()) errors.subject = "Subject is required.";
|
||||
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: `
|
||||
<b>Name:</b> ${formData.name}<br />
|
||||
<b>Phone:</b> ${formData.phone}<br />
|
||||
<b>Email:</b> ${formData.email}<br />
|
||||
<b>Subject:</b> ${formData.subject}<br /><br />
|
||||
<b>Message:</b><br />${formData.message}
|
||||
`,
|
||||
to: "info@metatroncubesolutions.com",
|
||||
senderName: "SocialBuddy Contact Form",
|
||||
recaptchaToken: captchaToken,
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await axios.post("https://mailserver.metatronnest.com/send", emailData, {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
|
||||
setAlert({
|
||||
show: true,
|
||||
type: "success",
|
||||
message: res?.data?.message || "Message sent successfully!",
|
||||
});
|
||||
|
||||
setFormData({ name: "", phone: "", email: "", subject: "", message: "", company: "" });
|
||||
setCaptchaToken(null);
|
||||
setFormErrors({});
|
||||
} catch {
|
||||
setAlert({
|
||||
show: true,
|
||||
type: "danger",
|
||||
message: "Failed to send message. Please try again later.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (alert.show) {
|
||||
const timer = setTimeout(() => {
|
||||
setAlert(prev => ({ ...prev, show: false }));
|
||||
}, 5000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [alert.show]);
|
||||
|
||||
const contactInfoCards = [
|
||||
{
|
||||
icon: <MapPin size={32} />,
|
||||
@ -133,6 +219,11 @@ export default function ContactPage() {
|
||||
|
||||
{/* Right Side: Form */}
|
||||
<div className={styles.formWrapper}>
|
||||
{alert.show && (
|
||||
<div className={`p-4 mb-4 text-sm rounded-lg ${alert.type === "success" ? "text-green-800 bg-green-50" : "text-red-800 bg-red-50"}`} role="alert">
|
||||
{alert.message}
|
||||
</div>
|
||||
)}
|
||||
<form className={styles.contactForm} onSubmit={handleSubmit}>
|
||||
<div className={styles.formRow}>
|
||||
<div className={styles.formGroup}>
|
||||
@ -145,8 +236,8 @@ export default function ContactPage() {
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
className={styles.input}
|
||||
required
|
||||
/>
|
||||
{formErrors.email && <small className="text-red-500 text-xs mt-1">{formErrors.email}</small>}
|
||||
</div>
|
||||
<div className={styles.formGroup}>
|
||||
<label htmlFor="name" className={styles.label}>Name</label>
|
||||
@ -158,8 +249,8 @@ export default function ContactPage() {
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
className={styles.input}
|
||||
required
|
||||
/>
|
||||
{formErrors.name && <small className="text-red-500 text-xs mt-1">{formErrors.name}</small>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -168,10 +259,27 @@ export default function ContactPage() {
|
||||
<input
|
||||
type="tel"
|
||||
id="phone"
|
||||
name="phone" // Note: Added phone to state if not present, need to handle
|
||||
name="phone"
|
||||
placeholder="Phone"
|
||||
value={formData.phone}
|
||||
onChange={handleChange}
|
||||
className={styles.input}
|
||||
/>
|
||||
{formErrors.phone && <small className="text-red-500 text-xs mt-1">{formErrors.phone}</small>}
|
||||
</div>
|
||||
|
||||
<div className={styles.formGroup}>
|
||||
<label htmlFor="subject" className={styles.label}>Subject</label>
|
||||
<input
|
||||
type="text"
|
||||
id="subject"
|
||||
name="subject"
|
||||
placeholder="Subject"
|
||||
value={formData.subject}
|
||||
onChange={handleChange}
|
||||
className={styles.input}
|
||||
/>
|
||||
{formErrors.subject && <small className="text-red-500 text-xs mt-1">{formErrors.subject}</small>}
|
||||
</div>
|
||||
|
||||
<div className={styles.formGroup}>
|
||||
@ -184,12 +292,20 @@ export default function ContactPage() {
|
||||
onChange={handleChange}
|
||||
className={styles.textarea}
|
||||
rows={5}
|
||||
required
|
||||
/>
|
||||
{formErrors.message && <small className="text-red-500 text-xs mt-1">{formErrors.message}</small>}
|
||||
</div>
|
||||
|
||||
<div className={`${styles.formGroup} mb-4`}>
|
||||
<ReCAPTCHA
|
||||
sitekey="6Lea8ZYrAAAAAHaghaLjDx_K084IFATZby7Rzqhl"
|
||||
onChange={handleCaptchaChange}
|
||||
/>
|
||||
{formErrors.captcha && <small className="text-red-500 text-xs mt-1">{formErrors.captcha}</small>}
|
||||
</div>
|
||||
|
||||
<button type="submit" className={styles.submitBtn}>
|
||||
SUBMIT BUTTON
|
||||
Submit Now
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -2,8 +2,13 @@ import type { Metadata } from "next";
|
||||
import "./globals.css";
|
||||
import Navbar from "@/components/Navbar";
|
||||
import Footer from "@/components/Footer";
|
||||
import ScrollToTop from "@/components/ScrollToTop";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL("https://socialbuddy-marketing.metatronnest.com"),
|
||||
alternates: {
|
||||
canonical: "./",
|
||||
},
|
||||
title: "SocialBuddy - Manage All Your Social Media in One Place",
|
||||
description: "Empowering businesses and creators to schedule posts, analyze performance, engage with audiences, and grow their social presence with powerful tools and analytics.",
|
||||
keywords: ["social media management", "social media scheduler", "social media analytics", "content calendar", "social media tools"],
|
||||
@ -47,6 +52,7 @@ export default function RootLayout({
|
||||
<body>
|
||||
<Navbar />
|
||||
{children}
|
||||
<ScrollToTop />
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -442,7 +442,27 @@
|
||||
/* Blue active dot */
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 500px) {
|
||||
.searchBarCta {
|
||||
flex-direction: column;
|
||||
padding: 0.75rem;
|
||||
gap: 1rem;
|
||||
height: auto;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.inputGroup {
|
||||
width: 100%;
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
||||
.searchBtn {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Existing Responsive */
|
||||
@media (max-width: 1024px) {
|
||||
.contentWrapper {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
@ -4,20 +4,24 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
background: transparent;
|
||||
background: #ffffff;
|
||||
transition: all 0.3s ease;
|
||||
padding: 1rem 0;
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
|
||||
.navbar.scrolled {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: var(--shadow-md);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.navbar.mobileOpen {
|
||||
background: #ffffff;
|
||||
box-shadow: var(--shadow-md);
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
||||
@ -180,7 +184,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
padding: 2rem 0;
|
||||
padding: 2rem 0 4rem;
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
@ -191,6 +195,7 @@
|
||||
}
|
||||
|
||||
.mobileNavLink {
|
||||
display: block;
|
||||
color: var(--foreground);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
|
||||
@ -15,10 +15,19 @@ export default function Navbar() {
|
||||
setIsScrolled(window.scrollY > 20);
|
||||
};
|
||||
|
||||
handleScroll();
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isMobileMenuOpen) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
document.body.style.overflow = 'unset';
|
||||
}
|
||||
}, [isMobileMenuOpen]);
|
||||
|
||||
const navLinks = [
|
||||
{ href: '/', label: 'Home' },
|
||||
{
|
||||
@ -123,7 +132,7 @@ export default function Navbar() {
|
||||
</span>
|
||||
</div>
|
||||
{((link.label === 'Features' && isMobileFeaturesOpen) || (link.label === 'Channels' && isMobileChannelsOpen)) && (
|
||||
<div style={{ marginLeft: '1rem', borderLeft: '1px solid var(--border-color)' }}>
|
||||
<div style={{ marginLeft: '1rem', borderLeft: '1px solid var(--border-color)', display: 'flex', flexDirection: 'column' }}>
|
||||
{link.dropdown.map((subLink) => (
|
||||
<Link
|
||||
key={subLink.href}
|
||||
|
||||
45
components/ScrollToTop.module.css
Normal file
45
components/ScrollToTop.module.css
Normal file
@ -0,0 +1,45 @@
|
||||
.scrollToTop {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
right: 2rem;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: var(--gradient-primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateY(20px);
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: var(--shadow-lg), var(--shadow-glow);
|
||||
}
|
||||
|
||||
.scrollToTop.visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.scrollToTop:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: var(--shadow-xl), 0 0 20px rgba(236, 72, 153, 0.5);
|
||||
}
|
||||
|
||||
.scrollToTop:active {
|
||||
transform: translateY(0) scale(0.9);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.scrollToTop {
|
||||
bottom: 1.5rem;
|
||||
right: 1.5rem;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
}
|
||||
}
|
||||
39
components/ScrollToTop.tsx
Normal file
39
components/ScrollToTop.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { ArrowUp } from 'lucide-react';
|
||||
import styles from './ScrollToTop.module.css';
|
||||
|
||||
export default function ScrollToTop() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const toggleVisibility = () => {
|
||||
if (window.scrollY > 300) {
|
||||
setIsVisible(true);
|
||||
} else {
|
||||
setIsVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', toggleVisibility);
|
||||
return () => window.removeEventListener('scroll', toggleVisibility);
|
||||
}, []);
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`${styles.scrollToTop} ${isVisible ? styles.visible : ''}`}
|
||||
onClick={scrollToTop}
|
||||
aria-label="Scroll to top"
|
||||
>
|
||||
<ArrowUp size={24} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
446
package-lock.json
generated
446
package-lock.json
generated
@ -8,16 +8,23 @@
|
||||
"name": "socialbuddy-temp",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"framer-motion": "^12.23.26",
|
||||
"lucide-react": "^0.561.0",
|
||||
"next": "16.0.8",
|
||||
"react": "19.2.1",
|
||||
"react-dom": "19.2.1"
|
||||
"react-dom": "19.2.1",
|
||||
"react-google-recaptcha": "^3.1.0",
|
||||
"selenium-webdriver": "^4.27.0",
|
||||
"sitemap": "^8.0.0",
|
||||
"xml2js": "^0.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@types/react-google-recaptcha": "^2.1.9",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.0.8",
|
||||
"tailwindcss": "^4",
|
||||
@ -277,6 +284,12 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@bazel/runfiles": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@bazel/runfiles/-/runfiles-6.5.0.tgz",
|
||||
"integrity": "sha512-RzahvqTkfpY2jsDxo8YItPX+/iZ6hbiikw1YhE0bA9EKBR5Og8Pa6FHn9PO9M0zaXRVsr0GFQLKbB/0rzy9SzA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@emnapi/core": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz",
|
||||
@ -1550,7 +1563,6 @@
|
||||
"version": "20.19.26",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.26.tgz",
|
||||
"integrity": "sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
@ -1576,6 +1588,25 @@
|
||||
"@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/@types/sax": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz",
|
||||
"integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz",
|
||||
@ -2170,6 +2201,12 @@
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/arg": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
@ -2364,6 +2401,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",
|
||||
@ -2390,6 +2433,17 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
|
||||
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axobject-query": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
|
||||
@ -2498,7 +2552,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",
|
||||
@ -2598,6 +2651,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",
|
||||
@ -2612,6 +2677,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@ -2756,6 +2827,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",
|
||||
@ -2783,7 +2863,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",
|
||||
@ -2895,7 +2974,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"
|
||||
@ -2905,7 +2983,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"
|
||||
@ -2943,7 +3020,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"
|
||||
@ -2956,7 +3032,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",
|
||||
@ -3571,6 +3646,26 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||
"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",
|
||||
@ -3587,11 +3682,53 @@
|
||||
"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/framer-motion": {
|
||||
"version": "12.23.26",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.26.tgz",
|
||||
"integrity": "sha512-cPcIhgR42xBn1Uj+PzOyheMtZ73H927+uWPDVhUMqxy8UHt6Okavb6xIz9J/phFUHUj0OncR6UvMfJTXoc/LKA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.23.23",
|
||||
"motion-utils": "^12.23.6",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"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"
|
||||
@ -3652,7 +3789,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",
|
||||
@ -3677,7 +3813,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",
|
||||
@ -3765,7 +3900,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"
|
||||
@ -3837,7 +3971,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"
|
||||
@ -3850,7 +3983,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"
|
||||
@ -3866,7 +3998,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"
|
||||
@ -3892,6 +4023,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",
|
||||
@ -3902,6 +4042,12 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||
@ -3929,6 +4075,12 @@
|
||||
"node": ">=0.8.19"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/internal-slot": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
|
||||
@ -4405,7 +4557,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": {
|
||||
@ -4484,6 +4635,18 @@
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||
"license": "(MIT OR GPL-3.0-or-later)",
|
||||
"dependencies": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
"readable-stream": "~2.3.6",
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||
@ -4528,6 +4691,15 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss": {
|
||||
"version": "1.30.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
|
||||
@ -4816,7 +4988,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"
|
||||
@ -4858,7 +5029,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"
|
||||
@ -4888,6 +5058,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.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
@ -4911,6 +5102,21 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "12.23.23",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz",
|
||||
"integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^12.23.6"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "12.23.6",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
|
||||
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@ -5050,7 +5256,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"
|
||||
@ -5237,6 +5442,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@ -5345,11 +5556,16 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/prop-types": {
|
||||
"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",
|
||||
@ -5357,6 +5573,12 @@
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
@ -5397,6 +5619,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.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz",
|
||||
@ -5409,11 +5644,44 @@
|
||||
"react": "^19.2.1"
|
||||
}
|
||||
},
|
||||
"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-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/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream/node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/reflect.getprototypeof": {
|
||||
@ -5556,6 +5824,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/safe-push-apply": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
|
||||
@ -5591,12 +5865,43 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz",
|
||||
"integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==",
|
||||
"license": "BlueOak-1.0.0"
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
||||
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/selenium-webdriver": {
|
||||
"version": "4.39.0",
|
||||
"resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.39.0.tgz",
|
||||
"integrity": "sha512-NAs9jCU+UeZ/ZmRb8R6zOp7N8eMklefdBYASnaRmCNXdgFE8w3OCxxZmLixkwqnGDHY5VF7hCulfw1Mls43N/A==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/SeleniumHQ"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/selenium"
|
||||
}
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@bazel/runfiles": "^6.5.0",
|
||||
"jszip": "^3.10.1",
|
||||
"tmp": "^0.2.5",
|
||||
"ws": "^8.18.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
@ -5656,6 +5961,12 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
||||
@ -5813,6 +6124,31 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/sitemap": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/sitemap/-/sitemap-8.0.2.tgz",
|
||||
"integrity": "sha512-LwktpJcyZDoa0IL6KT++lQ53pbSrx2c9ge41/SeLTyqy2XUNA6uR4+P9u5IVo5lPeL2arAcOKn1aZAxoYbCKlQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "^17.0.5",
|
||||
"@types/sax": "^1.2.1",
|
||||
"arg": "^5.0.0",
|
||||
"sax": "^1.4.1"
|
||||
},
|
||||
"bin": {
|
||||
"sitemap": "dist/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0",
|
||||
"npm": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sitemap/node_modules/@types/node": {
|
||||
"version": "17.0.45",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
|
||||
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
@ -5843,6 +6179,15 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string.prototype.includes": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
|
||||
@ -6097,6 +6442,15 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/tmp": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
|
||||
"integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.14"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
@ -6307,7 +6661,6 @@
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unrs-resolver": {
|
||||
@ -6386,6 +6739,12 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
@ -6501,6 +6860,49 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xml2js": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
|
||||
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sax": ">=0.6.0",
|
||||
"xmlbuilder": "~11.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlbuilder": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
|
||||
13
package.json
13
package.json
@ -6,19 +6,28 @@
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint"
|
||||
"lint": "eslint",
|
||||
"generate-sitemap": "node scripts/generate-sitemap.js",
|
||||
"seo-test": "node scripts/seo-test-selenium.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"framer-motion": "^12.23.26",
|
||||
"lucide-react": "^0.561.0",
|
||||
"next": "16.0.8",
|
||||
"react": "19.2.1",
|
||||
"react-dom": "19.2.1"
|
||||
"react-dom": "19.2.1",
|
||||
"react-google-recaptcha": "^3.1.0",
|
||||
"selenium-webdriver": "^4.27.0",
|
||||
"sitemap": "^8.0.0",
|
||||
"xml2js": "^0.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@types/react-google-recaptcha": "^2.1.9",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.0.8",
|
||||
"tailwindcss": "^4",
|
||||
|
||||
1
public/sitemap.xml
Normal file
1
public/sitemap.xml
Normal file
File diff suppressed because one or more lines are too long
124
scripts/generate-sitemap.js
Normal file
124
scripts/generate-sitemap.js
Normal file
@ -0,0 +1,124 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { SitemapStream, streamToPromise } = require("sitemap");
|
||||
|
||||
const hostname = "https://socialbuddy-marketing.metatronnest.com/";
|
||||
const addTrailingSlash = true;
|
||||
|
||||
// Utility to determine if a trailing slash should be added
|
||||
const shouldAddSlash = (url) => {
|
||||
if (url === "/") return false;
|
||||
if (/\.[a-z0-9]{2,6}(\?.*)?$/i.test(url)) return false; // skip files
|
||||
return true;
|
||||
};
|
||||
|
||||
// Format URL to ensure proper slashes and handle anchors
|
||||
const formatUrl = (url) => {
|
||||
// Split path and hash
|
||||
let [pathPart, hashPart] = url.split("#");
|
||||
|
||||
// Remove extra leading slashes and ensure single starting slash
|
||||
pathPart = "/" + pathPart.replace(/^\/+/, "");
|
||||
|
||||
// Add or remove trailing slash for pathPart
|
||||
if (addTrailingSlash && shouldAddSlash(pathPart) && !pathPart.endsWith("/")) {
|
||||
pathPart += "/";
|
||||
}
|
||||
if (!addTrailingSlash && pathPart.endsWith("/") && pathPart !== "/") {
|
||||
pathPart = pathPart.slice(0, -1);
|
||||
}
|
||||
|
||||
// Recombine with hash if it exists
|
||||
return hashPart ? pathPart + "#" + hashPart : pathPart;
|
||||
};
|
||||
|
||||
// List of URLs to include in sitemap
|
||||
const sectionLinks = [
|
||||
{ url: "/", changefreq: "daily", priority: 1.0 },
|
||||
{ url: "/about", changefreq: "weekly", priority: 0.8 },
|
||||
{ url: "/pricing", changefreq: "weekly", priority: 0.8 },
|
||||
{ url: "/contact", changefreq: "weekly", priority: 0.7 },
|
||||
{ url: "/careers", changefreq: "weekly", priority: 0.7 },
|
||||
{ url: "/resources", changefreq: "weekly", priority: 0.7 },
|
||||
];
|
||||
|
||||
// Features pages
|
||||
const featureLinks = [
|
||||
{ url: "/features/publish", changefreq: "weekly", priority: 0.7 },
|
||||
{ url: "/features/analyze", changefreq: "weekly", priority: 0.7 },
|
||||
{ url: "/features/engage", changefreq: "weekly", priority: 0.7 },
|
||||
{ url: "/features/create", changefreq: "weekly", priority: 0.7 },
|
||||
{ url: "/features/start-page", changefreq: "weekly", priority: 0.7 },
|
||||
{ url: "/features/ai-assistant", changefreq: "weekly", priority: 0.7 },
|
||||
];
|
||||
|
||||
// Channel pages
|
||||
const channelLinks = [
|
||||
{ url: "/channels/instagram", changefreq: "weekly", priority: 0.7 },
|
||||
{ url: "/channels/facebook", changefreq: "weekly", priority: 0.7 },
|
||||
{ url: "/channels/twitter", changefreq: "weekly", priority: 0.7 },
|
||||
{ url: "/channels/linkedin", changefreq: "weekly", priority: 0.7 },
|
||||
{ url: "/channels/tiktok", changefreq: "weekly", priority: 0.7 },
|
||||
{ url: "/channels/pinterest", changefreq: "weekly", priority: 0.7 },
|
||||
|
||||
];
|
||||
|
||||
// Resources pages
|
||||
const resourceLinks = [
|
||||
{ url: "/resources/mastering-social-media-scheduling", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/best-times-to-post-instagram", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/content-calendar-templates", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/understanding-social-metrics", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/roi-social-media-marketing", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/creating-custom-reports", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/community-management-best-practices", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/managing-negative-comments", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/social-listening-guide", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/design-tips-non-designers", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/video-content-trends", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/writing-engaging-captions", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/optimizing-link-in-bio", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/landing-page-conversion", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/personal-branding-tips", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/ai-for-content-creation", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/future-of-marketing-ai", changefreq: "weekly", priority: 0.6 },
|
||||
{ url: "/resources/humanizing-ai-content", changefreq: "weekly", priority: 0.6 },
|
||||
];
|
||||
|
||||
// Format all URLs
|
||||
const allLinks = [
|
||||
...sectionLinks,
|
||||
...featureLinks,
|
||||
...channelLinks,
|
||||
...resourceLinks,
|
||||
].map((link) => ({
|
||||
...link,
|
||||
url: formatUrl(link.url),
|
||||
}));
|
||||
|
||||
// Generate sitemap.xml
|
||||
async function generateSitemap() {
|
||||
try {
|
||||
const sitemap = new SitemapStream({ hostname });
|
||||
const writeStream = fs.createWriteStream(
|
||||
path.resolve(__dirname, "../public/sitemap.xml")
|
||||
);
|
||||
|
||||
sitemap.pipe(writeStream);
|
||||
|
||||
console.log("📦 Writing URLs to sitemap:");
|
||||
allLinks.forEach((link) => {
|
||||
console.log(" -", hostname + link.url);
|
||||
sitemap.write(link);
|
||||
});
|
||||
|
||||
sitemap.end();
|
||||
await streamToPromise(sitemap);
|
||||
|
||||
console.log("✅ sitemap.xml created successfully!");
|
||||
} catch (error) {
|
||||
console.error("❌ Error creating sitemap.xml:", error);
|
||||
}
|
||||
}
|
||||
|
||||
generateSitemap();
|
||||
69
scripts/image_alt_issues.csv
Normal file
69
scripts/image_alt_issues.csv
Normal file
@ -0,0 +1,69 @@
|
||||
Page URL,Image Src,Alt Text,Issue Type
|
||||
"http://localhost:3000/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/","","Unified Message Center","Duplicate Alt (3 times)"
|
||||
"http://localhost:3000/","","User","Duplicate Alt (9 times)"
|
||||
"http://localhost:3000/about/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/pricing/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/contact/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/careers/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/features/publish/","http://localhost:3000/images/shape-left.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/features/publish/","http://localhost:3000/images/shape-right.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/features/publish/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/features/analyze/","http://localhost:3000/images/shape-left.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/features/analyze/","http://localhost:3000/images/shape-right.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/features/analyze/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/features/engage/","http://localhost:3000/images/shape-left.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/features/engage/","http://localhost:3000/images/shape-right.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/features/engage/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/features/create/","http://localhost:3000/images/shape-left.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/features/create/","http://localhost:3000/images/shape-right.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/features/create/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/features/start-page/","http://localhost:3000/images/shape-left.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/features/start-page/","http://localhost:3000/images/shape-right.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/features/start-page/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/features/ai-assistant/","http://localhost:3000/images/shape-left.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/features/ai-assistant/","http://localhost:3000/images/shape-right.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/features/ai-assistant/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/channels/instagram/","http://localhost:3000/images/shape-left.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/channels/instagram/","http://localhost:3000/images/shape-right.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/channels/instagram/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/channels/instagram/","","Chloe Adams","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/channels/facebook/","http://localhost:3000/images/shape-left.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/channels/facebook/","http://localhost:3000/images/shape-right.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/channels/facebook/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/channels/facebook/","","Mark Stephens","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/channels/twitter/","http://localhost:3000/images/shape-left.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/channels/twitter/","http://localhost:3000/images/shape-right.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/channels/twitter/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/channels/twitter/","","Elon M.","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/channels/linkedin/","http://localhost:3000/images/shape-left.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/channels/linkedin/","http://localhost:3000/images/shape-right.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/channels/linkedin/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/channels/linkedin/","","Brian K.","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/channels/tiktok/","http://localhost:3000/images/shape-left.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/channels/tiktok/","http://localhost:3000/images/shape-right.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/channels/tiktok/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/channels/tiktok/","","Sarah J.","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/channels/pinterest/","http://localhost:3000/images/shape-left.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/channels/pinterest/","http://localhost:3000/images/shape-right.png","(empty)","Empty Alt"
|
||||
"http://localhost:3000/channels/pinterest/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/channels/pinterest/","","Martha S.","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/mastering-social-media-scheduling/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/best-times-to-post-instagram/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/content-calendar-templates/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/understanding-social-metrics/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/roi-social-media-marketing/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/creating-custom-reports/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/community-management-best-practices/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/managing-negative-comments/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/social-listening-guide/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/design-tips-non-designers/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/video-content-trends/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/writing-engaging-captions/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/optimizing-link-in-bio/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/landing-page-conversion/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/personal-branding-tips/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/ai-for-content-creation/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/future-of-marketing-ai/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
"http://localhost:3000/resources/humanizing-ai-content/","","SocialBuddy Logo","Duplicate Alt (2 times)"
|
||||
|
261
scripts/seo-test-selenium.js
Normal file
261
scripts/seo-test-selenium.js
Normal file
@ -0,0 +1,261 @@
|
||||
// 🚀 Full SEO + Broken Link + 404 + Accessibility + Image Alt CSV Export
|
||||
// Run with: node seo-test-selenium.js
|
||||
|
||||
const { Builder, By } = require("selenium-webdriver");
|
||||
const chrome = require("selenium-webdriver/chrome");
|
||||
const axios = require("axios");
|
||||
const xml2js = require("xml2js");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
// CSV file for Image Alt issues
|
||||
const csvPath = path.join(__dirname, "image_alt_issues.csv");
|
||||
fs.writeFileSync(csvPath, "Page URL,Image Src,Alt Text,Issue Type\n", "utf8");
|
||||
|
||||
// ==========================
|
||||
// 1️⃣ Fetch URLs from sitemap.xml
|
||||
// ==========================
|
||||
async function getUrlsFromSitemap(sitemapUrl) {
|
||||
try {
|
||||
const res = await axios.get(sitemapUrl);
|
||||
const parsed = await xml2js.parseStringPromise(res.data);
|
||||
return parsed.urlset.url.map((u) => u.loc[0]);
|
||||
} catch (err) {
|
||||
console.error("❌ Failed to load sitemap:", err.message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// 2️⃣ Check HTTP Status
|
||||
// ==========================
|
||||
async function checkLinkStatus(url) {
|
||||
try {
|
||||
const res = await axios.get(url, {
|
||||
timeout: 10000,
|
||||
validateStatus: () => true,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data.toLowerCase().includes("page not found")) {
|
||||
return "Soft 404";
|
||||
}
|
||||
|
||||
return res.status;
|
||||
} catch (err) {
|
||||
return err.response ? err.response.status : "❌ No Response";
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// 3️⃣ Main SEO + Accessibility + Image Alt Audit
|
||||
// ==========================
|
||||
async function checkSEO(url, siteDomain) {
|
||||
const options = new chrome.Options();
|
||||
options.addArguments("--headless", "--no-sandbox", "--disable-gpu");
|
||||
|
||||
const driver = await new Builder()
|
||||
.forBrowser("chrome")
|
||||
.setChromeOptions(options)
|
||||
.build();
|
||||
|
||||
try {
|
||||
const pageStatus = await checkLinkStatus(url);
|
||||
if (pageStatus === 404 || pageStatus === "Soft 404") {
|
||||
console.log(`\n🚫 ${url} → ❌ Page not found (${pageStatus})`);
|
||||
return;
|
||||
}
|
||||
|
||||
await driver.get(url);
|
||||
const pageSource = await driver.getPageSource();
|
||||
|
||||
// Basic SEO Elements
|
||||
const title = await driver.getTitle();
|
||||
const descElem = await driver.findElements(By.css('meta[name="description"]'));
|
||||
const canonicalElem = await driver.findElements(By.css('link[rel="canonical"]'));
|
||||
const robotsElem = await driver.findElements(By.css('meta[name="robots"]'));
|
||||
const viewportElem = await driver.findElements(By.css('meta[name="viewport"]'));
|
||||
const charset = await driver.findElements(By.css('meta[charset]'));
|
||||
const htmlTag = await driver.findElement(By.css("html"));
|
||||
const langAttr = await htmlTag.getAttribute("lang").catch(() => "");
|
||||
const h1Tags = await driver.findElements(By.css("h1"));
|
||||
const h2Tags = await driver.findElements(By.css("h2"));
|
||||
|
||||
// Meta Description
|
||||
let descContent = descElem.length > 0 ? await descElem[0].getAttribute("content") : "";
|
||||
const descLength = descContent.length;
|
||||
const descStatus =
|
||||
descLength === 0
|
||||
? "❌ Missing"
|
||||
: descLength < 50
|
||||
? `⚠️ Too short (${descLength})`
|
||||
: descLength > 160
|
||||
? `⚠️ Too long (${descLength})`
|
||||
: "✅ Perfect";
|
||||
|
||||
// Title length check
|
||||
const titleLength = title.length;
|
||||
const titleStatus =
|
||||
titleLength === 0
|
||||
? "❌ Missing"
|
||||
: titleLength < 30
|
||||
? `⚠️ Too short (${titleLength})`
|
||||
: titleLength > 65
|
||||
? `⚠️ Too long (${titleLength})`
|
||||
: "✅ Perfect";
|
||||
|
||||
// Canonical
|
||||
const canonicalURL =
|
||||
canonicalElem.length > 0 ? await canonicalElem[0].getAttribute("href") : "❌ Missing";
|
||||
|
||||
// 🖼️ Image Accessibility Audit
|
||||
const imgs = await driver.findElements(By.css("img"));
|
||||
let missingAlt = 0;
|
||||
let emptyAlt = 0;
|
||||
let duplicateAlt = [];
|
||||
const altTextMap = new Map();
|
||||
|
||||
for (const img of imgs) {
|
||||
const src = await img.getAttribute("src");
|
||||
const alt = (await img.getAttribute("alt"))?.trim() ?? null;
|
||||
|
||||
if (alt === null) {
|
||||
missingAlt++;
|
||||
fs.appendFileSync(csvPath, `"${url}","${src}","","Missing Alt"\n`, "utf8");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (alt === "") {
|
||||
emptyAlt++;
|
||||
fs.appendFileSync(csvPath, `"${url}","${src}","(empty)","Empty Alt"\n`, "utf8");
|
||||
}
|
||||
|
||||
if (altTextMap.has(alt)) {
|
||||
altTextMap.set(alt, altTextMap.get(alt) + 1);
|
||||
} else {
|
||||
altTextMap.set(alt, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [altText, count] of altTextMap.entries()) {
|
||||
if (altText && count > 1) {
|
||||
duplicateAlt.push({ altText, count });
|
||||
fs.appendFileSync(
|
||||
csvPath,
|
||||
`"${url}","","${altText}","Duplicate Alt (${count} times)"\n`,
|
||||
"utf8"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Detect tracking & schema tags
|
||||
const hasGTM = pageSource.includes("googletagmanager.com/gtm.js");
|
||||
const hasClarity = pageSource.includes("clarity.ms/tag");
|
||||
const hasFBPixel = pageSource.includes("fbevents.js") || pageSource.includes("fbq(");
|
||||
const hasAnalytics = pageSource.includes("www.googletagmanager.com/gtag/js");
|
||||
|
||||
const ogTags = await driver.findElements(By.css("meta[property^='og:']"));
|
||||
const twitterTags = await driver.findElements(By.css("meta[name^='twitter:']"));
|
||||
const schemaScripts = await driver.findElements(By.css('script[type="application/ld+json"]'));
|
||||
|
||||
// Links check
|
||||
const anchorTags = await driver.findElements(By.css("a[href]"));
|
||||
const brokenLinks = [];
|
||||
for (const a of anchorTags) {
|
||||
const href = await a.getAttribute("href");
|
||||
if (!href || href.startsWith("#") || href.startsWith("mailto:")) continue;
|
||||
|
||||
const fullUrl = href.startsWith("http")
|
||||
? href
|
||||
: `${siteDomain}${href.startsWith("/") ? href : `/${href}`}`;
|
||||
|
||||
if (fullUrl.includes(siteDomain)) {
|
||||
const status = await checkLinkStatus(fullUrl);
|
||||
if (status === 404 || status === "Soft 404" || status === "❌ No Response") {
|
||||
brokenLinks.push({ link: fullUrl, status });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lazy loading check
|
||||
const images = await driver.findElements(By.css("img, video, iframe"));
|
||||
const lazyLoadCount = await Promise.all(
|
||||
images.map(async (img) => {
|
||||
const loading = await img.getAttribute("loading");
|
||||
return loading === "lazy";
|
||||
})
|
||||
);
|
||||
const lazyLoaded = lazyLoadCount.filter((v) => v).length;
|
||||
|
||||
// Console Summary
|
||||
console.log(`\n🔍 Checking: ${url}`);
|
||||
console.log("-------------------------------------------");
|
||||
console.log("Title:", titleStatus);
|
||||
console.log("Meta Description:", descStatus);
|
||||
console.log("Canonical URL:", canonicalURL);
|
||||
console.log("Meta Robots:", robotsElem.length > 0 ? "✅ Found" : "⚠️ Missing");
|
||||
console.log("Viewport:", viewportElem.length > 0 ? "✅ Found" : "⚠️ Missing");
|
||||
console.log("Charset:", charset.length > 0 ? "✅ Found" : "❌ Missing");
|
||||
console.log("HTML lang:", langAttr ? `✅ ${langAttr}` : "⚠️ Missing");
|
||||
console.log("H1 Tags:", h1Tags.length > 0 ? `✅ ${h1Tags.length}` : "❌ Missing");
|
||||
console.log("H2 Tags:", h2Tags.length > 0 ? `ℹ️ ${h2Tags.length}` : "⚠️ None");
|
||||
console.log("Images:", imgs.length);
|
||||
console.log(
|
||||
"Missing Alt:",
|
||||
missingAlt > 0 ? `❌ ${missingAlt}` : "✅ None"
|
||||
);
|
||||
console.log(
|
||||
"Empty Alt:",
|
||||
emptyAlt > 0 ? `⚠️ ${emptyAlt}` : "✅ None"
|
||||
);
|
||||
console.log(
|
||||
"Duplicate Alt:",
|
||||
duplicateAlt.length > 0 ? `⚠️ ${duplicateAlt.length}` : "✅ None"
|
||||
);
|
||||
console.log("Lazy Loaded Images:", lazyLoaded > 0 ? `✅ ${lazyLoaded}` : "⚠️ None");
|
||||
console.log("Open Graph Tags:", ogTags.length > 0 ? "✅ Found" : "⚠️ Missing");
|
||||
console.log("Twitter Tags:", twitterTags.length > 0 ? "✅ Found" : "⚠️ Missing");
|
||||
console.log("Schema Markup:", schemaScripts.length > 0 ? "✅ Found" : "⚠️ Missing");
|
||||
console.log("Google Analytics:", hasAnalytics ? "✅ Found" : "⚠️ Missing");
|
||||
console.log("GTM:", hasGTM ? "✅ Found" : "⚠️ Missing");
|
||||
console.log("Clarity:", hasClarity ? "✅ Found" : "⚠️ Missing");
|
||||
console.log("Facebook Pixel:", hasFBPixel ? "✅ Found" : "⚠️ Missing");
|
||||
|
||||
if (brokenLinks.length > 0) {
|
||||
console.log("\n❌ Broken Links:");
|
||||
brokenLinks.forEach((b) => console.log(` → ${b.link} [${b.status}]`));
|
||||
} else {
|
||||
console.log("✅ No broken links found.");
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error(`❌ Error on ${url}:`, err.message);
|
||||
} finally {
|
||||
await driver.quit();
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// 4️⃣ Run Full Site Audit
|
||||
// ==========================
|
||||
(async () => {
|
||||
const sitemapUrl = "http://localhost:3000/sitemap.xml"; // your local sitemap
|
||||
const siteDomain = "https://socialbuddy-marketing.metatronnest.com"; // your production domain
|
||||
|
||||
console.log("📄 Fetching URLs from sitemap...");
|
||||
const urls = await getUrlsFromSitemap(sitemapUrl);
|
||||
|
||||
if (urls.length === 0) {
|
||||
console.error("❌ No URLs found in sitemap.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`✅ Found ${urls.length} URLs in sitemap.`);
|
||||
console.log("🚀 Starting Full SEO + Accessibility + Broken Link Audit...");
|
||||
|
||||
for (const url of urls) {
|
||||
await checkSEO(url, siteDomain);
|
||||
}
|
||||
|
||||
console.log("\n✅ Full SEO Audit Completed!");
|
||||
console.log(`📁 CSV Report: ${csvPath}`);
|
||||
})();
|
||||
Loading…
x
Reference in New Issue
Block a user