update banner, agent

This commit is contained in:
Anang Yusman 2026-02-02 16:08:30 +08:00
parent 7d3ac22960
commit a9b4aab886
7 changed files with 176 additions and 136 deletions

View File

@ -2,28 +2,54 @@
import Image from "next/image"; import Image from "next/image";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { useEffect, useState } from "react";
import { getAgentData } from "@/service/agent";
const agents = [ type Agent = {
{ id: number;
name: "Johny Nugroho", name: string;
title: "Branch Manager Jaecoo Cihampelas Bandung", job_title: string;
image: "/johny.png", status_id: number;
}, profile_picture_url: string;
{ created_at: string;
name: "Basuki Pamungkas", };
title: "Spv Jaecoo Cihampelas Bandung",
image: "/basuki.png",
},
{
name: "Deni Tihayar",
title: "Spv Jaecoo Cihampelas Bandung",
image: "/deni.png",
},
];
export default function Agent() { export default function Agent() {
const [agents, setAgents] = useState<Agent[]>([]);
useEffect(() => {
const fetchAgents = async () => {
try {
const req = {
limit: "10",
page: 1,
search: "",
};
const res = await getAgentData(req);
const agentsData: Agent[] = res?.data?.data || [];
const latestApprovedAgents = agentsData
.filter((agent) => agent.status_id === 2) // ✅ approved only
.sort(
(a, b) =>
new Date(b.created_at).getTime() -
new Date(a.created_at).getTime(),
) // ✅ newest first
.slice(0, 5); // ✅ max 5
setAgents(latestApprovedAgents);
} catch (error) {
console.error("Failed to fetch agents:", error);
}
};
fetchAgents();
}, []);
return ( return (
<section className="py-16 px-6 md:px-12 bg-[#f9f9f9] text-center mt-0"> <section className="py-16 px-6 md:px-5 bg-[#FAFDFF] text-center mt-0">
<motion.h2 <motion.h2
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@ -34,20 +60,10 @@ export default function Agent() {
Our Teams Our Teams
</motion.h2> </motion.h2>
<motion.p <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-2 place-items-center mt-10">
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
className="text-gray-600 mb-10 text-lg"
>
Temui anggota tim kami yang luar biasa
</motion.p>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6 place-items-center">
{agents.map((agent, index) => ( {agents.map((agent, index) => (
<motion.div <motion.div
key={index} key={agent.id}
initial={{ opacity: 0, y: 40 }} initial={{ opacity: 0, y: 40 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
transition={{ transition={{
@ -56,19 +72,21 @@ export default function Agent() {
ease: "easeOut", ease: "easeOut",
}} }}
viewport={{ once: true, amount: 0.3 }} viewport={{ once: true, amount: 0.3 }}
className="bg-white shadow-md px-2 py-4 gap-4 flex flex-col items-center h-[300px] w-[224px]" className="bg-white shadow-md py-4 gap-2 flex flex-col items-center h-[300px] w-[250px]"
> >
<div className="relative w-28 h-36 mb-3"> <div className="relative w-44 h-48 mb-3">
<Image <Image
src={agent.image} src={agent.profile_picture_url}
alt={agent.name} alt={agent.name}
fill fill
className="rounded-full object-cover" className="rounded-full object-cover"
/> />
</div> </div>
<h3 className="text-lg text-gray-900 text-center">{agent.name}</h3> <h3 className="text-lg text-gray-900 text-center">{agent.name}</h3>
<p className="text-xs text-gray-600 text-center mt-1">
{agent.title} <p className="text-sm text-gray-600 text-center mt-1">
{agent.job_title}
</p> </p>
</motion.div> </motion.div>
))} ))}

View File

@ -6,7 +6,7 @@ export default function Footer() {
<div className="flex flex-col md:flex-row gap-10"> <div className="flex flex-col md:flex-row gap-10">
<div className="w-full md:w-4/12"> <div className="w-full md:w-4/12">
<Image <Image
src="/masjaecoo.png" src="/jaecoobot.png"
alt="Jaecoo" alt="Jaecoo"
width={300} width={300}
height={200} height={200}

View File

@ -1,4 +1,5 @@
"use client"; "use client";
import Image from "next/image"; import Image from "next/image";
import { import {
Carousel, Carousel,
@ -19,131 +20,111 @@ import {
} from "../ui/dialog"; } from "../ui/dialog";
import { Input } from "../ui/input"; import { Input } from "../ui/input";
import { Textarea } from "../ui/textarea"; import { Textarea } from "../ui/textarea";
import { useState } from "react"; import { useEffect, useState } from "react";
import Autoplay from "embla-carousel-autoplay"; // ✅ Import plugin autoplay import Autoplay from "embla-carousel-autoplay"; // ✅ Import plugin autoplay
import { useRef } from "react"; import { useRef } from "react";
import { getBannerData } from "@/service/banner";
const heroImages = ["/Hero.png", "/hero-bdg2.png", "/hero-bdg3.png"]; const heroImages = [
"/Hero.png",
// "/Carousell-01.png",
"/Carousell-02.png",
"/Carousell-03.png",
"/Carousell-04.png",
"/Carousell-05.png",
];
type Banner = {
id: number;
title: string;
description: string;
status_id: number;
position: string;
thumbnail_url: string;
created_at: string;
};
export default function Header() { export default function Header() {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [banners, setBanners] = useState<Banner[]>([]);
useEffect(() => {
const fetchBanners = async () => {
try {
const req = {
limit: "10",
page: 1,
search: "",
};
const res = await getBannerData(req);
const bannerData: Banner[] = res?.data?.data || [];
const activeBanners = bannerData
.filter((banner) => banner.status_id === 2) // ✅ approved only
.sort((a, b) => {
const posA = Number(a.position);
const posB = Number(b.position);
if (posA !== posB) return posA - posB;
return (
new Date(b.created_at).getTime() -
new Date(a.created_at).getTime()
);
});
setBanners(activeBanners);
} catch (err) {
console.error("Failed to fetch banners:", err);
}
};
fetchBanners();
}, []);
// ✅ Gunakan useRef untuk plugin autoplay
const plugin = useRef(Autoplay({ delay: 4000, stopOnInteraction: false })); const plugin = useRef(Autoplay({ delay: 4000, stopOnInteraction: false }));
return ( return (
<section className="relative w-full overflow-hidden bg-white"> <section className="relative w-full overflow-hidden bg-white">
<Carousel <Carousel className="w-full relative" plugins={[plugin.current]}>
className="w-full relative"
plugins={[plugin.current]} // ✅ Tambahkan plugin di sini
>
<CarouselContent> <CarouselContent>
{heroImages.map((img, index) => ( {banners.map((banner, index) => (
<CarouselItem key={index}> <CarouselItem key={banner.id}>
<div className="relative w-full h-[400px] sm:h-[500px] md:h-[810px]"> <div className="relative w-full h-[400px] sm:h-[500px] md:h-[810px]">
<Image <Image
src={img} src={banner.thumbnail_url}
alt={`JAECOO Image ${index + 1}`} alt={banner.title}
width={1400} fill
height={810} priority={index === 0}
className="object-cover w-full h-full" className="object-cover"
/> />
{index === 0 && ( {/* {index === 0 && (
<div className="absolute inset-0 flex flex-col justify-center items-start px-4 sm:px-8 md:px-28 z-10"> <div className="absolute inset-0 flex flex-col justify-center items-start px-4 sm:px-8 md:px-28 z-10">
<motion.h1 <motion.h1
initial={{ opacity: 0, y: 40 }} initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, ease: "easeOut" }} transition={{ duration: 0.8 }}
className="text-2xl sm:text-3xl md:text-5xl font-bold text-black mb-4" className="text-2xl sm:text-3xl md:text-5xl font-bold text-black mb-4"
> >
JAECOO J7 SHS-P {banner.title}
</motion.h1> </motion.h1>
<motion.p {banner.description && (
initial={{ opacity: 0, y: 40 }} <motion.p
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.9,
ease: "easeOut",
delay: 0.2,
}}
className="text-sm sm:text-base md:text-lg text-black mb-6"
>
DELICATE OFF-ROAD SUV
</motion.p>
<motion.div
className="flex flex-col sm:flex-row items-start sm:items-center gap-3 sm:gap-4"
initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1, ease: "easeOut", delay: 0.4 }}
>
<motion.div
className="flex items-center gap-4"
initial={{ opacity: 0, y: 40 }} initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ transition={{ duration: 0.9, delay: 0.2 }}
duration: 1, className="text-sm sm:text-base md:text-lg text-black mb-6"
ease: "easeOut",
delay: 0.4,
}}
> >
<Dialog open={open} onOpenChange={setOpen}> {banner.description}
<DialogTrigger asChild> </motion.p>
<Button className="bg-[#1F6779] text-white h-[30px] md:h-[40px] rounded-full hover:cursor-pointer"> )}
TEST DRIVE
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[1400px] h-[600px]">
<div className="flex items-center gap-4">
<Image
src="/masjaecoonav.png"
alt="MAS JAECOO Logo"
width={300}
height={30}
className=" object-fill"
/>
</div>
<DialogHeader>
<DialogTitle className="text-2xl text-center mb-4">
FORM TEST DRIVE
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 px-10">
<Input placeholder="Nama" />
<Input placeholder="Email" />
<Input placeholder="Mobile Number" />
<Input placeholder="Location" />
</div>
<div className="mt-3 px-10">
<Textarea placeholder="Full Message" rows={4} />
</div>
<div className="mt-6 text-left ml-10">
<Button
onClick={() => setOpen(false)}
className="bg-[#1F6779] text-white rounded-full"
>
SEND INQUIRY
</Button>
</div>
</DialogContent>
</Dialog>
<Link href={"/product"}>
<Button
variant="outline"
className="rounded-full border-black text-black px-6 py-2 hover:cursor-pointer hover:bg-amber-50"
>
EXPLORE
</Button>
</Link>
</motion.div>
</motion.div>
</div> </div>
)} )} */}
</div> </div>
</CarouselItem> </CarouselItem>
))} ))}

View File

@ -115,7 +115,7 @@ export default function Navbar() {
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<Link href="/" className="flex items-center space-x-2"> <Link href="/" className="flex items-center space-x-2">
<Image <Image
src="/masjaecoonav.png" src="/jaecoonew.png"
alt="MAS JAECOO Logo" alt="MAS JAECOO Logo"
width={300} width={300}
height={30} height={30}

View File

@ -1,18 +1,59 @@
"use client"; "use client";
import { getBannerData } from "@/service/banner";
import Image from "next/image"; import Image from "next/image";
import { useEffect, useState } from "react";
type Banner = {
id: number;
title: string;
status_id: number;
thumbnail_url: string;
};
export default function Video() { export default function Video() {
const [banner, setBanner] = useState<Banner | null>(null);
useEffect(() => {
const fetchRandomBanner = async () => {
try {
const req = {
limit: "20", // ambil agak banyak biar random lebih terasa
page: 1,
search: "",
};
const res = await getBannerData(req);
const banners: Banner[] = res?.data?.data || [];
const approvedBanners = banners.filter((item) => item.status_id === 2);
if (approvedBanners.length === 0) return;
const randomBanner =
approvedBanners[Math.floor(Math.random() * approvedBanners.length)];
setBanner(randomBanner);
} catch (error) {
console.error("Failed to fetch random banner:", error);
}
};
fetchRandomBanner();
}, []);
return ( return (
<section className="pt-10 bg-white"> <section className="pt-10 bg-white">
<div className="relative mb-10 w-full h-[250px] sm:h-[400px] md:h-[600px]"> <div className="relative mb-10 w-full h-[250px] sm:h-[600px] md:h-[800px]">
<Image <Image
src={"/maintenance.png"} src={banner?.thumbnail_url || "/maintenance.png"}
alt="maintenance" alt={banner?.title || "Banner"}
fill fill
className="object-cover" className="object-cover"
priority priority
/> />
s
</div> </div>
<div className="relative w-full h-[500px] overflow-hidden"> <div className="relative w-full h-[500px] overflow-hidden">

BIN
public/jaecoobot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
public/jaecoonew.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB