update
This commit is contained in:
433
frontEnd/components/pages/home/HomeBanner.tsx
Normal file
433
frontEnd/components/pages/home/HomeBanner.tsx
Normal file
@@ -0,0 +1,433 @@
|
||||
"use client";
|
||||
import Link from "next/link";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
const HomeBanner = () => {
|
||||
const [currentTextIndex, setCurrentTextIndex] = useState(0);
|
||||
const [isTransitioning, setIsTransitioning] = useState(false);
|
||||
|
||||
// Fix viewport height for mobile browsers (especially iOS Safari)
|
||||
useEffect(() => {
|
||||
const setVH = () => {
|
||||
const vh = window.innerHeight * 0.01;
|
||||
document.documentElement.style.setProperty('--vh', `${vh}px`);
|
||||
};
|
||||
|
||||
setVH();
|
||||
window.addEventListener('resize', setVH);
|
||||
// Use 'resize' event instead of deprecated 'orientationchange'
|
||||
// The resize event fires on orientation change as well
|
||||
const handleOrientationChange = () => {
|
||||
// Small delay to ensure viewport has updated
|
||||
setTimeout(setVH, 100);
|
||||
};
|
||||
window.addEventListener('resize', handleOrientationChange);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', setVH);
|
||||
window.removeEventListener('resize', handleOrientationChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Static banner slides data based on actual services
|
||||
const carouselTexts = [
|
||||
{
|
||||
id: 1,
|
||||
badge: "Custom Development",
|
||||
icon: "fa-solid fa-code",
|
||||
heading: "Tailored Enterprise Software ",
|
||||
highlight: "Development",
|
||||
subheading: "Aligned with Your Business Goals",
|
||||
description: "We design and build custom digital solutions that deliver reliable, scalable, and future-ready applications, driving measurable value and competitive advantage for your enterprise.",
|
||||
button_text: "Explore Solutions",
|
||||
button_url: "/services/custom-software-development",
|
||||
is_active: true,
|
||||
display_order: 1,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
badge: "Business Intelligence",
|
||||
icon: "fa-solid fa-brain",
|
||||
heading: "AI-Powered ",
|
||||
highlight: "Analytics",
|
||||
subheading: "Transform Data into Insights",
|
||||
description: "Turn enterprise data into actionable intelligence with advanced AI and machine learning, enabling smarter decisions, performance optimization, and data-driven innovation.",
|
||||
button_text: "Discover AI Solutions",
|
||||
button_url: "/services/ai-powered-business-intelligence",
|
||||
is_active: true,
|
||||
display_order: 2,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
badge: "System Integration",
|
||||
icon: "fa-solid fa-plug",
|
||||
heading: "Enterprise Systems ",
|
||||
highlight: "Integration",
|
||||
subheading: "Seamless Connectivity",
|
||||
description: "Connect everything — from payment systems and ERP platforms to cloud services, enabling your enterprise to operate seamlessly across physical and digital environments.",
|
||||
button_text: "View Integrations",
|
||||
button_url: "/services/external-systems-integrations",
|
||||
is_active: true,
|
||||
display_order: 3,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
badge: "Incident Management",
|
||||
icon: "fa-solid fa-bell",
|
||||
heading: "Intelligent Incident ",
|
||||
highlight: "Management",
|
||||
subheading: "Minimize Downtime & Protect Trust",
|
||||
description: "Cloud-based incident management that empowers teams to detect, respond, and resolve issues faster, reducing downtime and maintaining customer confidence.",
|
||||
button_text: "Learn More",
|
||||
button_url: "/services/incident-management-saas",
|
||||
is_active: true,
|
||||
display_order: 4,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
}
|
||||
];
|
||||
|
||||
// Carousel rotation effect
|
||||
useEffect(() => {
|
||||
if (carouselTexts.length <= 1) return;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
setIsTransitioning(true);
|
||||
|
||||
setTimeout(() => {
|
||||
setCurrentTextIndex((prevIndex) =>
|
||||
prevIndex === carouselTexts.length - 1 ? 0 : prevIndex + 1
|
||||
);
|
||||
setIsTransitioning(false);
|
||||
}, 1000); // Slightly longer for smoother transition
|
||||
}, 6000); // Increased interval for slower changes
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [carouselTexts.length]);
|
||||
|
||||
const currentText = carouselTexts[currentTextIndex];
|
||||
|
||||
if (!currentText) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="modern-banner">
|
||||
<div className="banner-background">
|
||||
<div className="gradient-orb orb-1"></div>
|
||||
<div className="gradient-orb orb-2"></div>
|
||||
<div className="gradient-orb orb-3"></div>
|
||||
|
||||
{/* Industrial Enterprise Background Elements */}
|
||||
<div className="enterprise-bg-elements">
|
||||
{/* Flying Code Elements */}
|
||||
<div className="flying-code">
|
||||
<div className="code-snippet code-1">
|
||||
<span className="code-line">const enterprise = {'{'}</span>
|
||||
<span className="code-line"> security: 'enterprise-grade',</span>
|
||||
<span className="code-line"> scalability: 'unlimited'</span>
|
||||
<span className="code-line">{'}'};</span>
|
||||
</div>
|
||||
<div className="code-snippet code-2">
|
||||
<span className="code-line">if (security === 'max') {'{'}</span>
|
||||
<span className="code-line"> deploy.enterprise();</span>
|
||||
<span className="code-line">{'}'}</span>
|
||||
</div>
|
||||
<div className="code-snippet code-3">
|
||||
<span className="code-line">class EnterpriseSoftware {'{'}</span>
|
||||
<span className="code-line"> constructor() {'{'}</span>
|
||||
<span className="code-line"> this.secure = true;</span>
|
||||
<span className="code-line"> {'}'}</span>
|
||||
<span className="code-line">{'}'}</span>
|
||||
</div>
|
||||
<div className="code-snippet code-4">
|
||||
<span className="code-line">API.authenticate({'{'}</span>
|
||||
<span className="code-line"> level: 'enterprise',</span>
|
||||
<span className="code-line"> encryption: 'AES-256'</span>
|
||||
<span className="code-line">{'}'});</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Industrial Grid */}
|
||||
<div className="industrial-grid">
|
||||
<div className="grid-line horizontal h-1"></div>
|
||||
<div className="grid-line horizontal h-2"></div>
|
||||
<div className="grid-line horizontal h-3"></div>
|
||||
<div className="grid-line horizontal h-4"></div>
|
||||
<div className="grid-line vertical v-1"></div>
|
||||
<div className="grid-line vertical v-2"></div>
|
||||
<div className="grid-line vertical v-3"></div>
|
||||
<div className="grid-line vertical v-4"></div>
|
||||
</div>
|
||||
|
||||
{/* Security Elements */}
|
||||
<div className="security-elements">
|
||||
<div className="shield shield-1">
|
||||
<i className="fa-solid fa-shield-halved"></i>
|
||||
</div>
|
||||
<div className="shield shield-2">
|
||||
<i className="fa-solid fa-lock"></i>
|
||||
</div>
|
||||
<div className="shield shield-3">
|
||||
<i className="fa-solid fa-key"></i>
|
||||
</div>
|
||||
<div className="shield shield-4">
|
||||
<i className="fa-solid fa-fingerprint"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Circuit Patterns */}
|
||||
<div className="circuit-patterns">
|
||||
<div className="circuit circuit-1">
|
||||
<div className="circuit-node"></div>
|
||||
<div className="circuit-line"></div>
|
||||
<div className="circuit-node"></div>
|
||||
</div>
|
||||
<div className="circuit circuit-2">
|
||||
<div className="circuit-node"></div>
|
||||
<div className="circuit-line"></div>
|
||||
<div className="circuit-node"></div>
|
||||
<div className="circuit-line"></div>
|
||||
<div className="circuit-node"></div>
|
||||
</div>
|
||||
<div className="circuit circuit-3">
|
||||
<div className="circuit-node"></div>
|
||||
<div className="circuit-line"></div>
|
||||
<div className="circuit-node"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Data Streams */}
|
||||
<div className="data-streams">
|
||||
<div className="stream stream-1">
|
||||
<div className="data-bit"></div>
|
||||
<div className="data-bit"></div>
|
||||
<div className="data-bit"></div>
|
||||
<div className="data-bit"></div>
|
||||
</div>
|
||||
<div className="stream stream-2">
|
||||
<div className="data-bit"></div>
|
||||
<div className="data-bit"></div>
|
||||
<div className="data-bit"></div>
|
||||
<div className="data-bit"></div>
|
||||
<div className="data-bit"></div>
|
||||
</div>
|
||||
<div className="stream stream-3">
|
||||
<div className="data-bit"></div>
|
||||
<div className="data-bit"></div>
|
||||
<div className="data-bit"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Request/Response Data */}
|
||||
<div className="request-response-data">
|
||||
<div className="api-request req-1">
|
||||
<div className="request-label">POST /api/enterprise</div>
|
||||
<div className="request-data">
|
||||
<div className="data-packet"></div>
|
||||
<div className="data-packet"></div>
|
||||
<div className="data-packet"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="api-response resp-1">
|
||||
<div className="response-label">200 OK</div>
|
||||
<div className="response-data">
|
||||
<div className="data-packet"></div>
|
||||
<div className="data-packet"></div>
|
||||
<div className="data-packet"></div>
|
||||
<div className="data-packet"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="api-request req-2">
|
||||
<div className="request-label">GET /api/analytics</div>
|
||||
<div className="request-data">
|
||||
<div className="data-packet"></div>
|
||||
<div className="data-packet"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="api-response resp-2">
|
||||
<div className="response-label">201 Created</div>
|
||||
<div className="response-data">
|
||||
<div className="data-packet"></div>
|
||||
<div className="data-packet"></div>
|
||||
<div className="data-packet"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Space Data Generation */}
|
||||
<div className="space-data-generation">
|
||||
<div className="data-cluster cluster-1">
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
</div>
|
||||
<div className="data-cluster cluster-2">
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
</div>
|
||||
<div className="data-cluster cluster-3">
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
</div>
|
||||
<div className="data-cluster cluster-4">
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
<div className="data-point"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Database Connections */}
|
||||
<div className="database-connections">
|
||||
<div className="db-connection conn-1">
|
||||
<div className="connection-line"></div>
|
||||
<div className="db-node">
|
||||
<i className="fa-solid fa-database"></i>
|
||||
</div>
|
||||
<div className="connection-pulse"></div>
|
||||
</div>
|
||||
<div className="db-connection conn-2">
|
||||
<div className="connection-line"></div>
|
||||
<div className="db-node">
|
||||
<i className="fa-solid fa-server"></i>
|
||||
</div>
|
||||
<div className="connection-pulse"></div>
|
||||
</div>
|
||||
<div className="db-connection conn-3">
|
||||
<div className="connection-line"></div>
|
||||
<div className="db-node">
|
||||
<i className="fa-solid fa-cloud"></i>
|
||||
</div>
|
||||
<div className="connection-pulse"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Real-time Metrics */}
|
||||
<div className="real-time-metrics">
|
||||
<div className="metric metric-1">
|
||||
<div className="metric-label">API Calls/sec</div>
|
||||
<div className="metric-value">2,847</div>
|
||||
<div className="metric-bar">
|
||||
<div className="bar-fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="metric metric-2">
|
||||
<div className="metric-label">Data Processed</div>
|
||||
<div className="metric-value">15.2TB</div>
|
||||
<div className="metric-bar">
|
||||
<div className="bar-fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="metric metric-3">
|
||||
<div className="metric-label">Active Users</div>
|
||||
<div className="metric-value">45,892</div>
|
||||
<div className="metric-bar">
|
||||
<div className="bar-fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container">
|
||||
<div className="banner-content">
|
||||
<div className="content-center">
|
||||
<div className="badge-container">
|
||||
<span className="badge">
|
||||
<i className={currentText.icon}></i>
|
||||
{currentText.badge}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1 className={`main-heading carousel-text ${isTransitioning ? 'fade-out' : 'fade-in'}`}>
|
||||
{currentText.heading}
|
||||
<span className="gradient-text"> {currentText.highlight}</span>
|
||||
<br />
|
||||
{currentText.subheading}
|
||||
</h1>
|
||||
|
||||
<p className={`description carousel-text ${isTransitioning ? 'fade-out' : 'fade-in'}`}>
|
||||
{currentText.description}
|
||||
</p>
|
||||
|
||||
{/* Carousel Indicators */}
|
||||
<div className="carousel-indicators">
|
||||
{carouselTexts.map((_, index) => (
|
||||
<button
|
||||
key={index}
|
||||
className={`indicator ${index === currentTextIndex ? 'active' : ''}`}
|
||||
onClick={() => {
|
||||
if (index !== currentTextIndex) {
|
||||
setIsTransitioning(true);
|
||||
setTimeout(() => {
|
||||
setCurrentTextIndex(index);
|
||||
setIsTransitioning(false);
|
||||
}, 1000);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="cta-section">
|
||||
<Link href={currentText.button_url || "#"} className="cta-primary">
|
||||
<span>{currentText.button_text || "Learn More"}</span>
|
||||
<i className="fa-solid fa-arrow-right"></i>
|
||||
</Link>
|
||||
<Link href="contact-us" className="cta-secondary">
|
||||
<i className="fa-solid fa-phone"></i>
|
||||
<span>Contact Sales</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="trust-indicators">
|
||||
<div className="trust-item">
|
||||
<div className="trust-number">30+</div>
|
||||
<div className="trust-label">Enterprise Clients</div>
|
||||
</div>
|
||||
<div className="trust-item">
|
||||
<div className="trust-number">99.9%</div>
|
||||
<div className="trust-label">Uptime SLA</div>
|
||||
</div>
|
||||
<div className="trust-item">
|
||||
<div className="trust-number">24/7</div>
|
||||
<div className="trust-label">Enterprise Support</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div className="scroll-indicator" onClick={() => window.scrollTo({ top: window.innerHeight, behavior: 'smooth' })}>
|
||||
<div className="scroll-text">Scroll to explore</div>
|
||||
<div className="scroll-arrow">
|
||||
<i className="fa-solid fa-chevron-down"></i>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HomeBanner;
|
||||
65
frontEnd/components/pages/home/HomeInitAnimations.tsx
Normal file
65
frontEnd/components/pages/home/HomeInitAnimations.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
"use client";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const SmoothScroll = dynamic(() => import("../../shared/layout/animations/SmoothScroll"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
const ParallaxImage = dynamic(() => import("../../shared/layout/animations/ParallaxImage"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
const FadeImageBottom = dynamic(() => import("../../shared/layout/animations/FadeImageBottom"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
const ButtonHoverAnimation = dynamic(
|
||||
() => import("../../shared/layout/animations/ButtonHoverAnimation"),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
);
|
||||
|
||||
const VanillaTiltHover = dynamic(
|
||||
() => import("../../shared/layout/animations/VanillaTiltHover"),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
);
|
||||
|
||||
const SplitTextAnimations = dynamic(
|
||||
() => import("../../shared/layout/animations/SplitTextAnimations"),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
);
|
||||
|
||||
const ScrollToElement = dynamic(() => import("../../shared/layout/animations/ScrollToElement"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
const AppearDown = dynamic(() => import("../../shared/layout/animations/AppearDown"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
const FadeAnimations = dynamic(() => import("../../shared/layout/animations/FadeAnimations"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
const HomeInitAnimations = () => {
|
||||
return (
|
||||
<>
|
||||
<SmoothScroll />
|
||||
<ParallaxImage />
|
||||
<FadeImageBottom />
|
||||
<ButtonHoverAnimation />
|
||||
<VanillaTiltHover />
|
||||
<SplitTextAnimations />
|
||||
<ScrollToElement />
|
||||
<AppearDown />
|
||||
<FadeAnimations />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HomeInitAnimations;
|
||||
123
frontEnd/components/pages/home/HomeLatestPost.tsx
Normal file
123
frontEnd/components/pages/home/HomeLatestPost.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
"use client";
|
||||
import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
import { Autoplay } from "swiper/modules";
|
||||
import "swiper/swiper-bundle.css";
|
||||
import { useLatestPosts } from "@/lib/hooks/useBlog";
|
||||
import { getValidImageUrl, getValidImageAlt, FALLBACK_IMAGES } from "@/lib/imageUtils";
|
||||
|
||||
const HomeLatestPost = () => {
|
||||
const { posts, loading, error } = useLatestPosts(12); // Get 12 latest posts
|
||||
|
||||
return (
|
||||
<section className="tp-latest-post pt-120 pb-120 bg-quinary">
|
||||
<div className="container">
|
||||
<div className="row vertical-column-gap align-items-center">
|
||||
<div className="col-12 col-lg-7">
|
||||
<div className="tp-lp-title text-center text-lg-start">
|
||||
<h2 className="mt-8 fw-7 text-secondary title-anim">
|
||||
Latest Insights
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-5">
|
||||
<div className="tp-lp-cta text-center text-lg-end">
|
||||
<Link href="/insights" className="btn-line text-uppercase">
|
||||
View All Insights
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="tp-lp-slider-wrapper mt-60">
|
||||
{loading ? (
|
||||
<div className="text-center py-5">
|
||||
<div className="spinner-border text-primary" role="status">
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p className="text-tertiary mt-3">Loading insights...</p>
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="text-center py-5">
|
||||
<p className="text-danger">Error loading insights. Please try again later.</p>
|
||||
</div>
|
||||
) : posts.length === 0 ? (
|
||||
<div className="text-center py-5">
|
||||
<p className="text-tertiary">No insights available yet.</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="tp-lp-slider-wrapper">
|
||||
<Swiper
|
||||
slidesPerView={"auto"}
|
||||
spaceBetween={24}
|
||||
slidesPerGroup={1}
|
||||
freeMode={true}
|
||||
speed={1200}
|
||||
loop={posts.length > 3}
|
||||
roundLengths={true}
|
||||
modules={[Autoplay]}
|
||||
autoplay={{
|
||||
delay: 5000,
|
||||
disableOnInteraction: false,
|
||||
pauseOnMouseEnter: true,
|
||||
}}
|
||||
className="tp-lp-slider"
|
||||
>
|
||||
{posts.map((post) => (
|
||||
<SwiperSlide key={post.id}>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link
|
||||
href={`/insights/${post.slug}`}
|
||||
className="w-100 overflow-hidden d-block"
|
||||
>
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={getValidImageUrl(post.thumbnail, FALLBACK_IMAGES.BLOG)}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220 parallax-image"
|
||||
alt={getValidImageAlt(post.title)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
{post.author_name || 'Admin'}
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
{new Date(post.published_at || post.created_at).toLocaleDateString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric'
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href={`/insights/${post.slug}`}>
|
||||
{post.title}
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default HomeLatestPost;
|
||||
67
frontEnd/components/pages/home/HomeScrollProgressButton.tsx
Normal file
67
frontEnd/components/pages/home/HomeScrollProgressButton.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
"use client";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
|
||||
const HomeScrollProgressButton = () => {
|
||||
useEffect(() => {
|
||||
window.scroll(0, 0);
|
||||
}, []);
|
||||
|
||||
const [scrollProgress, setScrollProgress] = useState(0);
|
||||
const [isActive, setIsActive] = useState(false);
|
||||
const scrollRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const handleScroll = () => {
|
||||
const totalHeight = document.body.scrollHeight - window.innerHeight;
|
||||
const progress = (window.scrollY / totalHeight) * 100;
|
||||
setScrollProgress(progress);
|
||||
setIsActive(window.scrollY > 50);
|
||||
};
|
||||
|
||||
const handleProgressClick = () => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: "smooth",
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
handleScroll();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("scroll", handleScroll);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={scrollRef}
|
||||
className={`progress-wrap ${isActive ? " active-progress" : " "}`}
|
||||
onClick={handleProgressClick}
|
||||
title="Go To Top"
|
||||
>
|
||||
<span></span>
|
||||
<svg
|
||||
className="progress-circle svg-content"
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="-1 -1 102 102"
|
||||
>
|
||||
<path
|
||||
d="M50,1 a49,49 0 0,1 0,98 a49,49 0 0,1 0,-98"
|
||||
stroke="#3887FE"
|
||||
strokeWidth="4"
|
||||
fill="none"
|
||||
style={{
|
||||
strokeDasharray: "308.66px",
|
||||
strokeDashoffset: `${308.66 - scrollProgress * 3.0866}px`,
|
||||
}}
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default HomeScrollProgressButton;
|
||||
121
frontEnd/components/pages/home/Overview.tsx
Normal file
121
frontEnd/components/pages/home/Overview.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import { useMemo } from "react";
|
||||
import { useServices } from "@/lib/hooks/useServices";
|
||||
import { serviceUtils } from "@/lib/api/serviceService";
|
||||
import one from "@/public/images/overview/one.png";
|
||||
import two from "@/public/images/overview/two.png";
|
||||
import three from "@/public/images/overview/three.png";
|
||||
import four from "@/public/images/overview/four.png";
|
||||
import five from "@/public/images/overview/five.png";
|
||||
|
||||
// Default images array for fallback
|
||||
const defaultImages = [one, two, three, four, five];
|
||||
|
||||
const Overview = () => {
|
||||
// Memoize the parameters to prevent infinite re-renders
|
||||
const serviceParams = useMemo(() => ({
|
||||
ordering: 'display_order',
|
||||
page: 1
|
||||
}), []);
|
||||
|
||||
// Fetch services from API, limit to 5 for overview section
|
||||
const { services, loading, error } = useServices(serviceParams);
|
||||
|
||||
// Get first 5 services for the overview
|
||||
const displayServices = services.slice(0, 5);
|
||||
return (
|
||||
<section
|
||||
className="tp-overview pt-120 pb-120 sticky-wrapper"
|
||||
id="scroll-to"
|
||||
>
|
||||
<div className="container">
|
||||
<div className="row vertical-column-gap-lg">
|
||||
<div className="col-12 col-lg-5">
|
||||
<div className="tp-overview__content sticky-item">
|
||||
<h2 className="fw-7 text-secondary title-anim mb-30 mt-8">
|
||||
Enterprise Solutions That Scale
|
||||
</h2>
|
||||
<p className="mt-8 cur-lg">
|
||||
Our enterprise software development teams deliver mission-critical solutions
|
||||
for Fortune 500 companies. We design, build, and deploy scalable systems
|
||||
that transform your business operations, enhance security, and drive digital
|
||||
innovation across your organization.
|
||||
</p>
|
||||
<div className="mt-40">
|
||||
<Link href="services" className="btn-line text-uppercase">
|
||||
EXPLORE SOLUTIONS
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-7 col-xxl-6 offset-xxl-1">
|
||||
<div className="tp-overview__items sticky-item">
|
||||
{loading ? (
|
||||
// Loading state
|
||||
<div className="text-center py-5">
|
||||
<div className="spinner-border text-primary" role="status">
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p className="mt-3 text-muted">Loading services...</p>
|
||||
</div>
|
||||
) : error ? (
|
||||
// Error state
|
||||
<div className="text-center py-5">
|
||||
<p className="text-danger">Failed to load services. Please try again later.</p>
|
||||
</div>
|
||||
) : displayServices.length === 0 ? (
|
||||
// No services state
|
||||
<div className="text-center py-5">
|
||||
<p className="text-muted">No services available at the moment.</p>
|
||||
</div>
|
||||
) : (
|
||||
// Services list
|
||||
displayServices.map((service, index) => {
|
||||
// Always use hardcoded images, not API images
|
||||
const serviceImage = defaultImages[index] || defaultImages[0];
|
||||
|
||||
return (
|
||||
<div key={service.id} className="tp-overview-single appear-down">
|
||||
<div className="thumb">
|
||||
<Image
|
||||
src={serviceImage}
|
||||
width={80}
|
||||
height={80}
|
||||
alt={service.title}
|
||||
/>
|
||||
</div>
|
||||
<div className="wrapper">
|
||||
<div className="content">
|
||||
<h4 className="mt-8 mb-12 fw-6 text-secondary">
|
||||
<Link href={`/services/${service.slug}`}>
|
||||
{service.title}
|
||||
</Link>
|
||||
</h4>
|
||||
<p className="text-tertiary">
|
||||
{service.short_description || service.description}
|
||||
</p>
|
||||
</div>
|
||||
<div className="cta">
|
||||
<Link href={`/services/${service.slug}`}>
|
||||
<span className="material-symbols-outlined">
|
||||
call_made
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Overview;
|
||||
78
frontEnd/components/pages/home/ServiceIntro.tsx
Normal file
78
frontEnd/components/pages/home/ServiceIntro.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import thumb from "@/public/images/leading.jpg";
|
||||
|
||||
const ServiceIntro = () => {
|
||||
return (
|
||||
<section className="tp-service pt-120 pb-120">
|
||||
<div className="container">
|
||||
<div className="row vertical-column-gap-md">
|
||||
<div className="col-12 col-lg-4">
|
||||
<div className="tp-service__thumb" style={{ maxWidth: '400px', border: 'none', padding: 0, margin: 0, overflow: 'hidden', borderRadius: '8px' }}>
|
||||
<Link href="services">
|
||||
<Image
|
||||
src={thumb}
|
||||
alt="Enterprise Software Solutions"
|
||||
width={400}
|
||||
height={500}
|
||||
objectFit="cover"
|
||||
style={{ display: 'block', border: 'none', margin: 0, padding: 0 }}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-8">
|
||||
<div className="tp-service__content">
|
||||
<div className="tp-section-wrapper">
|
||||
|
||||
<h2 className="title-anim text-secondary fw-7 mb-30">
|
||||
Accelerating Digital Transformation Through<br />
|
||||
Mission-Critical Enterprise Software
|
||||
</h2>
|
||||
</div>
|
||||
<div className="pl-50">
|
||||
<p className="cur-lg mb-25">
|
||||
GNX partners with Fortune 40 companies and global enterprises to architect,
|
||||
develop, and deploy business-critical software solutions that drive measurable
|
||||
results. Our engineering teams deliver secure, scalable, and compliant systems
|
||||
that power digital innovation across industries.
|
||||
</p>
|
||||
<p className="cur-lg mb-30">
|
||||
From cloud-native architectures and enterprise integration platforms to
|
||||
AI-powered analytics and legacy modernization, we provide end-to-end
|
||||
technology solutions that reduce operational costs, enhance efficiency,
|
||||
and deliver competitive advantage.
|
||||
</p>
|
||||
<div className="tp-service__features mb-40">
|
||||
<ul className="list-unstyled">
|
||||
<li className="mb-15">
|
||||
<i className="fa-solid fa-circle-check text-primary me-10"></i>
|
||||
<span className="fw-6">Enterprise-Grade Security & Compliance</span>
|
||||
</li>
|
||||
<li className="mb-15">
|
||||
<i className="fa-solid fa-circle-check text-primary me-10"></i>
|
||||
<span className="fw-6">Scalable Cloud-Native Architectures</span>
|
||||
</li>
|
||||
<li className="mb-15">
|
||||
<i className="fa-solid fa-circle-check text-primary me-10"></i>
|
||||
<span className="fw-6">24/7 Support & Dedicated Engineering Teams</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="mt-60">
|
||||
<Link href="services" className="btn-anim btn-anim-light">
|
||||
Explore Our Solutions
|
||||
<i className="fa-solid fa-arrow-right"></i>
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServiceIntro;
|
||||
175
frontEnd/components/pages/home/Story.tsx
Normal file
175
frontEnd/components/pages/home/Story.tsx
Normal file
@@ -0,0 +1,175 @@
|
||||
"use client";
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import { getValidImageUrl, FALLBACK_IMAGES } from "@/lib/imageUtils";
|
||||
import { useCaseStudies } from "@/lib/hooks/useCaseStudy";
|
||||
import { API_CONFIG } from "@/lib/config/api";
|
||||
|
||||
const Story = () => {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const [activeImageIndex, setActiveImageIndex] = useState(0);
|
||||
|
||||
// Fetch case studies from API with ordering and limit
|
||||
const params = useMemo(() => ({
|
||||
ordering: 'display_order',
|
||||
page_size: 5
|
||||
}), []);
|
||||
|
||||
const { caseStudies, loading, error } = useCaseStudies(params);
|
||||
|
||||
// Fallback to static data if API fails or is loading
|
||||
const staticStoryData = [
|
||||
{
|
||||
id: 1,
|
||||
category_name: "Financial Services",
|
||||
title: "Banking System Modernization",
|
||||
excerpt: "Complete digital transformation of legacy banking systems with enhanced security and real-time processing capabilities.",
|
||||
thumbnail: "/images/case/one.png",
|
||||
slug: "banking-system-modernization",
|
||||
display_order: 1,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
category_name: "Healthcare",
|
||||
title: "Patient Management System",
|
||||
excerpt: "Enterprise-grade patient management system with HIPAA compliance and seamless integration across multiple healthcare facilities.",
|
||||
thumbnail: "/images/case/two.png",
|
||||
slug: "patient-management-system",
|
||||
display_order: 2,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
category_name: "Manufacturing",
|
||||
title: "Supply Chain Optimization",
|
||||
excerpt: "Advanced supply chain management system with real-time tracking, predictive analytics, and automated inventory management.",
|
||||
thumbnail: "/images/case/three.png",
|
||||
slug: "supply-chain-optimization",
|
||||
display_order: 3,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
category_name: "E-commerce",
|
||||
title: "Multi-Platform Integration",
|
||||
excerpt: "Seamless integration of multiple e-commerce platforms with unified inventory management and real-time synchronization.",
|
||||
thumbnail: "/images/case/four.png",
|
||||
slug: "multi-platform-integration",
|
||||
display_order: 4,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
category_name: "Education",
|
||||
title: "Learning Management System",
|
||||
excerpt: "Comprehensive LMS with advanced analytics, automated grading, and seamless integration with existing educational tools.",
|
||||
thumbnail: "/images/case/five.png",
|
||||
slug: "learning-management-system",
|
||||
display_order: 5,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
}
|
||||
];
|
||||
|
||||
// Use API data if available, otherwise use static data
|
||||
const storyData = caseStudies.length > 0 ? caseStudies : staticStoryData;
|
||||
|
||||
// Log when API data is loaded
|
||||
useEffect(() => {
|
||||
if (caseStudies.length > 0) {
|
||||
}
|
||||
}, [caseStudies]);
|
||||
|
||||
const handleMouseEnter = (index: number) => {
|
||||
setActiveIndex(index);
|
||||
setActiveImageIndex(index);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<section className="tp-story pt-120 pb-120 bg-black sticky-wrapper fade-wrapper">
|
||||
<div className="container">
|
||||
<div className="row vertical-column-gap-md">
|
||||
<div className="col-12 col-lg-5">
|
||||
<div className="tp-story__content sticky-item">
|
||||
<h2 className="mt-8 title-anim text-white fw-7">
|
||||
Enterprise Case Studies
|
||||
</h2>
|
||||
<div className="tp-story__items">
|
||||
{storyData.map((item, index) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`tp-story__single fade-top ${
|
||||
index === activeIndex ? "active" : ""
|
||||
}`}
|
||||
onMouseEnter={() => handleMouseEnter(index)}
|
||||
>
|
||||
<p className="fw-6 mt-8">
|
||||
<Link href={`/case-study/${item.slug}`}>
|
||||
{item.category_name || "Case Study"}
|
||||
</Link>
|
||||
</p>
|
||||
<h5 className="fw-4 mt-12 mb-12 text-white">
|
||||
{item.title}
|
||||
</h5>
|
||||
<p className="text-xs">{item.excerpt}</p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-7 col-xxl-6 offset-xxl-1 d-none d-lg-block">
|
||||
<div className="tp-story__thumbs sticky-item">
|
||||
{storyData.map((item, index) => {
|
||||
// Get the image URL - handle different scenarios
|
||||
let imageUrl;
|
||||
if (item.thumbnail) {
|
||||
if (item.thumbnail.startsWith('http')) {
|
||||
// Full URL (external)
|
||||
imageUrl = item.thumbnail;
|
||||
} else if (item.thumbnail.startsWith('/media')) {
|
||||
// Relative path starting with /media
|
||||
imageUrl = `${API_CONFIG.BASE_URL}${item.thumbnail}`;
|
||||
} else {
|
||||
// Just filename or relative path
|
||||
imageUrl = `${API_CONFIG.MEDIA_URL}/${item.thumbnail}`;
|
||||
}
|
||||
} else {
|
||||
// Fallback to static image
|
||||
imageUrl = getValidImageUrl('/images/case/one.png', FALLBACK_IMAGES.CASE_STUDY);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`tp-story-thumb ${
|
||||
index === activeImageIndex ? "thumb-active" : ""
|
||||
}`}
|
||||
>
|
||||
<Image
|
||||
src={imageUrl}
|
||||
width={600}
|
||||
height={300}
|
||||
className="w-100 mh-300"
|
||||
alt={item.title || "Case Study"}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Story;
|
||||
Reference in New Issue
Block a user