-
Project Initialization
- Initialize Next.js 14 with TypeScript
- Configure TailwindCSS and ShadCN UI
- Set up ESLint, Prettier, and Husky
- Configure environment variables
-
Core Infrastructure
- Create base layout components
- Set up routing structure
- Configure analytics and tracking
- Implement security headers
-
Design System
- Set up color palette and typography
- Create base UI components
- Implement responsive breakpoints
- Configure animation system
-
Homepage Implementation
- Hero section with domain search
- Services overview
- Trust signals and testimonials
- Call-to-action sections
-
Services Pages
- Individual service pages
- Pricing integration
- FAQ sections
- Contact forms
-
Supporting Pages
- About page
- Contact page
- Legal pages
- Blog structure
-
Integrations
- WHMCS integration
- CRM and email automation
- Analytics implementation
- Live chat integration
-
Performance Optimization
- Image optimization
- Code splitting
- Caching strategies
- Core Web Vitals optimization
-
SEO Implementation
- Meta tag generation
- Schema markup
- Sitemap generation
- Content optimization
// src/components/layout/header.tsx
import { useState } from 'react';
import { Menu, X, Shield, Server } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { MobileMenu } from './mobile-menu';
interface HeaderProps {
className?: string;
}
export function Header({ className }: HeaderProps) {
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
<header className={`sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 ${className}`}>
<div className="container flex h-16 items-center justify-between">
{/* Logo */}
<div className="flex items-center space-x-2">
<Shield className="h-8 w-8 text-primary" />
<span className="text-xl font-bold">Securiace</span>
</div>
{/* Desktop Navigation */}
<nav className="hidden md:flex items-center space-x-6">
<a href="/services" className="text-sm font-medium hover:text-primary transition-colors">
Services
</a>
<a href="/pricing" className="text-sm font-medium hover:text-primary transition-colors">
Pricing
</a>
<a href="/about" className="text-sm font-medium hover:text-primary transition-colors">
About
</a>
<a href="/blog" className="text-sm font-medium hover:text-primary transition-colors">
Blog
</a>
<a href="/support" className="text-sm font-medium hover:text-primary transition-colors">
Support
</a>
</nav>
{/* CTA Buttons */}
<div className="hidden md:flex items-center space-x-4">
<Button variant="ghost" size="sm">
Sign In
</Button>
<Button size="sm">
Get Started
</Button>
</div>
{/* Mobile Menu Button */}
<Button
variant="ghost"
size="sm"
className="md:hidden"
onClick={() => setIsMenuOpen(!isMenuOpen)}
>
{isMenuOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
</Button>
</div>
{/* Mobile Menu */}
{isMenuOpen && <MobileMenu onClose={() => setIsMenuOpen(false)} />}
</header>
);
}// src/components/layout/footer.tsx
import { Shield, Mail, Phone, MapPin } from 'lucide-react';
import { Button } from '@/components/ui/button';
export function Footer() {
return (
<footer className="bg-gray-900 text-white">
<div className="container py-12">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
{/* Company Info */}
<div className="space-y-4">
<div className="flex items-center space-x-2">
<Shield className="h-8 w-8 text-primary" />
<span className="text-xl font-bold">Securiace</span>
</div>
<p className="text-gray-300 text-sm">
Secure, Reliable, Managed Hosting & IT Solutions for businesses worldwide.
</p>
<div className="flex space-x-4">
{/* Social Media Links */}
</div>
</div>
{/* Services */}
<div>
<h3 className="font-semibold mb-4">Services</h3>
<ul className="space-y-2 text-sm text-gray-300">
<li><a href="/services/shared-hosting" className="hover:text-white transition-colors">Shared Hosting</a></li>
<li><a href="/services/cloud-hosting" className="hover:text-white transition-colors">Cloud Hosting</a></li>
<li><a href="/services/managed-vps" className="hover:text-white transition-colors">Managed VPS</a></li>
<li><a href="/services/cybersecurity" className="hover:text-white transition-colors">Cybersecurity</a></li>
</ul>
</div>
{/* Support */}
<div>
<h3 className="font-semibold mb-4">Support</h3>
<ul className="space-y-2 text-sm text-gray-300">
<li><a href="/support" className="hover:text-white transition-colors">Help Center</a></li>
<li><a href="/support/contact" className="hover:text-white transition-colors">Contact Us</a></li>
<li><a href="/trust/status" className="hover:text-white transition-colors">Status Page</a></li>
<li><a href="/blog" className="hover:text-white transition-colors">Blog</a></li>
</ul>
</div>
{/* Contact */}
<div>
<h3 className="font-semibold mb-4">Contact</h3>
<div className="space-y-2 text-sm text-gray-300">
<div className="flex items-center space-x-2">
<Mail className="h-4 w-4" />
<span>support@securiace.com</span>
</div>
<div className="flex items-center space-x-2">
<Phone className="h-4 w-4" />
<span>+91-XXXXXXXXXX</span>
</div>
<div className="flex items-center space-x-2">
<MapPin className="h-4 w-4" />
<span>Mumbai, India</span>
</div>
</div>
</div>
</div>
<div className="border-t border-gray-800 mt-8 pt-8 flex flex-col md:flex-row justify-between items-center">
<p className="text-sm text-gray-400">
© 2024 Securiace Technologies. All rights reserved.
</p>
<div className="flex space-x-6 mt-4 md:mt-0">
<a href="/legal/privacy-policy" className="text-sm text-gray-400 hover:text-white transition-colors">
Privacy Policy
</a>
<a href="/legal/terms-of-service" className="text-sm text-gray-400 hover:text-white transition-colors">
Terms of Service
</a>
</div>
</div>
</div>
</footer>
);
}// src/components/sections/hero/hero-section.tsx
import { motion } from 'framer-motion';
import { Button } from '@/components/ui/button';
import { DomainSearch } from './domain-search';
import { TrustSignals } from './trust-signals';
import { CounterAnimation } from '@/components/animations/counter-animation';
const fadeInUp = {
initial: { opacity: 0, y: 60 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.6, ease: "easeOut" }
};
const staggerContainer = {
animate: {
transition: {
staggerChildren: 0.1
}
}
};
export function HeroSection() {
return (
<section className="relative min-h-screen flex items-center justify-center bg-gradient-to-br from-primary-50 to-secondary-50">
<div className="container px-4 py-20">
<motion.div
variants={staggerContainer}
initial="initial"
animate="animate"
className="text-center max-w-4xl mx-auto"
>
{/* Main Headline */}
<motion.h1
variants={fadeInUp}
className="text-4xl md:text-6xl font-bold text-gray-900 mb-6"
>
Secure, Reliable, Managed
<span className="text-primary block">Hosting & IT Solutions</span>
</motion.h1>
{/* Subheadline */}
<motion.p
variants={fadeInUp}
className="text-xl text-gray-600 mb-8 max-w-2xl mx-auto"
>
Enterprise-grade hosting with 99.9% uptime guarantee, advanced security,
and 24/7 expert support. Trusted by businesses worldwide.
</motion.p>
{/* Domain Search */}
<motion.div variants={fadeInUp} className="mb-8">
<DomainSearch />
</motion.div>
{/* CTA Buttons */}
<motion.div
variants={fadeInUp}
className="flex flex-col sm:flex-row gap-4 justify-center mb-12"
>
<Button size="lg" className="text-lg px-8 py-6">
Start Hosting Now
</Button>
<Button variant="outline" size="lg" className="text-lg px-8 py-6">
Get Free SSL
</Button>
</motion.div>
{/* Trust Signals */}
<motion.div variants={fadeInUp}>
<TrustSignals />
</motion.div>
{/* Animated Counters */}
<motion.div
variants={fadeInUp}
className="grid grid-cols-1 md:grid-cols-3 gap-8 mt-16"
>
<div className="text-center">
<CounterAnimation
end={99.9}
duration={2000}
suffix="%"
className="text-4xl font-bold text-primary"
/>
<p className="text-gray-600 mt-2">Uptime Guarantee</p>
</div>
<div className="text-center">
<CounterAnimation
end={10000}
duration={2000}
suffix="+"
className="text-4xl font-bold text-primary"
/>
<p className="text-gray-600 mt-2">Happy Clients</p>
</div>
<div className="text-center">
<CounterAnimation
end={5}
duration={2000}
suffix="+"
className="text-4xl font-bold text-primary"
/>
<p className="text-gray-600 mt-2">Years Experience</p>
</div>
</motion.div>
</motion.div>
</div>
</section>
);
}// src/components/sections/services/service-card.tsx
import { motion } from 'framer-motion';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Check, ArrowRight } from 'lucide-react';
interface ServiceCardProps {
service: {
id: string;
name: string;
description: string;
features: string[];
price: {
monthly: number;
annually: number;
};
popular?: boolean;
cta: string;
};
index: number;
}
const cardVariants = {
initial: { opacity: 0, y: 60 },
animate: { opacity: 1, y: 0 },
hover: { y: -8, transition: { duration: 0.2 } }
};
export function ServiceCard({ service, index }: ServiceCardProps) {
return (
<motion.div
variants={cardVariants}
initial="initial"
whileInView="animate"
whileHover="hover"
viewport={{ once: true, margin: "-100px" }}
transition={{ delay: index * 0.1 }}
>
<Card className={`relative h-full ${service.popular ? 'border-primary shadow-lg' : ''}`}>
{service.popular && (
<Badge className="absolute -top-3 left-1/2 transform -translate-x-1/2 bg-primary text-white">
Most Popular
</Badge>
)}
<CardHeader className="text-center pb-4">
<CardTitle className="text-2xl font-bold">{service.name}</CardTitle>
<CardDescription className="text-gray-600 mt-2">
{service.description}
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Pricing */}
<div className="text-center">
<div className="text-4xl font-bold text-primary">
₹{service.price.monthly}
<span className="text-lg text-gray-500">/month</span>
</div>
<p className="text-sm text-gray-500 mt-1">
Billed annually (₹{service.price.annually}/year)
</p>
</div>
{/* Features */}
<ul className="space-y-3">
{service.features.map((feature, featureIndex) => (
<li key={featureIndex} className="flex items-center space-x-3">
<Check className="h-5 w-5 text-green-500 flex-shrink-0" />
<span className="text-gray-700">{feature}</span>
</li>
))}
</ul>
{/* CTA Button */}
<Button
className={`w-full ${service.popular ? 'bg-primary hover:bg-primary/90' : ''}`}
variant={service.popular ? 'default' : 'outline'}
>
{service.cta}
<ArrowRight className="ml-2 h-4 w-4" />
</Button>
</CardContent>
</Card>
</motion.div>
);
}// src/components/forms/contact-form.tsx
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { toast } from '@/components/ui/use-toast';
import { Loader2, Send, CheckCircle } from 'lucide-react';
const contactSchema = z.object({
name: z.string().min(2, 'Name must be at least 2 characters'),
email: z.string().email('Invalid email address'),
phone: z.string().optional(),
company: z.string().optional(),
service: z.string().min(1, 'Please select a service'),
message: z.string().min(10, 'Message must be at least 10 characters'),
});
type ContactFormData = z.infer<typeof contactSchema>;
const services = [
'Shared Hosting',
'Cloud Hosting',
'Managed VPS',
'Dedicated Servers',
'Cybersecurity',
'MSP Services',
'Other'
];
export function ContactForm() {
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSubmitted, setIsSubmitted] = useState(false);
const form = useForm<ContactFormData>({
resolver: zodResolver(contactSchema),
defaultValues: {
name: '',
email: '',
phone: '',
company: '',
service: '',
message: '',
},
});
const onSubmit = async (data: ContactFormData) => {
setIsSubmitting(true);
try {
const response = await fetch('/api/contact', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (response.ok) {
setIsSubmitted(true);
toast({
title: "Message sent successfully!",
description: "We'll get back to you within 24 hours.",
});
form.reset();
} else {
throw new Error('Failed to send message');
}
} catch (error) {
toast({
title: "Error",
description: "Failed to send message. Please try again.",
variant: "destructive",
});
} finally {
setIsSubmitting(false);
}
};
if (isSubmitted) {
return (
<Card className="max-w-md mx-auto">
<CardContent className="pt-6 text-center">
<CheckCircle className="h-16 w-16 text-green-500 mx-auto mb-4" />
<h3 className="text-xl font-semibold mb-2">Thank You!</h3>
<p className="text-gray-600">
Your message has been sent successfully. We'll get back to you within 24 hours.
</p>
</CardContent>
</Card>
);
}
return (
<Card className="max-w-2xl mx-auto">
<CardHeader>
<CardTitle>Get in Touch</CardTitle>
<CardDescription>
Ready to secure your business? Send us a message and we'll get back to you.
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label htmlFor="name" className="block text-sm font-medium mb-2">
Name *
</label>
<Input
id="name"
{...form.register('name')}
placeholder="Your full name"
className={form.formState.errors.name ? 'border-red-500' : ''}
/>
{form.formState.errors.name && (
<p className="text-red-500 text-sm mt-1">
{form.formState.errors.name.message}
</p>
)}
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium mb-2">
Email *
</label>
<Input
id="email"
type="email"
{...form.register('email')}
placeholder="your@email.com"
className={form.formState.errors.email ? 'border-red-500' : ''}
/>
{form.formState.errors.email && (
<p className="text-red-500 text-sm mt-1">
{form.formState.errors.email.message}
</p>
)}
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label htmlFor="phone" className="block text-sm font-medium mb-2">
Phone
</label>
<Input
id="phone"
{...form.register('phone')}
placeholder="+91-XXXXXXXXXX"
/>
</div>
<div>
<label htmlFor="company" className="block text-sm font-medium mb-2">
Company
</label>
<Input
id="company"
{...form.register('company')}
placeholder="Your company name"
/>
</div>
</div>
<div>
<label htmlFor="service" className="block text-sm font-medium mb-2">
Service Interest *
</label>
<select
id="service"
{...form.register('service')}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
>
<option value="">Select a service</option>
{services.map((service) => (
<option key={service} value={service}>
{service}
</option>
))}
</select>
{form.formState.errors.service && (
<p className="text-red-500 text-sm mt-1">
{form.formState.errors.service.message}
</p>
)}
</div>
<div>
<label htmlFor="message" className="block text-sm font-medium mb-2">
Message *
</label>
<Textarea
id="message"
{...form.register('message')}
placeholder="Tell us about your requirements..."
rows={4}
className={form.formState.errors.message ? 'border-red-500' : ''}
/>
{form.formState.errors.message && (
<p className="text-red-500 text-sm mt-1">
{form.formState.errors.message.message}
</p>
)}
</div>
<Button
type="submit"
disabled={isSubmitting}
className="w-full"
>
{isSubmitting ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Sending...
</>
) : (
<>
<Send className="mr-2 h-4 w-4" />
Send Message
</>
)}
</Button>
</form>
</CardContent>
</Card>
);
}// src/app/api/contact/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
import { sendEmail } from '@/lib/email';
import { createContact } from '@/lib/twenty-crm';
import { subscribeToListmonk } from '@/lib/listmonk';
const contactSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
phone: z.string().optional(),
company: z.string().optional(),
service: z.string().min(1),
message: z.string().min(10),
});
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const validatedData = contactSchema.parse(body);
// Send email notification
await sendEmail({
to: 'support@securiace.com',
subject: `New Contact Form Submission from ${validatedData.name}`,
template: 'contact-notification',
data: validatedData,
});
// Create contact in CRM
await createContact({
name: validatedData.name,
email: validatedData.email,
phone: validatedData.phone,
company: validatedData.company,
message: validatedData.message,
source: 'website',
tags: [
'contact-form',
validatedData.service.toLowerCase().replace(' ', '-'),
],
});
// Subscribe to email list
await subscribeToListmonk({
email: validatedData.email,
name: validatedData.name,
lists: [1], // General newsletter list
attributes: {
company: validatedData.company,
phone: validatedData.phone,
service_interest: validatedData.service,
source: 'contact-form',
},
});
return NextResponse.json(
{ message: 'Contact form submitted successfully' },
{ status: 200 }
);
} catch (error) {
console.error('Contact form error:', error);
if (error instanceof z.ZodError) {
return NextResponse.json(
{ error: 'Invalid form data', details: error.errors },
{ status: 400 }
);
}
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}// src/lib/animations.ts
export const fadeInUp = {
initial: { opacity: 0, y: 60 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.6, ease: 'easeOut' },
};
export const fadeInLeft = {
initial: { opacity: 0, x: -60 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.6, ease: 'easeOut' },
};
export const fadeInRight = {
initial: { opacity: 0, x: 60 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.6, ease: 'easeOut' },
};
export const scaleIn = {
initial: { scale: 0.8, opacity: 0 },
animate: { scale: 1, opacity: 1 },
transition: { duration: 0.4, ease: 'easeOut' },
};
export const staggerContainer = {
animate: {
transition: {
staggerChildren: 0.1,
},
},
};
export const slideInFromBottom = {
initial: { opacity: 0, y: 100 },
whileInView: { opacity: 1, y: 0 },
transition: { duration: 0.8, ease: 'easeOut' },
viewport: { once: true, margin: '-100px' },
};// src/components/animations/counter-animation.tsx
import { useEffect, useState } from 'react';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
interface CounterAnimationProps {
end: number;
duration?: number;
suffix?: string;
prefix?: string;
className?: string;
}
export function CounterAnimation({
end,
duration = 2000,
suffix = '',
prefix = '',
className
}: CounterAnimationProps) {
const [count, setCount] = useState(0);
const ref = useRef(null);
const isInView = useInView(ref, { once: true });
useEffect(() => {
if (!isInView) return;
let startTime: number;
const startCount = 0;
const animate = (currentTime: number) => {
if (!startTime) startTime = currentTime;
const progress = Math.min((currentTime - startTime) / duration, 1);
const easeOutQuart = 1 - Math.pow(1 - progress, 4);
const currentCount = Math.floor(easeOutQuart * (end - startCount) + startCount);
setCount(currentCount);
if (progress < 1) {
requestAnimationFrame(animate);
}
};
requestAnimationFrame(animate);
}, [end, duration, isInView]);
return (
<motion.span
ref={ref}
className={className}
initial={{ opacity: 0 }}
animate={{ opacity: isInView ? 1 : 0 }}
>
{prefix}{count}{suffix}
</motion.span>
);
}// src/lib/seo.ts
import { Metadata } from 'next';
interface SEOProps {
title: string;
description: string;
keywords?: string[];
canonical?: string;
ogImage?: string;
noIndex?: boolean;
}
export function generateMetadata({
title,
description,
keywords = [],
canonical,
ogImage = '/images/og-default.jpg',
noIndex = false,
}: SEOProps): Metadata {
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://securiace.com';
const fullTitle = `${title} | Securiace Technologies`;
const canonicalUrl = canonical ? `${siteUrl}${canonical}` : undefined;
return {
title: fullTitle,
description,
keywords: keywords.join(', '),
canonical: canonicalUrl,
robots: {
index: !noIndex,
follow: !noIndex,
googleBot: {
index: !noIndex,
follow: !noIndex,
'max-video-preview': -1,
'max-image-preview': 'large',
'max-snippet': -1,
},
},
openGraph: {
title: fullTitle,
description,
url: canonicalUrl,
siteName: 'Securiace Technologies',
images: [
{
url: ogImage,
width: 1200,
height: 630,
alt: title,
},
],
locale: 'en_US',
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: fullTitle,
description,
images: [ogImage],
},
};
}
export function generateSchemaMarkup(
type: 'organization' | 'product' | 'service',
data: any
) {
const baseSchema = {
'@context': 'https://schema.org',
'@type': type,
...data,
};
return {
__html: JSON.stringify(baseSchema),
};
}// src/lib/analytics.ts
declare global {
interface Window {
gtag: (...args: any[]) => void;
plausible: (...args: any[]) => void;
}
}
export function trackEvent(
eventName: string,
properties?: Record<string, any>
) {
if (typeof window === 'undefined') return;
// Google Analytics 4
if (window.gtag) {
window.gtag('event', eventName, {
event_category: properties?.category || 'engagement',
event_label: properties?.label,
value: properties?.value,
...properties,
});
}
// Plausible Analytics
if (window.plausible) {
window.plausible(eventName, {
props: properties,
});
}
}
export function trackPageView(url: string, title: string) {
if (typeof window === 'undefined') return;
// Google Analytics 4
if (window.gtag) {
window.gtag('config', process.env.NEXT_PUBLIC_GA_ID, {
page_title: title,
page_location: url,
});
}
// Plausible Analytics
if (window.plausible) {
window.plausible('pageview', {
u: url,
});
}
}
export function trackConversion(conversionType: string, value?: number) {
trackEvent('conversion', {
conversion_type: conversionType,
value: value,
currency: 'INR',
});
}This implementation guide provides comprehensive, production-ready code examples that the Cursor AI agent can use to build the Securiace Technologies website with enterprise-grade quality and performance.