update banner, agent
This commit is contained in:
parent
7d3ac22960
commit
a9b4aab886
|
|
@ -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>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
Loading…
Reference in New Issue