Files
edr-platform/apps/edr-freight-web/backoffice/src/layout/components/HeroSection.tsx
natib21 e6e44e773b fix ui
2026-07-10 11:25:59 +00:00

561 lines
25 KiB
TypeScript

import { useEffect, useState } from "react";
import { motion, useAnimation, Variants } from "framer-motion";
import { useInView } from "react-intersection-observer";
import { easeInOut } from "framer-motion";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import Cookies from "js-cookie";
import { ArrowRight, BookOpenText, ShieldCheck } from "lucide-react";
import {
useTenantConfig,
resolveModuleConfig,
} from "@/layout/components/TenantConfig";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/shared/common/ui/dialog";
const HeroSection = () => {
const controls = useAnimation();
const { t } = useTranslation();
const navigate = useNavigate();
const { config: tenantConfig } = useTenantConfig();
const moduleConfig = resolveModuleConfig(tenantConfig);
const primary = tenantConfig.primaryColor || "#18AA9D";
const secondary = tenantConfig.secondaryColor || primary;
const [ref, inView] = useInView({
threshold: 0.1,
triggerOnce: true,
});
useEffect(() => {
if (inView) {
controls.start("visible");
} else {
controls.start("hidden");
}
}, [controls, inView]);
const containerVariants: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.15,
delayChildren: 0.2,
},
},
};
const itemVariants: Variants = {
hidden: { y: 30, opacity: 0 },
visible: {
y: 0,
opacity: 1,
transition: {
duration: 0.6,
ease: easeInOut,
},
},
};
const isTokenPresent = Boolean(Cookies.get("auth-token"));
const heroTitle =
tenantConfig?.organizationName || t("landingPage.smartOffice");
const welcomeMessage =
tenantConfig?.welcomeMessage || t("landingPage.initiativeDescription");
const organizationInitials =
heroTitle
?.split(/[\s\-–—]+/)
.filter(Boolean)
.map((word) => word.charAt(0))
.join("")
.toUpperCase() || "SO";
const cardVariants: Variants = {
hidden: { scale: 0.85, opacity: 0, rotateX: 10 },
visible: {
scale: 1,
opacity: 1,
rotateX: 0,
transition: {
delay: 0.4,
duration: 0.8,
type: "spring",
stiffness: 100,
damping: 15,
},
},
};
const floatingElementVariants: Variants = {
float: {
y: [0, -20, 0],
transition: {
duration: 4,
repeat: Infinity,
ease: "easeInOut", // ✅ correct literal type
},
},
};
return (
<section className="relative bg-gradient-to-br from-white via-blue-50/30 to-primary-50/50 dark:from-gray-900 dark:via-gray-800 dark:to-gray-900 overflow-x-hidden min-h-screen flex items-start pt-8">
{/* Enhanced Background */}
<div className="absolute inset-0 overflow-hidden">
{/* Gradient Orbs */}
<div className="absolute -top-40 -left-40 w-80 h-80 bg-gradient-to-r from-primary to-primary-500 rounded-full mix-blend-multiply filter blur-3xl opacity-15 animate-orb-slow dark:opacity-10"></div>
<div className="absolute -top-20 -right-20 w-96 h-96 bg-gradient-to-r from-primary-500 to-primary rounded-full mix-blend-multiply filter blur-3xl opacity-10 animate-orb-medium dark:opacity-5"></div>
<div className="absolute -bottom-40 left-1/3 w-72 h-72 bg-gradient-to-r from-primary-700 to-primary-500 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-orb-fast dark:opacity-10"></div>
{/* Grid Pattern */}
<div className="absolute inset-0 bg-[linear-gradient(rgba(24,170,157,0.03)_1px,transparent_1px),linear-gradient(90deg,rgba(24,170,157,0.03)_1px,transparent_1px)] bg-[size:60px_60px] [mask-image:radial-gradient(ellipse_80%_50%_at_50%_50%,black,transparent)] dark:bg-[linear-gradient(rgba(24,170,157,0.05)_1px,transparent_1px),linear-gradient(90deg,rgba(24,170,157,0.05)_1px,transparent_1px)]"></div>
</div>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10 pt-12 lg:pt-20">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 lg:gap-20 items-start">
{/* Enhanced Content */}
<motion.div
ref={ref}
initial="hidden"
animate={controls}
variants={containerVariants}
className="space-y-8 lg:space-y-10"
>
{!isTokenPresent && (
<motion.div
variants={itemVariants}
className="inline-flex items-center px-4 py-2 rounded-full bg-gradient-to-r from-primary/10 to-primary-500/10 border border-primary/20 text-primary text-sm font-medium mb-4"
>
<span className="relative flex h-2 w-2 mr-2">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-primary"></span>
</span>
{"Online"}
</motion.div>
)}
<motion.h1
variants={itemVariants}
className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold tracking-tight text-gray-900 dark:text-white leading-tight"
>
<span className="block bg-gradient-to-r from-gray-900 to-gray-700 dark:from-white dark:to-gray-300 bg-clip-text text-transparent">
{tenantConfig?.organizationName || t("landingPage.smartOffice")}
</span>
<span className="block text-transparent bg-clip-text bg-gradient-to-r from-primary via-primary-500 to-primary-500">
{t("landingPage.platformSystem")}
</span>
</motion.h1>
<motion.p
variants={itemVariants}
className="text-lg md:text-xl text-gray-600 dark:text-gray-300 max-w-lg leading-relaxed"
>
{welcomeMessage}
</motion.p>
<motion.div variants={itemVariants}>
<Dialog>
<DialogTrigger asChild>
<button className="group inline-flex items-center gap-3 rounded-full border border-primary/25 bg-white/75 px-5 py-3 text-sm font-semibold text-primary shadow-sm shadow-primary/10 backdrop-blur-md transition-all duration-300 hover:-translate-y-0.5 hover:border-primary/45 hover:bg-primary/10 hover:shadow-lg hover:shadow-primary/15 focus:outline-none focus:ring-2 focus:ring-primary/40 focus:ring-offset-2 dark:bg-gray-900/60 dark:hover:bg-primary/15 dark:focus:ring-offset-gray-900">
<span className="flex size-9 items-center justify-center rounded-full bg-primary/10 text-primary transition-colors duration-300 group-hover:bg-primary group-hover:text-white">
<BookOpenText className="size-4" />
</span>
<span>{t("newssection.readMore")}</span>
<ArrowRight className="size-4 transition-transform duration-300 group-hover:translate-x-1" />
</button>
</DialogTrigger>
<DialogContent className="max-h-[86vh] max-w-3xl overflow-hidden rounded-2xl border border-primary/15 bg-white/95 p-0 shadow-2xl shadow-primary/10 backdrop-blur-xl dark:border-primary/20 dark:bg-gray-900/95">
<div className="h-1.5 bg-gradient-to-r from-primary via-primary-500 to-primary" />
<DialogHeader className="relative gap-0 px-6 pb-5 pt-6 text-left sm:px-8">
<div className="absolute right-8 top-8 hidden h-24 w-24 rounded-full bg-primary/10 blur-2xl sm:block" />
<div className="relative flex items-start gap-4">
{tenantConfig?.logo ? (
<div className="relative group inline-flex shrink-0">
<div className="absolute inset-0 rounded-2xl bg-gradient-to-r from-primary/20 to-secondary/20 blur-xl opacity-0 transition-all duration-500 scale-110 group-hover:opacity-100" />
<motion.div
whileHover={{ scale: 1.04 }}
whileTap={{ scale: 0.97 }}
transition={{
type: "spring",
stiffness: 220,
damping: 16,
}}
className="relative flex h-16 w-24 items-center justify-center overflow-hidden rounded-2xl border-none bg-white/5 shadow-md dark:bg-slate-900/40"
title={heroTitle}
>
<motion.img
src={tenantConfig.logo}
alt={`${heroTitle} logo`}
className="h-full w-full object-contain drop-shadow-lg transition-transform duration-300 group-hover:scale-105"
onError={(e) => {
const fallback =
e.currentTarget.parentElement?.querySelector(
".modal-logo-fallback",
);
if (fallback) {
fallback.classList.remove("hidden");
fallback.classList.add("flex");
}
e.currentTarget.remove();
}}
/>
<div className="modal-logo-fallback absolute inset-0 hidden items-center justify-center rounded-2xl bg-gradient-to-br from-primary to-primary-500 text-xl font-bold text-white">
{organizationInitials || "SO"}
</div>
<div className="absolute inset-0 -translate-x-full bg-gradient-to-r from-transparent via-white/15 to-transparent transition-transform duration-1000 group-hover:translate-x-full" />
</motion.div>
</div>
) : (
<div className="relative shrink-0">
<div className="absolute inset-0 rounded-2xl bg-primary/20 blur-xl scale-110" />
<div className="relative flex h-16 w-24 items-center justify-center overflow-hidden rounded-2xl bg-gradient-to-br from-primary to-primary-500 text-xl font-bold text-white shadow-lg shadow-primary/25 ring-1 ring-white/20">
<span className="drop-shadow-sm">
{organizationInitials || "SO"}
</span>
<div className="absolute inset-x-0 top-0 h-1/2 bg-white/15" />
</div>
</div>
)}
<div className="min-w-0 pt-1">
<DialogTitle className="text-2xl font-bold leading-tight text-gray-950 dark:text-white sm:text-3xl">
{heroTitle}
</DialogTitle>
<DialogDescription className="sr-only">
{welcomeMessage}
</DialogDescription>
<div className="mt-3 h-1 w-20 rounded-full bg-gradient-to-r from-primary to-primary-500" />
</div>
</div>
</DialogHeader>
<div className="border-t border-gray-100 bg-gradient-to-b from-gray-50/80 to-white px-6 py-6 dark:border-gray-800 dark:from-gray-950/40 dark:to-gray-900 sm:px-8">
<div className="rounded-xl border border-gray-100 bg-white p-5 text-base leading-8 text-gray-700 shadow-sm dark:border-gray-800 dark:bg-gray-900/80 dark:text-gray-300 sm:p-6 sm:text-lg">
<p className="whitespace-pre-line">{welcomeMessage}</p>
</div>
</div>
</DialogContent>
</Dialog>
</motion.div>
<motion.div
variants={itemVariants}
className="flex flex-col sm:flex-row flex-wrap gap-4 pt-2"
>
{moduleConfig.complaint && (
<motion.button
type="button"
whileHover={{
scale: 1.05,
boxShadow: `0 20px 40px ${primary}4d`,
}}
whileTap={{ scale: 0.95 }}
onClick={() => navigate("/complaints")}
className="group relative px-8 py-4 rounded-xl bg-gradient-to-r from-primary to-primary-500 text-white font-semibold text-lg shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 overflow-hidden"
>
<div className="absolute inset-0 bg-gradient-to-r from-white/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<span className="relative flex items-center justify-center">
{t("complaint.fayda.submitComplaint")}
<ShieldCheck className="w-5 h-5 ml-2" />
</span>
</motion.button>
)}
{!isTokenPresent && (
<motion.a
whileHover={{
scale: 1.05,
boxShadow: `0 20px 40px ${primary}4d`,
}}
whileTap={{ scale: 0.95 }}
href="/login"
className="group relative px-8 py-4 rounded-xl bg-gradient-to-r from-primary to-primary-500 text-white font-semibold text-lg shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 overflow-hidden"
>
<div className="absolute inset-0 bg-gradient-to-r from-white/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<span className="relative flex items-center justify-center">
{t("landingPage.signIn")}
<svg
className="ml-2 w-4 h-4 group-hover:translate-x-1 transition-transform"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M13 7l5 5m0 0l-5 5m5-5H6"
/>
</svg>
</span>
</motion.a>
)}
<motion.a
whileHover={{
scale: 1.05,
backgroundColor: "rgba(24, 170, 157, 0.08)",
}}
whileTap={{ scale: 0.95 }}
href="#features"
className="group px-8 py-4 rounded-xl border-2 border-primary text-primary font-semibold text-lg hover:shadow-lg transition-all duration-300 flex items-center justify-center"
>
{t("landingPage.learnMore")}
<svg
className="ml-2 w-4 h-4 group-hover:translate-y-0.5 transition-transform"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 14l-7 7m0 0l-7-7m7 7V3"
/>
</svg>
</motion.a>
</motion.div>
{/* Stats */}
<motion.div
variants={itemVariants}
className="flex flex-wrap gap-8 pt-5 pb-10"
>
<div className="text-center">
<div className="text-2xl font-bold text-gray-900 dark:text-white">
95%
</div>
<div className="text-sm text-gray-600 dark:text-gray-400 mt-1">
{t("landingPage.uptime")}
</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-gray-900 dark:text-white">
25+
</div>
<div className="text-sm text-gray-600 dark:text-gray-400 mt-1">
{t("landingPage.bureaus")}
</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-gray-900 dark:text-white">
24/7
</div>
<div className="text-sm text-gray-600 dark:text-gray-400 mt-1">
{t("landingPage.support")}
</div>
</div>
</motion.div>
</motion.div>
{/* Enhanced Card */}
<motion.div
initial="hidden"
animate={controls}
variants={cardVariants}
className="relative"
>
<div className="absolute -inset-4 bg-gradient-to-r from-primary to-primary-500 rounded-3xl opacity-20 blur-xl animate-pulse-slow"></div>
<div className="absolute -inset-2 bg-gradient-to-r from-primary to-primary-500 rounded-2xl opacity-10 blur-lg"></div>
<motion.div
whileHover={{ y: -5, rotateX: 5 }}
transition={{ type: "spring", stiffness: 300, damping: 20 }}
className="relative h-full bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm rounded-2xl shadow-2xl border border-white/20 dark:border-gray-700 overflow-hidden"
>
{/* Card Header */}
<div className="absolute top-0 left-0 right-0 h-2 bg-gradient-to-r from-primary to-primary-500"></div>
<div className="p-8 lg:p-10 h-full flex flex-col items-center justify-center bg-gradient-to-br from-white/90 to-gray-50/80 dark:from-gray-800/90 dark:to-gray-700/80">
<div className="text-center space-y-6">
{/* Logo/Brand */}
<div className="mb-6">
<div
className="w-16 h-16 mx-auto mb-4 rounded-2xl shadow-lg flex items-center justify-center"
style={{
background: `linear-gradient(135deg, ${primary}, ${secondary})`,
}}
>
<span className="text-white font-bold text-xl">SO</span>
</div>
</div>
<div className="text-4xl lg:text-5xl font-bold mb-2 tracking-tight text-slate-900 dark:text-white">
{t("landingPage.sops")}
</div>
<div className="text-lg font-semibold text-gray-800 dark:text-gray-200">
{tenantConfig.appName}
</div>
<div className="text-gray-600 dark:text-gray-400 max-w-md leading-relaxed">
{t("landingPage.revolutionDescription")}
</div>
{/* Features List */}
<div className="grid grid-cols-2 gap-4 pt-4">
{[
"features1",
"features2",
"features3",
"features4",
"features5",
"features6",
].map((feature, index) => (
<motion.div
key={feature}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.8 + index * 0.1 }}
className="flex items-center text-sm text-gray-600 dark:text-gray-400"
>
<div
className="w-2 h-2 rounded-full mr-2"
style={{ backgroundColor: primary }}
></div>
{t(`landingPage.${feature}`)}
</motion.div>
))}
</div>
<div className="pt-6">
<motion.div
whileHover={{ scale: 1.05 }}
className="inline-flex items-center px-6 py-3 rounded-full font-semibold backdrop-blur-sm"
style={{
background: `linear-gradient(to right, ${primary}1a, ${secondary}1a)`,
color: primary,
border: `1px solid ${primary}33`,
}}
>
<span className="relative flex h-3 w-3 mr-3">
<span
className="animate-ping absolute inline-flex h-full w-full rounded-full opacity-75"
style={{ backgroundColor: primary }}
></span>
<span
className="relative inline-flex rounded-full h-3 w-3"
style={{ backgroundColor: primary }}
></span>
</span>
{t("landingPage.liveDemo")}
</motion.div>
</div>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
{/* Enhanced Floating Elements */}
<motion.div
variants={floatingElementVariants}
animate="float"
className="hidden lg:block absolute bottom-20 left-20"
>
<div className="w-12 h-12 rounded-2xl bg-gradient-to-br from-primary/20 to-primary-500/20 border border-primary/10 backdrop-blur-sm rotate-45"></div>
</motion.div>
<motion.div
variants={floatingElementVariants}
animate="float"
transition={{ delay: 1 }}
className="hidden lg:block absolute top-32 right-32"
>
<div className="w-16 h-16 rounded-full bg-gradient-to-br from-primary-500/15 to-primary/15 border border-primary-500/10 backdrop-blur-sm"></div>
</motion.div>
<motion.div
variants={floatingElementVariants}
animate="float"
transition={{ delay: 2 }}
className="hidden lg:block absolute top-1/2 left-1/4"
>
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-primary-700/10 to-primary-500/10 border border-primary-700/10 backdrop-blur-sm rotate-12"></div>
</motion.div>
{/* Scroll Indicator */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 2 }}
className="absolute bottom-8 left-1/2 transform -translate-x-1/2 hidden lg:block"
>
<motion.div
animate={{ y: [0, 10, 0] }}
transition={{ duration: 2, repeat: Infinity }}
className="w-6 h-10 border-2 border-gray-300 rounded-full flex justify-center"
>
<motion.div
animate={{ y: [0, 12, 0] }}
transition={{ duration: 2, repeat: Infinity }}
className="w-1 h-3 bg-gray-400 rounded-full mt-2"
></motion.div>
</motion.div>
</motion.div>
<style>
{`
@keyframes orb-slow {
0%, 100% {
transform: translate(0px, 0px) scale(1);
}
33% {
transform: translate(40px, -60px) scale(1.1);
}
66% {
transform: translate(-30px, 30px) scale(0.9);
}
}
@keyframes orb-medium {
0%, 100% {
transform: translate(0px, 0px) scale(1);
}
33% {
transform: translate(-50px, 40px) scale(1.05);
}
66% {
transform: translate(20px, -20px) scale(0.95);
}
}
@keyframes orb-fast {
0%, 100% {
transform: translate(0px, 0px) scale(1);
}
50% {
transform: translate(20px, -40px) scale(1.08);
}
}
.animate-orb-slow {
animation: orb-slow 15s infinite ease-in-out;
}
.animate-orb-medium {
animation: orb-medium 12s infinite ease-in-out;
}
.animate-orb-fast {
animation: orb-fast 10s infinite ease-in-out;
}
@keyframes pulse-slow {
0%, 100% {
opacity: 0.2;
}
50% {
opacity: 0.3;
}
}
.animate-pulse-slow {
animation: pulse-slow 4s infinite ease-in-out;
}
`}
</style>
</section>
);
};
export default HeroSection;