This commit is contained in:
Anang Yusman 2025-09-02 11:22:35 +08:00
parent 902f734d68
commit 95b6acccd9
12 changed files with 872 additions and 639 deletions

View File

@ -10,7 +10,10 @@ import withReactContent from "sweetalert2-react-content";
import { useDropzone } from "react-dropzone"; import { useDropzone } from "react-dropzone";
import { close, error, loading } from "@/config/swal"; import { close, error, loading } from "@/config/swal";
import useDisclosure from "@/components/useDisclosure"; import useDisclosure from "@/components/useDisclosure";
import { createAdvertise } from "@/service/advertisement"; import {
createAdvertise,
createMediaFileAdvertise,
} from "@/service/advertisement";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Dialog, Dialog,
@ -72,24 +75,32 @@ export default function AdvertisePage() {
const onSubmit = async (values: z.infer<typeof createArticleSchema>) => { const onSubmit = async (values: z.infer<typeof createArticleSchema>) => {
loading(); loading();
const formData = {
title: values.title, const payload = {
description: values.description, Title: values.title,
placement: placement, Description: values.description,
redirectLink: values.url, Placement: placement,
RedirectLink: values.url,
}; };
const res = await createAdvertise(formData);
const res = await createAdvertise(payload);
if (res?.error) { if (res?.error) {
error(res?.message); error(res?.message);
return false; return false;
} }
// const idNow = res?.data?.data?.id; const idNow = res?.data?.data?.id;
if (files.length > 0) { if (files.length > 0 && idNow) {
const formFiles = new FormData(); const formFiles = new FormData();
formFiles.append("file", files[0]); formFiles.append("file", files[0]);
// const resFile = await createMediaFileAdvertise(idNow, formFiles); const resFile = await createMediaFileAdvertise(idNow, formFiles);
if (resFile?.error) {
error(resFile?.message);
return false;
}
} }
close(); close();

View File

@ -5,6 +5,7 @@ import Latest from "@/components/landing-page/latest";
import LatestandPopular from "@/components/landing-page/latest-and-popular"; import LatestandPopular from "@/components/landing-page/latest-and-popular";
import Navbar from "@/components/landing-page/navbar"; import Navbar from "@/components/landing-page/navbar";
import News from "@/components/landing-page/news"; import News from "@/components/landing-page/news";
import { id } from "date-fns/locale";
import Image from "next/image"; import Image from "next/image";
export default function Home() { export default function Home() {
@ -27,7 +28,7 @@ export default function Home() {
<div className="flex-1"> <div className="flex-1">
<Header /> <Header />
</div> </div>
<Latest /> <Latest id={2} />
<News /> <News />
<Author /> <Author />
<LatestandPopular /> <LatestandPopular />

View File

@ -101,6 +101,9 @@ export default function Login() {
Cookies.set("username", profile?.data?.data?.username, { Cookies.set("username", profile?.data?.data?.username, {
expires: 1, expires: 1,
}); });
Cookies.set("fullname", profile?.data?.data?.fullname, {
expires: 1,
});
Cookies.set("urie", profile?.data?.data?.roleId, { Cookies.set("urie", profile?.data?.data?.roleId, {
expires: 1, expires: 1,
}); });
@ -192,16 +195,18 @@ export default function Login() {
<div className="text-center"> <div className="text-center">
<Link href={"/"}> <Link href={"/"}>
<div className="bg-white/10 backdrop-blur-sm rounded-2xl p-8 shadow-2xl border border-white/20"> <div className="bg-white/10 backdrop-blur-sm rounded-2xl p-8 shadow-2xl border border-white/20">
<img <img
src="/mikul.png" src="/mikul.png"
alt="Mikul News Logo" alt="Mikul News Logo"
className="max-w-xs h-auto drop-shadow-lg" className="max-w-xs h-auto drop-shadow-lg"
/> />
</div> </div>
</Link> </Link>
<div className="mt-8 text-white/90"> <div className="mt-8 text-white/90">
<h2 className="text-2xl font-bold mb-2">Portal Mikul News</h2> <h2 className="text-2xl font-bold mb-2">Portal Mikul News</h2>
<p className="text-sm opacity-80">Platform berita terpercaya untuk informasi terkini</p> <p className="text-sm opacity-80">
Platform berita terpercaya untuk informasi terkini
</p>
</div> </div>
</div> </div>
</div> </div>
@ -216,9 +221,9 @@ export default function Login() {
{/* Mobile Logo */} {/* Mobile Logo */}
<div className="lg:hidden text-center mb-8"> <div className="lg:hidden text-center mb-8">
<Link href={"/"}> <Link href={"/"}>
<img <img
src="/mikul.png" src="/mikul.png"
alt="Mikul News Logo" alt="Mikul News Logo"
className="h-12 mx-auto" className="h-12 mx-auto"
/> />
</Link> </Link>
@ -228,17 +233,32 @@ export default function Login() {
<div className="bg-white rounded-2xl shadow-xl p-8 border border-gray-100"> <div className="bg-white rounded-2xl shadow-xl p-8 border border-gray-100">
<div className="text-center mb-8"> <div className="text-center mb-8">
<div className="w-16 h-16 bg-orange-100 rounded-full flex items-center justify-center mx-auto mb-4"> <div className="w-16 h-16 bg-orange-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-orange-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" /> className="w-8 h-8 text-orange-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
/>
</svg> </svg>
</div> </div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">Setup Akun</h2> <h2 className="text-2xl font-bold text-gray-900 mb-2">
Setup Akun
</h2>
<p className="text-gray-600">Lengkapi informasi email Anda</p> <p className="text-gray-600">Lengkapi informasi email Anda</p>
</div> </div>
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<Label htmlFor="old-email" className="text-sm font-medium text-gray-700 mb-2 block"> <Label
htmlFor="old-email"
className="text-sm font-medium text-gray-700 mb-2 block"
>
Email Lama Email Lama
</Label> </Label>
<Input <Input
@ -253,7 +273,10 @@ export default function Login() {
</div> </div>
<div> <div>
<Label htmlFor="new-email" className="text-sm font-medium text-gray-700 mb-2 block"> <Label
htmlFor="new-email"
className="text-sm font-medium text-gray-700 mb-2 block"
>
Email Baru Email Baru
</Label> </Label>
<Input <Input
@ -280,29 +303,60 @@ export default function Login() {
<div className="bg-white rounded-2xl shadow-xl p-8 border border-gray-100"> <div className="bg-white rounded-2xl shadow-xl p-8 border border-gray-100">
<div className="text-center"> <div className="text-center">
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4"> <div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> className="w-8 h-8 text-blue-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg> </svg>
</div> </div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">Verifikasi OTP</h2> <h2 className="text-2xl font-bold text-gray-900 mb-2">
<p className="text-gray-600">Masukkan kode OTP yang telah dikirim</p> Verifikasi OTP
</h2>
<p className="text-gray-600">
Masukkan kode OTP yang telah dikirim
</p>
</div> </div>
</div> </div>
) : isResetPassword ? ( ) : isResetPassword ? (
<div className="bg-white rounded-2xl shadow-xl p-8 border border-gray-100"> <div className="bg-white rounded-2xl shadow-xl p-8 border border-gray-100">
<div className="text-center mb-8"> <div className="text-center mb-8">
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4"> <div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" /> className="w-8 h-8 text-red-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
/>
</svg> </svg>
</div> </div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">Reset Password</h2> <h2 className="text-2xl font-bold text-gray-900 mb-2">
<p className="text-gray-600">Masukkan username untuk reset password</p> Reset Password
</h2>
<p className="text-gray-600">
Masukkan username untuk reset password
</p>
</div> </div>
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<Label htmlFor="reset-username" className="text-sm font-medium text-gray-700 mb-2 block"> <Label
htmlFor="reset-username"
className="text-sm font-medium text-gray-700 mb-2 block"
>
Username Username
</Label> </Label>
<Input <Input
@ -312,9 +366,15 @@ export default function Login() {
placeholder="Masukkan username" placeholder="Masukkan username"
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-red-500 transition-colors" className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-red-500 transition-colors"
value={checkUsernameValue} value={checkUsernameValue}
onChange={(e) => setCheckUsernameValue(e.target.value.trim())} onChange={(e) =>
onPaste={(e) => setCheckUsernameValue(e.currentTarget.value.trim())} setCheckUsernameValue(e.target.value.trim())
onCopy={(e) => setCheckUsernameValue(e.currentTarget.value.trim())} }
onPaste={(e) =>
setCheckUsernameValue(e.currentTarget.value.trim())
}
onCopy={(e) =>
setCheckUsernameValue(e.currentTarget.value.trim())
}
/> />
</div> </div>
@ -348,17 +408,34 @@ export default function Login() {
<div className="bg-white rounded-2xl shadow-xl p-8 border border-gray-100"> <div className="bg-white rounded-2xl shadow-xl p-8 border border-gray-100">
<div className="text-center mb-8"> <div className="text-center mb-8">
<div className="w-16 h-16 bg-emerald-100 rounded-full flex items-center justify-center mx-auto mb-4"> <div className="w-16 h-16 bg-emerald-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1" /> className="w-8 h-8 text-emerald-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"
/>
</svg> </svg>
</div> </div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">Selamat Datang</h2> <h2 className="text-2xl font-bold text-gray-900 mb-2">
<p className="text-gray-600">Portal Mikul News - Platform berita terpercaya</p> Selamat Datang
</h2>
<p className="text-gray-600">
Portal Mikul News - Platform berita terpercaya
</p>
</div> </div>
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<Label htmlFor="username" className="text-sm font-medium text-gray-700 mb-2 block"> <Label
htmlFor="username"
className="text-sm font-medium text-gray-700 mb-2 block"
>
Username Username
</Label> </Label>
<Input <Input
@ -369,13 +446,18 @@ export default function Login() {
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 transition-colors" className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 transition-colors"
value={username} value={username}
onChange={(e) => setValUsername(e.target.value.trim())} onChange={(e) => setValUsername(e.target.value.trim())}
onPaste={(e) => setValUsername(e.currentTarget.value.trim())} onPaste={(e) =>
setValUsername(e.currentTarget.value.trim())
}
onCopy={(e) => setValUsername(e.currentTarget.value.trim())} onCopy={(e) => setValUsername(e.currentTarget.value.trim())}
/> />
</div> </div>
<div> <div>
<Label htmlFor="password" className="text-sm font-medium text-gray-700 mb-2 block"> <Label
htmlFor="password"
className="text-sm font-medium text-gray-700 mb-2 block"
>
Password Password
</Label> </Label>
<div className="relative"> <div className="relative">

View File

@ -4,6 +4,7 @@ import Image from "next/image";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import news from "../news"; import news from "../news";
import Link from "next/link";
type Article = { type Article = {
id: number; id: number;
@ -90,39 +91,44 @@ export default function DevelopmentNews() {
{/* Left Content */} {/* Left Content */}
<div className="lg:col-span-2 space-y-10"> <div className="lg:col-span-2 space-y-10">
{articles.map((item) => ( {articles.map((item) => (
<div key={item.id} className="flex flex-col md:flex-row gap-6"> <div key={item.id}>
{/* Image + Category */} <Link
<div className="relative w-full md:w-1/2 h-64"> className="flex flex-col md:flex-row gap-6"
<Image href={`/detail/${item?.id}`}
src={item.thumbnailUrl || "/placeholder.png"} >
alt={item.title} {/* Image + Category */}
fill <div className="relative w-full md:w-1/2 h-64">
className="object-cover rounded" <Image
/> src={item.thumbnailUrl || "/placeholder.png"}
<span className="absolute top-3 left-3 bg-yellow-400 text-black px-3 py-1 text-xs font-bold"> alt={item.title}
{item.categories[0]?.title || categoryLabel} fill
</span> className="object-cover rounded"
</div> />
<span className="absolute top-3 left-3 bg-yellow-400 text-black px-3 py-1 text-xs font-bold">
{/* Content */} {item.categories[0]?.title || categoryLabel}
<div className="flex-1"> </span>
<h2 className="text-xl font-bold text-[#16324F] hover:text-green-600 cursor-pointer">
{item.title}
</h2>
<div className="text-sm text-gray-600 mt-2">
BY{" "}
<span className="text-green-600 font-semibold">
{item.createdByName || "Admin"}
</span>{" "}
{new Date(item.createdAt).toLocaleDateString("id-ID")}
</div> </div>
<p className="mt-3 text-gray-700">
{truncateText(item.description, 20)} {/* Content */}
</p> <div className="flex-1">
<button className="mt-4 px-4 py-2 border border-gray-400 text-gray-700 hover:bg-black hover:text-white transition"> <h2 className="text-xl font-bold text-[#16324F] hover:text-green-600 cursor-pointer">
READ MORE {item.title}
</button> </h2>
</div> <div className="text-sm text-gray-600 mt-2">
BY{" "}
<span className="text-green-600 font-semibold">
{item.createdByName || "Admin"}
</span>{" "}
{new Date(item.createdAt).toLocaleDateString("id-ID")}
</div>
<p className="mt-3 text-gray-700">
{truncateText(item.description, 20)}
</p>
<button className="mt-4 px-4 py-2 border border-gray-400 text-gray-700 hover:bg-black hover:text-white transition">
READ MORE
</button>
</div>
</Link>
</div> </div>
))} ))}
@ -198,19 +204,24 @@ export default function DevelopmentNews() {
<div className="mt-4 space-y-4"> <div className="mt-4 space-y-4">
{articles.map((item) => ( {articles.map((item) => (
<div key={item.id} className="flex gap-3 items-center"> <div key={item.id} className="flex gap-3 items-center">
<Image <Link
src={item.thumbnailUrl || "/no-image.jpg"} className="flex gap-3 items-center"
alt={item.title} href={`/detail/${item?.id}`}
width={80} >
height={60} <Image
className="object-cover rounded" src={item.thumbnailUrl || "/no-image.jpg"}
/> alt={item.title}
<div> width={80}
<h3 className="font-semibold text-sm">{item.title}</h3> height={60}
<p className="text-xs text-gray-500"> className="object-cover rounded"
{new Date(item.createdAt).toLocaleDateString()} />
</p> <div>
</div> <h3 className="font-semibold text-sm">{item.title}</h3>
<p className="text-xs text-gray-500">
{new Date(item.createdAt).toLocaleDateString()}
</p>
</div>
</Link>
</div> </div>
))} ))}
</div> </div>
@ -220,93 +231,100 @@ export default function DevelopmentNews() {
</h2> </h2>
<div className=" w-full"> <div className=" w-full">
<div className="relative w-full aspect-video mb-5"> <div className="relative w-full aspect-video mb-5">
<Image <Link href={`/detail/${articles[0]?.id}`}>
src={ <Image
articles[0]?.thumbnailUrl || src={
articles[0]?.files?.[0]?.file_url || articles[0]?.thumbnailUrl ||
"/default-image.jpg" articles[0]?.files?.[0]?.file_url ||
} "/default-image.jpg"
alt={"articles[0]?.title"} }
fill alt={"articles[0]?.title"}
sizes="(max-width: 1024px) 100vw, 33vw" fill
className="object-cover" sizes="(max-width: 1024px) 100vw, 33vw"
/> className="object-cover"
<div className="absolute inset-0 bg-black/30" /> />
<div className="absolute bottom-0.5 left-2 text-white"> <div className="absolute inset-0 bg-black/30" />
<h3 className=" font-semibold text-base mb-1"> <div className="absolute bottom-0.5 left-2 text-white">
{articles[0]?.title} <h3 className=" font-semibold text-base mb-1">
</h3> {articles[0]?.title}
<p className=" text-xs mb-2 flex items-center gap-2"> </h3>
<svg <p className=" text-xs mb-2 flex items-center gap-2">
xmlns="http://www.w3.org/2000/svg" <svg
width="16" xmlns="http://www.w3.org/2000/svg"
height="16" width="16"
viewBox="0 0 24 24" height="16"
> viewBox="0 0 24 24"
<g fill="none"> >
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" /> <g fill="none">
<path <path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
fill="currentColor" <path
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1" fill="currentColor"
/> d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
</g> />
</svg>{" "} </g>
{new Date(articles[0]?.createdAt).toLocaleDateString( </svg>{" "}
"id-ID", {new Date(articles[0]?.createdAt).toLocaleDateString(
{ "id-ID",
day: "numeric", {
month: "long", day: "numeric",
year: "numeric", month: "long",
} year: "numeric",
)} }
</p> )}
</div> </p>
</div>
</Link>
</div> </div>
<div className="space-y-5"> <div className="space-y-5">
{articles?.slice(1, 4).map((article, index) => ( {articles?.slice(1, 4).map((article, index) => (
<div key={index} className="flex gap-3"> <div key={index}>
<div className="relative w-[120px] h-[86px] shrink-0"> <Link
<Image className="flex gap-3"
src={ href={`/detail/${article?.id}`}
article?.thumbnailUrl || >
article?.files?.[0]?.file_url || <div className="relative w-[120px] h-[86px] shrink-0">
"/default-image.jpg" <Image
} src={
alt={"article?.title"} article?.thumbnailUrl ||
fill article?.files?.[0]?.file_url ||
className="object-cover" "/default-image.jpg"
/>
</div>
<div>
<h4 className="text-sm font-semibold mb-3">
{article?.title}
</h4>
<p className="text-xs text-gray-500 flex gap-2 items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
>
<g fill="none">
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
<path
fill="currentColor"
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
/>
</g>
</svg>{" "}
{new Date(articles[0]?.createdAt).toLocaleDateString(
"id-ID",
{
day: "numeric",
month: "long",
year: "numeric",
} }
)} alt={"article?.title"}
</p> fill
</div> className="object-cover"
/>
</div>
<div>
<h4 className="text-sm font-semibold mb-3">
{article?.title}
</h4>
<p className="text-xs text-gray-500 flex gap-2 items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
>
<g fill="none">
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
<path
fill="currentColor"
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
/>
</g>
</svg>{" "}
{new Date(articles[0]?.createdAt).toLocaleDateString(
"id-ID",
{
day: "numeric",
month: "long",
year: "numeric",
}
)}
</p>
</div>
</Link>
</div> </div>
))} ))}
</div> </div>

View File

@ -1,5 +1,6 @@
// components/Footer.tsx // components/Footer.tsx
import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
export default function Footer() { export default function Footer() {
@ -8,34 +9,48 @@ export default function Footer() {
<div className="max-w-7xl mx-auto py-10 bg-[#09282C] px-8"> <div className="max-w-7xl mx-auto py-10 bg-[#09282C] px-8">
{/* Top Menu Links */} {/* Top Menu Links */}
<div className="flex flex-col md:flex-row justify-center md:justify-between gap-3"> <div className="flex flex-col md:flex-row justify-center md:justify-between gap-3">
<div className="w-full md:w-2/12"> {/* <div className="w-full md:w-2/12">
<p className="text-sm text-gray-400 mt-5"> <p className="text-sm text-gray-400 mt-5">
© 2025{" "} © 2025{" "}
<span className="text-xs text-white font-semibold">JNews</span>- <span className="text-xs text-white font-semibold">JNews</span>-
Premium WordPress news & magazine theme by{" "} Premium WordPress news & magazine theme by{" "}
<span className="text-white font-semibold">Jegtheme</span> <span className="text-white font-semibold">Jegtheme</span>
</p> </p>
</div> */}
<div className="flex items-center overflow-hidden mb-4 py-6 px-8">
<Image
src="/mikul.png"
alt="Background"
width={272}
height={90}
className="w-full md:w-[272px] h-[90px] object-cover border"
priority
/>
</div> </div>
<div className="w-full md:w-6/12"> <div className="w-full md:w-6/12">
<h2 className="border-b-2 mb-5"></h2> <h2 className="border-b-2 mb-5"></h2>
<div className="flex items-start flex-wrap justify-start md:justify-start gap-2 md:gap-3 text-xs text-white font-semibold"> <div className="flex items-start flex-wrap justify-start md:justify-start gap-2 md:gap-3 text-xs text-white font-semibold">
{["Beranda ", "Pembangunan", "Kesehatan", "Berita Warga"].map( {[
(item, idx, arr) => ( { label: "Beranda", href: "#" },
<span { label: "Pembangunan", href: "/category/development" },
key={idx} { label: "Kesehatan", href: "/category/health" },
className="flex items-center gap-2 whitespace-nowrap" { label: "Berita Warga", href: "/category/citizen-news" },
> ].map((item, idx, arr) => (
<a href="#" className="hover:underline"> <span
{item} key={idx}
</a> className="flex items-center gap-2 whitespace-nowrap"
{idx !== arr.length - 1 && ( >
<span className="text-white">/</span> <a href={item.href} className="hover:underline">
)} {item.label}
</span> </a>
) {idx !== arr.length - 1 && (
)} <span className="text-white">/</span>
)}
</span>
))}
</div> </div>
</div> </div>
<div className=" w-full md:w-3/12"> <div className=" w-full md:w-3/12">
<div className="flex flex-col justify-center md:justify-end gap-2 md:gap-3 text-xs text-[#FFFFFF]"> <div className="flex flex-col justify-center md:justify-end gap-2 md:gap-3 text-xs text-[#FFFFFF]">
<p className="text-xs font-bold text-red-600 mb-2 md:mb-0 w-10/12 text-start"> <p className="text-xs font-bold text-red-600 mb-2 md:mb-0 w-10/12 text-start">

View File

@ -142,7 +142,7 @@ export default function HeroNewsSection() {
/> />
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/40 to-transparent p-4 flex flex-col justify-end"> <div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/40 to-transparent p-4 flex flex-col justify-end">
<span className="text-xs bg-yellow-400 text-black px-2 py-0.5 inline-block uppercase w-[130px]"> <span className="text-xs bg-yellow-400 text-black px-2 py-0.5 inline-block uppercase w-[130px]">
{article.categoryName || "TANPA KATEGORI"} {article?.categories?.[0]?.title || "TANPA KATEGORI"}
</span> </span>
<h3 className="text-sm font-semibold text-white leading-snug mb-1"> <h3 className="text-sm font-semibold text-white leading-snug mb-1">
{article.title} {article.title}

View File

@ -1,6 +1,7 @@
"use client"; "use client";
import { getListArticle } from "@/service/article"; import { getListArticle } from "@/service/article";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
const data1 = { const data1 = {
@ -249,80 +250,82 @@ export default function LatestandPopular() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
{currentArticles.map((article, index) => ( {currentArticles.map((article, index) => (
<div key={index}> <div key={index}>
<div className="relative w-full aspect-video mb-3"> <Link href={`/detail/${article?.id}`}>
<Image <div className="relative w-full aspect-video mb-3">
src={ <Image
article?.thumbnailUrl || src={
article?.files?.[0]?.file_url || article?.thumbnailUrl ||
"/default-image.jpg" article?.files?.[0]?.file_url ||
} "/default-image.jpg"
alt={article?.title || "No title"} }
fill alt={article?.title || "No title"}
sizes="(max-width: 1024px) 100vw, 33vw" fill
className="object-cover rounded" sizes="(max-width: 1024px) 100vw, 33vw"
/> className="object-cover rounded"
<div className="absolute inset-0 bg-black/20" /> />
<span className="absolute bottom-1 left-1 bg-[#FFC600] text-black text-[11px] px-2 py-1 uppercase"> <div className="absolute inset-0 bg-black/20" />
{article?.categories?.map((cat) => cat.title).join(", ")} <span className="absolute bottom-1 left-1 bg-[#FFC600] text-black text-[11px] px-2 py-1 uppercase">
</span> {article?.categories?.map((cat) => cat.title).join(", ")}
</div> </span>
<div className="text-black"> </div>
<h3 className="font-semibold text-base mb-1"> <div className="text-black">
{article.title} <h3 className="font-semibold text-base mb-1">
</h3> {article.title}
<p className="text-[#999999] text-sm font-serif"> </h3>
{article.description.length > 100 <p className="text-[#999999] text-sm font-serif">
? `${article.description.slice(0, 100)}...` {article.description.length > 100
: article.description} ? `${article.description.slice(0, 100)}...`
</p> : article.description}
<div className="text-xs text-[#999999] mb-2 flex items-center gap-2"> </p>
by{" "} <div className="text-xs text-[#999999] mb-2 flex items-center gap-2">
<span className="text-[#31942E]"> by{" "}
{article.createdByName} <span className="text-[#31942E]">
</span>{" "} {article.createdByName}
|{" "} </span>{" "}
<div className="text-xs mt-1.5 text-[#A0A0A0] space-x-2 flex items-center"> |{" "}
{/* Clock Icon + Date */} <div className="text-xs mt-1.5 text-[#A0A0A0] space-x-2 flex items-center">
<svg {/* Clock Icon + Date */}
xmlns="http://www.w3.org/2000/svg" <svg
width="16" xmlns="http://www.w3.org/2000/svg"
height="16" width="16"
viewBox="0 0 24 24" height="16"
> viewBox="0 0 24 24"
<g fill="none"> >
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" /> <g fill="none">
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
<path
fill="currentColor"
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
/>
</g>
</svg>{" "}
{new Date(article?.createdAt).toLocaleDateString(
"id-ID",
{
day: "numeric",
month: "long",
year: "numeric",
}
)}
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path <path
fill="currentColor" strokeLinecap="round"
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1" strokeLinejoin="round"
strokeWidth={2}
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/> />
</g> </svg>{" "}
</svg>{" "} 0
{new Date(article?.createdAt).toLocaleDateString( </div>
"id-ID",
{
day: "numeric",
month: "long",
year: "numeric",
}
)}
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/>
</svg>{" "}
0
</div> </div>
</div> </div>
</div> </Link>
</div> </div>
))} ))}
</div> </div>
@ -364,93 +367,100 @@ export default function LatestandPopular() {
</h2> </h2>
<div className=" w-full"> <div className=" w-full">
<div className="relative w-full aspect-video mb-5"> <div className="relative w-full aspect-video mb-5">
<Image <Link href={`/detail/${articles[0]?.id}`}>
src={ <Image
articles[0]?.thumbnailUrl || src={
articles[0]?.files?.[0]?.file_url || articles[0]?.thumbnailUrl ||
"/default-image.jpg" articles[0]?.files?.[0]?.file_url ||
} "/default-image.jpg"
alt={"articles[0]?.title"} }
fill alt={"articles[0]?.title"}
sizes="(max-width: 1024px) 100vw, 33vw" fill
className="object-cover" sizes="(max-width: 1024px) 100vw, 33vw"
/> className="object-cover"
<div className="absolute inset-0 bg-black/30" /> />
<div className="absolute bottom-0.5 left-2 text-white"> <div className="absolute inset-0 bg-black/30" />
<h3 className=" font-semibold text-base mb-1"> <div className="absolute bottom-0.5 left-2 text-white">
{articles[0]?.title} <h3 className=" font-semibold text-base mb-1">
</h3> {articles[0]?.title}
<p className=" text-xs mb-2 flex items-center gap-2"> </h3>
<svg <p className=" text-xs mb-2 flex items-center gap-2">
xmlns="http://www.w3.org/2000/svg" <svg
width="16" xmlns="http://www.w3.org/2000/svg"
height="16" width="16"
viewBox="0 0 24 24" height="16"
> viewBox="0 0 24 24"
<g fill="none"> >
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" /> <g fill="none">
<path <path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
fill="currentColor" <path
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1" fill="currentColor"
/> d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
</g> />
</svg>{" "} </g>
{new Date(articles[0]?.createdAt).toLocaleDateString( </svg>{" "}
"id-ID", {new Date(articles[0]?.createdAt).toLocaleDateString(
{ "id-ID",
day: "numeric", {
month: "long", day: "numeric",
year: "numeric", month: "long",
} year: "numeric",
)} }
</p> )}
</div> </p>
</div>
</Link>
</div> </div>
<div className="space-y-5"> <div className="space-y-5">
{articles?.slice(1, 4).map((article, index) => ( {articles?.slice(1, 4).map((article, index) => (
<div key={index} className="flex gap-3"> <div key={index} className="flex gap-3">
<div className="relative w-[120px] h-[86px] shrink-0"> <Link
<Image className="flex gap-3"
src={ href={`/detail/${article?.id}`}
article?.thumbnailUrl || >
article?.files?.[0]?.file_url || <div className="relative w-[120px] h-[86px] shrink-0">
"/default-image.jpg" <Image
} src={
alt={"article?.title"} article?.thumbnailUrl ||
fill article?.files?.[0]?.file_url ||
className="object-cover" "/default-image.jpg"
/>
</div>
<div>
<h4 className="text-sm font-semibold mb-3">
{article?.title}
</h4>
<p className="text-xs text-gray-500 flex gap-2 items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
>
<g fill="none">
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
<path
fill="currentColor"
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
/>
</g>
</svg>{" "}
{new Date(articles[0]?.createdAt).toLocaleDateString(
"id-ID",
{
day: "numeric",
month: "long",
year: "numeric",
} }
)} alt={"article?.title"}
</p> fill
</div> className="object-cover"
/>
</div>
<div>
<h4 className="text-sm font-semibold mb-3">
{article?.title}
</h4>
<p className="text-xs text-gray-500 flex gap-2 items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
>
<g fill="none">
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
<path
fill="currentColor"
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
/>
</g>
</svg>{" "}
{new Date(articles[0]?.createdAt).toLocaleDateString(
"id-ID",
{
day: "numeric",
month: "long",
year: "numeric",
}
)}
</p>
</div>
</Link>
</div> </div>
))} ))}
</div> </div>

View File

@ -1,7 +1,10 @@
"use client"; "use client";
import { getAdvertiseById } from "@/service/advertisement";
import { getListArticle } from "@/service/article"; import { getListArticle } from "@/service/article";
import { id } from "date-fns/locale";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
const data1 = { const data1 = {
@ -186,7 +189,7 @@ type Article = {
}[]; }[];
}; };
export default function Latest() { export default function Latest({ id }: { id: number }) {
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [totalPage, setTotalPage] = useState(1); const [totalPage, setTotalPage] = useState(1);
const [articles, setArticles] = useState<Article[]>([]); const [articles, setArticles] = useState<Article[]>([]);
@ -199,6 +202,17 @@ export default function Latest() {
endDate: null, endDate: null,
}); });
const [data, setData] = useState<any>(null);
useEffect(() => {
const fetchData = async () => {
const res = await getAdvertiseById(Number(id));
const result = res?.data?.data;
setData(result);
};
fetchData();
}, [id]);
useEffect(() => { useEffect(() => {
initState(); initState();
}, [page, showData, startDateValue, selectedCategories]); }, [page, showData, startDateValue, selectedCategories]);
@ -235,85 +249,92 @@ export default function Latest() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 "> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6 ">
<div className=" w-full"> <div className=" w-full">
<div className="relative w-full aspect-video mb-5"> <div className="relative w-full aspect-video mb-5">
<Image <Link href={`/detail/${articles[0]?.id}`}>
src={articles[0]?.files?.[0]?.file_url || "/nodata.png"} <Image
alt={"articles[0]?.title"} src={articles[0]?.files?.[0]?.file_url || "/nodata.png"}
fill alt={"articles[0]?.title"}
sizes="(max-width: 1024px) 100vw, 33vw" fill
className="object-cover" sizes="(max-width: 1024px) 100vw, 33vw"
/> className="object-cover"
<div className="absolute inset-0 bg-black/20" /> />
<div className="absolute bottom-0.5 left-2 text-white"> <div className="absolute inset-0 bg-black/20" />
<h3 className=" font-semibold text-base mb-1"> <div className="absolute bottom-0.5 left-2 text-white">
{articles[0]?.title} <h3 className=" font-semibold text-base mb-1">
</h3> {articles[0]?.title}
<p className=" text-xs mb-2 flex items-center gap-2"> </h3>
<svg <p className=" text-xs mb-2 flex items-center gap-2">
xmlns="http://www.w3.org/2000/svg" <svg
width="16" xmlns="http://www.w3.org/2000/svg"
height="16" width="16"
viewBox="0 0 24 24" height="16"
> viewBox="0 0 24 24"
<g fill="none"> >
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" /> <g fill="none">
<path <path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
fill="currentColor" <path
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1" fill="currentColor"
/> d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
</g> />
</svg>{" "} </g>
{new Date(articles[0]?.createdAt).toLocaleDateString( </svg>{" "}
"id-ID", {new Date(articles[0]?.createdAt).toLocaleDateString(
{ "id-ID",
day: "numeric", {
month: "long", day: "numeric",
year: "numeric", month: "long",
} year: "numeric",
)} }
</p> )}
</div> </p>
</div>
</Link>
</div> </div>
<div className="space-y-5"> <div className="space-y-5">
{articles.slice(1, 5).map((article, index) => ( {articles.slice(1, 5).map((article, index) => (
<div key={index} className="flex gap-3"> <div key={index} className="flex gap-3">
<div className="relative w-[120px] h-[86px] shrink-0"> <Link
<Image className="flex gap-3"
src={article?.thumbnailUrl || "/nodata.png"} href={`/detail/${article?.id}`}
alt={"article?.title"} >
fill <div className="relative w-[120px] h-[86px] shrink-0">
className="object-cover" <Image
/> src={article?.thumbnailUrl || "/nodata.png"}
</div> alt={"article?.title"}
<div> fill
<h4 className="text-sm font-semibold mb-3"> className="object-cover"
{article.title} />
</h4> </div>
<p className="text-xs text-gray-500 flex gap-2 articles-center"> <div>
<svg <h4 className="text-sm font-semibold mb-3">
xmlns="http://www.w3.org/2000/svg" {article.title}
width="16" </h4>
height="16" <p className="text-xs text-gray-500 flex gap-2 articles-center">
viewBox="0 0 24 24" <svg
> xmlns="http://www.w3.org/2000/svg"
<g fill="none"> width="16"
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" /> height="16"
<path viewBox="0 0 24 24"
fill="currentColor" >
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1" <g fill="none">
/> <path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
</g> <path
</svg>{" "} fill="currentColor"
{new Date(articles[0]?.createdAt).toLocaleDateString( d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
"id-ID", />
{ </g>
day: "numeric", </svg>{" "}
month: "long", {new Date(articles[0]?.createdAt).toLocaleDateString(
year: "numeric", "id-ID",
} {
)} day: "numeric",
</p> month: "long",
</div> year: "numeric",
}
)}
</p>
</div>
</Link>
</div> </div>
))} ))}
</div> </div>
@ -321,93 +342,100 @@ export default function Latest() {
<div className="w-full"> <div className="w-full">
<div className="relative w-full aspect-video mb-5"> <div className="relative w-full aspect-video mb-5">
<Image <Link href={`/detail/${articles[0]?.id}`}>
src={ <Image
articles[0]?.files?.[0]?.file_url || "/default-image.jpg" src={
} articles[0]?.files?.[0]?.file_url || "/default-image.jpg"
alt={"articles[0]?.title"} }
fill alt={"articles[0]?.title"}
sizes="(max-width: 1024px) 100vw, 33vw" fill
className="object-cover " sizes="(max-width: 1024px) 100vw, 33vw"
/> className="object-cover "
<div className="absolute inset-0 bg-black/20" /> />
<div className="absolute bottom-0.5 left-2 text-white"> <div className="absolute inset-0 bg-black/20" />
<h3 className=" font-semibold text-base mb-1"> <div className="absolute bottom-0.5 left-2 text-white">
{articles[0]?.title} <h3 className=" font-semibold text-base mb-1">
</h3> {articles[0]?.title}
<p className=" text-xs mb-2 flex items-center gap-2"> </h3>
<svg <p className=" text-xs mb-2 flex items-center gap-2">
xmlns="http://www.w3.org/2000/svg" <svg
width="16" xmlns="http://www.w3.org/2000/svg"
height="16" width="16"
viewBox="0 0 24 24" height="16"
> viewBox="0 0 24 24"
<g fill="none"> >
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" /> <g fill="none">
<path <path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
fill="currentColor" <path
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1" fill="currentColor"
/> d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
</g> />
</svg>{" "} </g>
{new Date(articles[0]?.createdAt).toLocaleDateString( </svg>{" "}
"id-ID", {new Date(articles[0]?.createdAt).toLocaleDateString(
{ "id-ID",
day: "numeric", {
month: "long", day: "numeric",
year: "numeric", month: "long",
} year: "numeric",
)} }
</p> )}
</div> </p>
</div>
</Link>
</div> </div>
<div className="space-y-5"> <div className="space-y-5">
{articles.slice(1, 5).map((article, index) => ( {articles.slice(1, 5).map((article, index) => (
<div key={index} className="flex gap-3"> <div key={index} className="flex gap-3">
<div className="relative w-[120px] h-[86px] shrink-0"> <Link
<Image className="flex gap-3"
src={article?.thumbnailUrl || "/nodata.png"} href={`/detail/${article?.id}`}
alt={"article?.title"} >
fill <div className="relative w-[120px] h-[86px] shrink-0">
className="object-cover " <Image
/> src={article?.thumbnailUrl || "/nodata.png"}
</div> alt={"article?.title"}
<div> fill
<h4 className="text-sm font-semibold mb-3"> className="object-cover "
{article?.title} />
</h4> </div>
<p className="text-xs text-gray-500 flex gap-2 articles-center"> <div>
<svg <h4 className="text-sm font-semibold mb-3">
xmlns="http://www.w3.org/2000/svg" {article?.title}
width="16" </h4>
height="16" <p className="text-xs text-gray-500 flex gap-2 articles-center">
viewBox="0 0 24 24" <svg
> xmlns="http://www.w3.org/2000/svg"
<g fill="none"> width="16"
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" /> height="16"
<path viewBox="0 0 24 24"
fill="currentColor" >
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1" <g fill="none">
/> <path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
</g> <path
</svg>{" "} fill="currentColor"
{new Date(articles[0]?.createdAt).toLocaleDateString( d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
"id-ID", />
{ </g>
day: "numeric", </svg>{" "}
month: "long", {new Date(articles[0]?.createdAt).toLocaleDateString(
year: "numeric", "id-ID",
} {
)} day: "numeric",
</p> month: "long",
</div> year: "numeric",
}
)}
</p>
</div>
</Link>
</div> </div>
))} ))}
</div> </div>
</div> </div>
</div> </div>
<div className="relative my-5 max-w-full h-[125px] overflow-hidden flex items-center mx-auto border"> {/* <div className="relative my-5 max-w-full h-[125px] overflow-hidden flex items-center mx-auto border">
<Image <Image
src="/image-kolom.png" src="/image-kolom.png"
alt="Berita Utama" alt="Berita Utama"
@ -434,7 +462,7 @@ export default function Latest() {
</h2> </h2>
<div className="w-full border bg-[#FAFAFA] h-[64px] flex justify-center items-center"> <div className="w-full border bg-[#FAFAFA] h-[64px] flex justify-center items-center">
<p className="text-center ">No Content Available</p> <p className="text-center ">No Content Available</p>
</div> </div> */}
</div> </div>
<aside className="w-full lg:w-[345px]"> <aside className="w-full lg:w-[345px]">
@ -446,86 +474,94 @@ export default function Latest() {
<div className="space-y-8"> <div className="space-y-8">
{popularPosts.slice(1, 5).map((post, index) => ( {popularPosts.slice(1, 5).map((post, index) => (
<div key={index} className="space-y-3"> <div key={index} className="space-y-3">
<div <Link href={`/detail/${post?.id}`}>
className={`flex gap-4 ${ <div
post.files?.[0]?.file_url className={`flex gap-4 ${
? "flex-col md:flex-row" post.files?.[0]?.file_url
: "flex-col" ? "flex-col md:flex-row"
}`} : "flex-col"
> }`}
<div className="flex-1"> >
<h3 className="text-base sm:text-lg md:text-sm font-bold leading-tight"> <div className="flex-1">
{post?.title} <h3 className="text-base sm:text-lg md:text-sm font-bold leading-tight">
</h3> {post?.title}
<div className="text-xs mt-1.5 text-[#A0A0A0] space-x-2 flex items-center"> </h3>
<svg <div className="text-xs mt-1.5 text-[#A0A0A0] space-x-2 flex items-center">
xmlns="http://www.w3.org/2000/svg" <svg
width="16" xmlns="http://www.w3.org/2000/svg"
height="16" width="16"
viewBox="0 0 24 24" height="16"
> viewBox="0 0 24 24"
<g fill="none"> >
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" /> <g fill="none">
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
<path
fill="currentColor"
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
/>
</g>
</svg>{" "}
{new Date(articles[0]?.createdAt).toLocaleDateString(
"id-ID",
{
day: "numeric",
month: "long",
year: "numeric",
}
)}
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path <path
fill="currentColor" strokeLinecap="round"
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1" strokeLinejoin="round"
strokeWidth={2}
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/> />
</g> </svg>{" "}
</svg>{" "} 0
{new Date(articles[0]?.createdAt).toLocaleDateString(
"id-ID",
{
day: "numeric",
month: "long",
year: "numeric",
}
)}
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/>
</svg>{" "}
0
</div>
</div>
{post.thumbnailUrl && (
<div className="w-full md:w-1/3 relative">
<div className="w-full aspect-[4/3] sm:w-[260px] sm:h-[157px] md:max-w-[320px] md:h-[220px] lg:max-w-[120px] lg:h-[87px] relative ml-auto">
<Image
src={post?.thumbnailUrl}
alt={"post?.title"}
fill
className="object-cover rounded"
/>
</div> </div>
</div> </div>
)}
</div>
<p className="text-[13px] text-[#3D4248] line-clamp-2 mb-3"> {post.thumbnailUrl && (
{post.description} <div className="w-full md:w-1/3 relative">
</p> <div className="w-full aspect-[4/3] sm:w-[260px] sm:h-[157px] md:max-w-[320px] md:h-[220px] lg:max-w-[120px] lg:h-[87px] relative ml-auto">
<Image
src={post?.thumbnailUrl}
alt={"post?.title"}
fill
className="object-cover rounded"
/>
</div>
</div>
)}
</div>
<p className="text-[13px] text-[#3D4248] line-clamp-2 mb-3">
{post.description}
</p>
</Link>
</div> </div>
))} ))}
</div> </div>
</div> </div>
<div className="relative w-[1111px] max-w-full h-[300px] overflow-hidden flex items-center mx-auto border my-6 rounded"> <div className="relative w-[1111px] max-w-full h-[300px] overflow-hidden flex items-center mx-auto border my-6 rounded">
<Image {data?.contentFileUrl ? (
src="/kolom.png" <Image
alt="Berita Utama" src={data.contentFileUrl}
fill alt={data?.title || "Berita Utama"}
className="object-contain rounded" fill
/> className="object-cover rounded"
/>
) : (
<div className="w-full h-full flex items-center justify-center text-gray-400">
Loading...
</div>
)}
</div> </div>
</aside> </aside>
</div> </div>

View File

@ -2,6 +2,7 @@
import { getListArticle } from "@/service/article"; import { getListArticle } from "@/service/article";
import { Clock } from "lucide-react"; import { Clock } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
type postsData = { type postsData = {
@ -60,52 +61,52 @@ export default function Beranda() {
return ( return (
<section className="max-w-7xl mx-auto px-10 py-5 bg-white"> <section className="max-w-7xl mx-auto px-10 py-5 bg-white">
<div className="flex flex-col md:flex-row md:items-center md:justify-between mb-8 pb-1 gap-2 bg-white border-b-2 pt-2 "> <div className="flex flex-col md:flex-row md:items-center md:justify-between mb-8 pb-1 gap-2 bg-white border-b-2 pt-2 ">
<h2 className="text-sm font-bold">Football</h2> <h2 className="text-sm font-bold">Pembangunan</h2>
<div className="flex flex-wrap gap-2 text-xs text-gray-600"> <div className="flex flex-wrap gap-2 text-xs text-gray-600">
<button className="hover:text-green-500">ALL</button> <button className="hover:text-green-500">ALL</button>
<button className="hover:text-green-500">Premier League</button> <button className="hover:text-green-500">Pembangunan</button>
<button className="hover:text-green-500">The Presidents Cup</button>
<button className="hover:text-green-500">Super Bowl</button>
</div> </div>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 md:gap-10 pt-4 "> <div className="grid grid-cols-1 md:grid-cols-3 gap-8 md:gap-10 pt-4 ">
{posts.slice(1, 5).map((posts, index) => ( {posts.slice(1, 5).map((posts, index) => (
<div key={index} className="bg-white overflow-hidden"> <div key={index} className="bg-white overflow-hidden">
<div className="relative"> <Link href={`/detail/${posts?.id}`}>
<Image <div className="relative">
src={ <Image
posts.thumbnailUrl || src={
posts?.files?.[0]?.file_url || posts.thumbnailUrl ||
"/default-image.jpg" posts?.files?.[0]?.file_url ||
} "/default-image.jpg"
alt={posts.title} }
width={500} alt={posts.title}
height={300} width={500}
className="w-full h-52 md:h-56 object-cover" height={300}
/> className="w-full h-52 md:h-56 object-cover"
<div className="absolute inset-0 bg-black/20" /> />
<span className="absolute top-1 left-1 bg-[#FFC600] text-black text-[11px] px-2 py-1 uppercase"> <div className="absolute inset-0 bg-black/20" />
{posts.categories?.[0]?.title} <span className="absolute top-1 left-1 bg-[#FFC600] text-black text-[11px] px-2 py-1 uppercase">
</span> {posts.categories?.[0]?.title}
<div className="p-3 md:p-2 absolute bottom-1 left-1 text-white"> </span>
<h3 className="font-bold text-sm md:text-base leading-snug mb-1"> <div className="p-3 md:p-2 absolute bottom-1 left-1 text-white">
{posts.title} <h3 className="font-bold text-sm md:text-base leading-snug mb-1">
</h3> {posts.title}
</h3>
<div className="text-xs flex items-center gap-2"> <div className="text-xs flex items-center gap-2">
<Clock className="w-3 h-3 " /> <Clock className="w-3 h-3 " />
<span className=" "> <span className=" ">
{posts.createdByName} -{" "} {posts.createdByName} -{" "}
{new Date(posts.createdAt).toLocaleDateString("id-ID", { {new Date(posts.createdAt).toLocaleDateString("id-ID", {
day: "numeric", day: "numeric",
month: "long", month: "long",
year: "numeric", year: "numeric",
})} })}
</span> </span>
</div>
</div> </div>
</div> </div>
</div> </Link>
</div> </div>
))} ))}
</div> </div>

View File

@ -126,7 +126,10 @@ export const RetractingSidebar = ({
className="hidden md:flex fixed top-4 left-20 z-40 p-3 bg-white rounded-xl shadow-lg border border-slate-200/60 hover:shadow-xl transition-all duration-200 hover:bg-slate-50" className="hidden md:flex fixed top-4 left-20 z-40 p-3 bg-white rounded-xl shadow-lg border border-slate-200/60 hover:shadow-xl transition-all duration-200 hover:bg-slate-50"
onClick={() => updateSidebarData(true)} onClick={() => updateSidebarData(true)}
> >
<Icon icon="heroicons:chevron-right" className="w-5 h-5 text-slate-600" /> <Icon
icon="heroicons:chevron-right"
className="w-5 h-5 text-slate-600"
/>
</motion.button> </motion.button>
)} )}
</AnimatePresence> </AnimatePresence>
@ -142,7 +145,10 @@ export const RetractingSidebar = ({
className="md:hidden fixed top-4 left-4 z-50 p-3 bg-white dark:bg-slate-800 rounded-xl shadow-lg border border-slate-200/60 dark:border-slate-700/60 hover:shadow-xl transition-all duration-200" className="md:hidden fixed top-4 left-4 z-50 p-3 bg-white dark:bg-slate-800 rounded-xl shadow-lg border border-slate-200/60 dark:border-slate-700/60 hover:shadow-xl transition-all duration-200"
onClick={() => updateSidebarData(true)} onClick={() => updateSidebarData(true)}
> >
<Icon icon="heroicons:chevron-right" className="w-6 h-6 text-slate-600" /> <Icon
icon="heroicons:chevron-right"
className="w-6 h-6 text-slate-600"
/>
</motion.button> </motion.button>
)} )}
</AnimatePresence> </AnimatePresence>
@ -183,6 +189,17 @@ const SidebarContent = ({
updateSidebarData: (newData: boolean) => void; updateSidebarData: (newData: boolean) => void;
}) => { }) => {
const { theme, toggleTheme } = useTheme(); const { theme, toggleTheme } = useTheme();
const [username, setUsername] = useState("");
useEffect(() => {
// Ambil cookie secara client-side
const cookies = document.cookie.split("; ").reduce((acc: any, cur) => {
const [key, value] = cur.split("=");
acc[key] = value;
return acc;
}, {});
setUsername(cookies.username || "Guest");
}, []);
return ( return (
<div className="flex flex-col h-full"> <div className="flex flex-col h-full">
{/* SCROLLABLE TOP SECTION */} {/* SCROLLABLE TOP SECTION */}
@ -193,7 +210,10 @@ const SidebarContent = ({
<div className="flex items-center justify-between px-4 py-6"> <div className="flex items-center justify-between px-4 py-6">
<Link href="/" className="flex items-center space-x-3"> <Link href="/" className="flex items-center space-x-3">
<div className="relative"> <div className="relative">
<img src="/mikul.png" className="w-10 h-10 rounded-lg shadow-sm" /> <img
src="/mikul.png"
className="w-10 h-10 rounded-lg shadow-sm"
/>
<div className="absolute -inset-1 bg-gradient-to-r from-blue-500 to-purple-500 rounded-lg opacity-20 blur-sm"></div> <div className="absolute -inset-1 bg-gradient-to-r from-blue-500 to-purple-500 rounded-lg opacity-20 blur-sm"></div>
</div> </div>
{open && ( {open && (
@ -210,7 +230,7 @@ const SidebarContent = ({
</motion.div> </motion.div>
)} )}
</Link> </Link>
{open && ( {open && (
<motion.button <motion.button
initial={{ opacity: 0, scale: 0.8 }} initial={{ opacity: 0, scale: 0.8 }}
@ -219,9 +239,9 @@ const SidebarContent = ({
className="p-2 rounded-lg hover:bg-slate-100 transition-colors duration-200 group" className="p-2 rounded-lg hover:bg-slate-100 transition-colors duration-200 group"
onClick={() => updateSidebarData(false)} onClick={() => updateSidebarData(false)}
> >
<Icon <Icon
icon="heroicons:chevron-left" icon="heroicons:chevron-left"
className="w-5 h-5 text-slate-500 group-hover:text-slate-700 transition-colors" className="w-5 h-5 text-slate-500 group-hover:text-slate-700 transition-colors"
/> />
</motion.button> </motion.button>
)} )}
@ -230,7 +250,7 @@ const SidebarContent = ({
{/* Navigation Sections */} {/* Navigation Sections */}
<div className="space-y-3 px-3 pb-6"> <div className="space-y-3 px-3 pb-6">
{sidebarSections.map((section, sectionIndex) => ( {sidebarSections.map((section, sectionIndex) => (
<motion.div <motion.div
key={section.title} key={section.title}
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
@ -238,7 +258,7 @@ const SidebarContent = ({
className="space-y-3" className="space-y-3"
> >
{open && ( {open && (
<motion.h3 <motion.h3
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
transition={{ delay: 0.2 + sectionIndex * 0.1 }} transition={{ delay: 0.2 + sectionIndex * 0.1 }}
@ -271,32 +291,34 @@ const SidebarContent = ({
{/* <div className="px-3 pb-2"> {/* <div className="px-3 pb-2">
<div className="h-px bg-gradient-to-r from-transparent via-slate-300 to-transparent"></div> <div className="h-px bg-gradient-to-r from-transparent via-slate-300 to-transparent"></div>
</div> */} </div> */}
{/* Theme Toggle */} {/* Theme Toggle */}
<div className="px-3 pt-1"> <div className="px-3 pt-1">
<motion.button <motion.button
onClick={toggleTheme} onClick={toggleTheme}
className={`relative flex h-12 w-full items-center rounded-xl transition-all duration-200 cursor-pointer group ${ className={`relative flex h-12 w-full items-center rounded-xl transition-all duration-200 cursor-pointer group ${
open ? 'px-3' : 'justify-center' open ? "px-3" : "justify-center"
} ${ } ${
theme === 'dark' theme === "dark"
? "bg-gradient-to-r from-emerald-500 to-green-500 text-white shadow-lg shadow-emerald-500/25" ? "bg-gradient-to-r from-emerald-500 to-green-500 text-white shadow-lg shadow-emerald-500/25"
: "text-slate-600 hover:bg-gradient-to-r hover:from-slate-100 hover:to-slate-200/50 hover:text-slate-800 dark:text-slate-300 dark:hover:bg-slate-700/50" : "text-slate-600 hover:bg-gradient-to-r hover:from-slate-100 hover:to-slate-200/50 hover:text-slate-800 dark:text-slate-300 dark:hover:bg-slate-700/50"
}`} }`}
whileHover={{ scale: 1.02 }} whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
> >
<motion.div <motion.div
className={`h-full flex items-center justify-center ${ className={`h-full flex items-center justify-center ${
open ? "w-12" : "w-full" open ? "w-12" : "w-full"
}`} }`}
> >
<div className={`text-lg transition-all duration-200 ${ <div
theme === 'dark' className={`text-lg transition-all duration-200 ${
? "text-white" theme === "dark"
: "text-slate-500 group-hover:text-slate-700 dark:text-slate-400 dark:group-hover:text-slate-200" ? "text-white"
}`}> : "text-slate-500 group-hover:text-slate-700 dark:text-slate-400 dark:group-hover:text-slate-200"
{theme === 'dark' ? ( }`}
>
{theme === "dark" ? (
<Icon icon="solar:sun-bold" className="text-lg" /> <Icon icon="solar:sun-bold" className="text-lg" />
) : ( ) : (
<Icon icon="solar:moon-bold" className="text-lg" /> <Icon icon="solar:moon-bold" className="text-lg" />
@ -305,15 +327,17 @@ const SidebarContent = ({
</motion.div> </motion.div>
{open && ( {open && (
<motion.span <motion.span
initial={{ opacity: 0, x: -10 }} initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }} animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.1, duration: 0.2 }} transition={{ delay: 0.1, duration: 0.2 }}
className={`text-sm font-medium transition-colors duration-200 ${ className={`text-sm font-medium transition-colors duration-200 ${
theme === 'dark' ? "text-white" : "text-slate-700 dark:text-slate-300" theme === "dark"
? "text-white"
: "text-slate-700 dark:text-slate-300"
}`} }`}
> >
{theme === 'dark' ? 'Light Mode' : 'Dark Mode'} {theme === "dark" ? "Light Mode" : "Dark Mode"}
</motion.span> </motion.span>
)} )}
</motion.button> </motion.button>
@ -334,13 +358,19 @@ const SidebarContent = ({
</div> </div>
{/* User Profile */} {/* User Profile */}
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4 }} transition={{ delay: 0.4 }}
className="px-3 py-3 border-t border-slate-200/60" className="px-3 py-3 border-t border-slate-200/60"
> >
<div className={`${open ? 'flex items-center space-x-3' : 'flex items-center justify-center'} p-3 rounded-xl bg-gradient-to-r from-slate-50 to-slate-100/50 hover:from-slate-100 hover:to-slate-200/50 transition-all duration-200 cursor-pointer group`}> <div
className={`${
open
? "flex items-center space-x-3"
: "flex items-center justify-center"
} p-3 rounded-xl bg-gradient-to-r from-slate-50 to-slate-100/50 hover:from-slate-100 hover:to-slate-200/50 transition-all duration-200 cursor-pointer group`}
>
<div className="relative"> <div className="relative">
<div className="w-10 h-10 rounded-full bg-gradient-to-r from-blue-500 to-purple-500 flex items-center justify-center text-white font-semibold text-sm shadow-lg"> <div className="w-10 h-10 rounded-full bg-gradient-to-r from-blue-500 to-purple-500 flex items-center justify-center text-white font-semibold text-sm shadow-lg">
A A
@ -348,13 +378,15 @@ const SidebarContent = ({
<div className="absolute -bottom-1 -right-1 w-4 h-4 bg-green-500 rounded-full border-2 border-white"></div> <div className="absolute -bottom-1 -right-1 w-4 h-4 bg-green-500 rounded-full border-2 border-white"></div>
</div> </div>
{open && ( {open && (
<motion.div <motion.div
initial={{ opacity: 0, x: -10 }} initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }} animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.5 }} transition={{ delay: 0.5 }}
className="flex-1 min-w-0" className="flex-1 min-w-0"
> >
<p className="text-sm font-medium text-slate-800 truncate">admin-mabes</p> <p className="text-sm font-medium text-slate-800 truncate">
{username}
</p>
<Link href="/auth"> <Link href="/auth">
<p className="text-xs text-slate-500 hover:text-blue-600 transition-colors duration-200"> <p className="text-xs text-slate-500 hover:text-blue-600 transition-colors duration-200">
Sign out Sign out
@ -394,6 +426,17 @@ const SidebarContent = ({
const Sidebar = () => { const Sidebar = () => {
const [open, setOpen] = useState(true); const [open, setOpen] = useState(true);
const pathname = usePathname(); const pathname = usePathname();
const [username, setUsername] = useState("");
useEffect(() => {
// Ambil cookie secara client-side
const cookies = document.cookie.split("; ").reduce((acc: any, cur) => {
const [key, value] = cur.split("=");
acc[key] = value;
return acc;
}, {});
setUsername(cookies.username || "Guest");
}, []);
return ( return (
<motion.nav <motion.nav
@ -512,7 +555,7 @@ const Sidebar = () => {
</g> </g>
</svg> </svg>
<div className="flex flex-col gap-0.5 text-xs"> <div className="flex flex-col gap-0.5 text-xs">
<p>admin-mabes</p> <p>{username}</p>
<p className="underline">Logout</p> <p className="underline">Logout</p>
</div> </div>
</div> </div>

View File

@ -1,5 +1,9 @@
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { httpDeleteInterceptor, httpPostInterceptor, httpPutInterceptor } from "./http-config/http-interceptor-services"; import {
httpDeleteInterceptor,
httpPostInterceptor,
httpPutInterceptor,
} from "./http-config/http-interceptor-services";
import { httpGet } from "./http-config/http-base-services"; import { httpGet } from "./http-config/http-base-services";
const token = Cookies.get("access_token"); const token = Cookies.get("access_token");
@ -8,16 +12,19 @@ export async function createAdvertise(data: any) {
const pathUrl = `/advertisement`; const pathUrl = `/advertisement`;
return await httpPostInterceptor(pathUrl, data); return await httpPostInterceptor(pathUrl, data);
} }
export async function createMediaFileAdvertise(id: string | number, data: any) { export async function createMediaFileAdvertise(id: string | number, data: any) {
const headers = { const headers = {
"content-type": "multipart/form-data", "Content-Type": "multipart/form-data",
}; };
const pathUrl = `/advertisement/upload/${id}`; const pathUrl = `/advertisement/upload/${id}`;
return await httpPostInterceptor(pathUrl, data, headers); return await httpPostInterceptor(pathUrl, data, headers);
} }
export async function getAdvertise(data: any) { export async function getAdvertise(data: any) {
const pathUrl = `/advertisement?page=${data?.page || 1}&limit=${data?.limit || ""}&placement=${data?.placement || ""}&isPublish=${data.isPublish || ""}`; const pathUrl = `/advertisement?page=${data?.page || 1}&limit=${
data?.limit || ""
}&placement=${data?.placement || ""}&isPublish=${data.isPublish || ""}`;
return await httpGet(pathUrl); return await httpGet(pathUrl);
} }

View File

@ -1,6 +1,12 @@
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { httpGet, httpPost } from "./http-config/http-base-services"; import { httpGet, httpPost } from "./http-config/http-base-services";
import { httpDeleteInterceptor, httpGetInterceptor, httpPostInterceptor, httpPutInterceptor } from "./http-config/http-interceptor-services"; import {
httpDeleteInterceptor,
httpGetInterceptor,
httpPostInterceptor,
httpPutInterceptor,
} from "./http-config/http-interceptor-services";
import { hex } from "framer-motion";
const token = Cookies.get("access_token"); const token = Cookies.get("access_token");
const id = Cookies.get("uie"); const id = Cookies.get("uie");
@ -32,12 +38,12 @@ export async function getDetailMasterUsers(id: string) {
} }
export async function editMasterUsers(data: any, id: string) { export async function editMasterUsers(data: any, id: string) {
const pathUrl = `/users/${id}` const pathUrl = `/users/${id}`;
return await httpPutInterceptor(pathUrl, data); return await httpPutInterceptor(pathUrl, data);
} }
export async function deleteMasterUser(id: string) { export async function deleteMasterUser(id: string) {
const pathUrl = `/users/${id}` const pathUrl = `/users/${id}`;
return await httpDeleteInterceptor(pathUrl); return await httpDeleteInterceptor(pathUrl);
} }
@ -49,7 +55,7 @@ export async function postSignIn(data: any) {
export async function getProfile(token?: string) { export async function getProfile(token?: string) {
const headers = { const headers = {
"content-type": "application/json", "content-type": "application/json",
"Authorization": `Bearer ${token}`, Authorization: `Bearer ${token}`,
}; };
const pathUrl = `/users/info`; const pathUrl = `/users/info`;
return await httpGet(pathUrl, headers); return await httpGet(pathUrl, headers);
@ -60,7 +66,7 @@ export async function updateProfile(data: any) {
return await httpPutInterceptor(pathUrl, data); return await httpPutInterceptor(pathUrl, data);
} }
export async function savePassword(data: any) { export async function savePassword(data: any) {
const pathUrl = `/users/save-password` const pathUrl = `/users/save-password`;
return await httpPostInterceptor(pathUrl, data); return await httpPostInterceptor(pathUrl, data);
} }
@ -84,8 +90,8 @@ export async function otpRequest(email: string, name: string) {
} }
export async function otpValidation(email: string, otpCode: string) { export async function otpValidation(email: string, otpCode: string) {
const pathUrl = `/users/otp-validation` const pathUrl = `/users/otp-validation`;
return await httpPost(pathUrl, { email, otpCode }); return await httpPost(pathUrl, { email, otpCode });
} }
export async function postArticleComment(data: any) { export async function postArticleComment(data: any) {
@ -111,11 +117,14 @@ export async function getArticleComment(id: string) {
} }
export async function deleteArticleComment(id: number) { export async function deleteArticleComment(id: number) {
const pathUrl = `/article-comments/${id}` const pathUrl = `/article-comments/${id}`;
return await httpDeleteInterceptor(pathUrl); return await httpDeleteInterceptor(pathUrl);
} }
export async function getCsrfToken() { export async function getCsrfToken() {
const pathUrl = "csrf-token"; const pathUrl = "csrf-token";
return httpGet(pathUrl); const headers = {
} "content-type": "application/json",
};
return httpGet(pathUrl, headers);
}