diff --git a/components/details/details-content.tsx b/components/details/details-content.tsx index 2000fd9..a7aa97c 100644 --- a/components/details/details-content.tsx +++ b/components/details/details-content.tsx @@ -426,14 +426,14 @@ export default function DetailContent() { -
+ {/*
Berita Utama -
+
*/}

Tinggalkan Balasan

diff --git a/components/form/article/create-article-form.tsx b/components/form/article/create-article-form.tsx index 4ef5c90..a8fbe78 100644 --- a/components/form/article/create-article-form.tsx +++ b/components/form/article/create-article-form.tsx @@ -9,7 +9,7 @@ import { CloudUploadIcon, TimesIcon } from "@/components/icons"; import Image from "next/image"; import ReactSelect from "react-select"; import makeAnimated from "react-select/animated"; -import { htmlToString } from "@/utils/global"; +import { convertDateFormatNoTime, htmlToString } from "@/utils/global"; import { close, error, loading, successToast } from "@/config/swal"; import { useRouter } from "next/navigation"; import Link from "next/link"; @@ -44,6 +44,13 @@ import GenerateSingleArticleForm from "./generate-ai-single-form"; import GenerateContentRewriteForm from "./generate-ai-content-rewrite-form"; import { Textarea } from "@/components/ui/textarea"; import { Badge } from "@/components/ui/badge"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { Calendar } from "@/components/ui/calendar"; +import DatePicker from "react-datepicker"; const CustomEditor = dynamic( () => { @@ -121,8 +128,8 @@ export default function CreateArticleForm() { "publish" ); const [isScheduled, setIsScheduled] = useState(false); - - const [startDateValue, setStartDateValue] = useState(null); + const [startDateValue, setStartDateValue] = useState(); + const [startTimeValue, setStartTimeValue] = useState(""); const { getRootProps, getInputProps } = useDropzone({ onDrop: (acceptedFiles) => { @@ -332,12 +339,34 @@ export default function CreateArticleForm() { } } - if (status === "scheduled") { + if (status === "scheduled" && startDateValue) { + // ambil waktu, default 00:00 jika belum diisi + const [hours, minutes] = startTimeValue + ? startTimeValue.split(":").map(Number) + : [0, 0]; + + // gabungkan tanggal + waktu + const combinedDate = new Date(startDateValue); + combinedDate.setHours(hours, minutes, 0, 0); + + // format: 2025-10-08 14:30:00 + const formattedDateTime = `${combinedDate.getFullYear()}-${String( + combinedDate.getMonth() + 1 + ).padStart(2, "0")}-${String(combinedDate.getDate()).padStart( + 2, + "0" + )} ${String(combinedDate.getHours()).padStart(2, "0")}:${String( + combinedDate.getMinutes() + ).padStart(2, "0")}:00`; + const request = { id: articleId, - date: `${startDateValue?.year}-${startDateValue?.month}-${startDateValue?.day}`, + date: formattedDateTime, }; + + console.log("📤 Sending schedule request:", request); const res = await createArticleSchedule(request); + console.log("✅ Schedule response:", res); } close(); @@ -812,32 +841,49 @@ export default function CreateArticleForm() {

- {/* {isScheduled && ( -
-
+ {isScheduled && ( +
+ {/* Pilih tanggal */} +

Tanggal

- + setStartDateValue(date ?? undefined) + } + dateFormat="yyyy-MM-dd" + className="w-full border rounded-lg px-2 py-1 text-black cursor-pointer h-[150px]" + placeholderText="Pilih tanggal" />
+ + {/* Pilih waktu */} +
+

Waktu

+ setStartTimeValue(e.target.value)} + className="w-full border rounded-lg px-2 py-[6px] text-black" + /> +
- )} */} + )}
diff --git a/components/landing-page/headers-guard.tsx b/components/landing-page/headers-guard.tsx index b249c3b..4e60cb4 100644 --- a/components/landing-page/headers-guard.tsx +++ b/components/landing-page/headers-guard.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import { getListArticle } from "@/service/article"; +import { getAdvertise } from "@/service/advertisement"; type Article = { id: number; @@ -17,6 +18,15 @@ type Article = { files: { fileUrl: string; file_alt: string }[]; }; +type Advertise = { + id: number; + title: string; + description: string; + placement: string; + contentFileUrl: string; + redirectLink: string; +}; + export default function HeaderGuard() { const [articles, setArticles] = useState([]); const [page, setPage] = useState(1); @@ -29,6 +39,36 @@ export default function HeaderGuard() { endDate: null, }); + const [bannerAd, setBannerAd] = useState(null); + + useEffect(() => { + initStateAdver(); + }, []); + + async function initStateAdver() { + const req = { + limit: 100, + page: 1, + sort: "desc", + sortBy: "created_at", + isPublish: true, + }; + + try { + const res = await getAdvertise(req); + const data: Advertise[] = res?.data?.data || [1]; + + // filter iklan dengan placement = "banner" + const banner = data.find((ad) => ad.placement === "jumbotron"); + + if (banner) { + setBannerAd(banner); + } + } catch (err) { + console.error("Error fetching advertisement:", err); + } + } + useEffect(() => { initState(); }, [page, showData, startDateValue, selectedCategories]); @@ -160,12 +200,32 @@ export default function HeaderGuard() { ))}
- Berita Utama + {bannerAd ? ( + +
+ {bannerAd.title +
+
+ ) : ( + Berita Utama + )}

diff --git a/components/landing-page/headers-latest.tsx b/components/landing-page/headers-latest.tsx index d0126a0..74b09e3 100644 --- a/components/landing-page/headers-latest.tsx +++ b/components/landing-page/headers-latest.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import { getListArticle } from "@/service/article"; +import { getAdvertise } from "@/service/advertisement"; type Article = { id: number; @@ -17,6 +18,15 @@ type Article = { files: { fileUrl: string; file_alt: string }[]; }; +type Advertise = { + id: number; + title: string; + description: string; + placement: string; + contentFileUrl: string; + redirectLink: string; +}; + export default function HeaderLatest() { const [articles, setArticles] = useState([]); const [page, setPage] = useState(1); @@ -29,6 +39,36 @@ export default function HeaderLatest() { endDate: null, }); + const [bannerAd, setBannerAd] = useState(null); + + useEffect(() => { + initStateAdver(); + }, []); + + async function initStateAdver() { + const req = { + limit: 100, + page: 1, + sort: "desc", + sortBy: "created_at", + isPublish: true, + }; + + try { + const res = await getAdvertise(req); + const data: Advertise[] = res?.data?.data || [1]; + + // filter iklan dengan placement = "banner" + const banner = data.find((ad) => ad.placement === "jumbotron"); + + if (banner) { + setBannerAd(banner); + } + } catch (err) { + console.error("Error fetching advertisement:", err); + } + } + useEffect(() => { initState(); }, [page, showData, startDateValue, selectedCategories]); @@ -161,12 +201,32 @@ export default function HeaderLatest() {

))}
- Berita Utama + {bannerAd ? ( + +
+ {bannerAd.title +
+
+ ) : ( + Berita Utama + )}

diff --git a/components/landing-page/headers-opinion.tsx b/components/landing-page/headers-opinion.tsx index ca40882..8e56741 100644 --- a/components/landing-page/headers-opinion.tsx +++ b/components/landing-page/headers-opinion.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import { getListArticle } from "@/service/article"; +import { getAdvertise } from "@/service/advertisement"; type Article = { id: number; @@ -17,6 +18,15 @@ type Article = { files: { fileUrl: string; file_alt: string }[]; }; +type Advertise = { + id: number; + title: string; + description: string; + placement: string; + contentFileUrl: string; + redirectLink: string; +}; + export default function HeaderOpinion() { const [articles, setArticles] = useState([]); const [page, setPage] = useState(1); @@ -29,6 +39,36 @@ export default function HeaderOpinion() { endDate: null, }); + const [bannerAd, setBannerAd] = useState(null); + + useEffect(() => { + initStateAdver(); + }, []); + + async function initStateAdver() { + const req = { + limit: 100, + page: 1, + sort: "desc", + sortBy: "created_at", + isPublish: true, + }; + + try { + const res = await getAdvertise(req); + const data: Advertise[] = res?.data?.data || [1]; + + // filter iklan dengan placement = "banner" + const banner = data.find((ad) => ad.placement === "jumbotron"); + + if (banner) { + setBannerAd(banner); + } + } catch (err) { + console.error("Error fetching advertisement:", err); + } + } + useEffect(() => { initState(); }, [page, showData, startDateValue, selectedCategories]); @@ -160,12 +200,32 @@ export default function HeaderOpinion() {

))}
- Berita Utama + {bannerAd ? ( + +
+ {bannerAd.title +
+
+ ) : ( + Berita Utama + )}

diff --git a/components/landing-page/headers-peace.tsx b/components/landing-page/headers-peace.tsx index 34c98ad..4dde7c2 100644 --- a/components/landing-page/headers-peace.tsx +++ b/components/landing-page/headers-peace.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import { getListArticle } from "@/service/article"; +import { getAdvertise } from "@/service/advertisement"; type Article = { id: number; @@ -17,6 +18,15 @@ type Article = { files: { fileUrl: string; file_alt: string }[]; }; +type Advertise = { + id: number; + title: string; + description: string; + placement: string; + contentFileUrl: string; + redirectLink: string; +}; + export default function HeaderPeace() { const [articles, setArticles] = useState([]); const [page, setPage] = useState(1); @@ -29,6 +39,36 @@ export default function HeaderPeace() { endDate: null, }); + const [bannerAd, setBannerAd] = useState(null); + + useEffect(() => { + initStateAdver(); + }, []); + + async function initStateAdver() { + const req = { + limit: 100, + page: 1, + sort: "desc", + sortBy: "created_at", + isPublish: true, + }; + + try { + const res = await getAdvertise(req); + const data: Advertise[] = res?.data?.data || [1]; + + // filter iklan dengan placement = "banner" + const banner = data.find((ad) => ad.placement === "jumbotron"); + + if (banner) { + setBannerAd(banner); + } + } catch (err) { + console.error("Error fetching advertisement:", err); + } + } + useEffect(() => { initState(); }, [page, showData, startDateValue, selectedCategories]); @@ -160,12 +200,32 @@ export default function HeaderPeace() {

))}
- Berita Utama + {bannerAd ? ( + +
+ {bannerAd.title +
+
+ ) : ( + Berita Utama + )}

diff --git a/components/landing-page/headers-popular.tsx b/components/landing-page/headers-popular.tsx index c281a2b..e704f65 100644 --- a/components/landing-page/headers-popular.tsx +++ b/components/landing-page/headers-popular.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import { getListArticle } from "@/service/article"; +import { getAdvertise } from "@/service/advertisement"; type Article = { id: number; @@ -17,6 +18,15 @@ type Article = { files: { fileUrl: string; file_alt: string }[]; }; +type Advertise = { + id: number; + title: string; + description: string; + placement: string; + contentFileUrl: string; + redirectLink: string; +}; + export default function HeaderPopular() { const [articles, setArticles] = useState([]); const [page, setPage] = useState(1); @@ -29,6 +39,36 @@ export default function HeaderPopular() { endDate: null, }); + const [bannerAd, setBannerAd] = useState(null); + + useEffect(() => { + initStateAdver(); + }, []); + + async function initStateAdver() { + const req = { + limit: 100, + page: 1, + sort: "desc", + sortBy: "created_at", + isPublish: true, + }; + + try { + const res = await getAdvertise(req); + const data: Advertise[] = res?.data?.data || [1]; + + // filter iklan dengan placement = "banner" + const banner = data.find((ad) => ad.placement === "jumbotron"); + + if (banner) { + setBannerAd(banner); + } + } catch (err) { + console.error("Error fetching advertisement:", err); + } + } + useEffect(() => { initState(); }, [page, showData, startDateValue, selectedCategories]); @@ -160,12 +200,32 @@ export default function HeaderPopular() {

))}
- Berita Utama + {bannerAd ? ( + +
+ {bannerAd.title +
+
+ ) : ( + Berita Utama + )}

diff --git a/components/landing-page/headers.tsx b/components/landing-page/headers.tsx index db0bdbc..27f9098 100644 --- a/components/landing-page/headers.tsx +++ b/components/landing-page/headers.tsx @@ -12,6 +12,7 @@ type Article = { categoryName: string; createdAt: string; createdByName: string; + customCreatorName: string; thumbnailUrl: string; categories: { title: string; @@ -87,7 +88,7 @@ export default function Beranda() { {item.title}

- {item.createdByName} + {item.createdByName || item?.customCreatorName} {new Date(item.createdAt).toLocaleDateString("en-US", { month: "long", diff --git a/components/landing-page/lifestyle.tsx b/components/landing-page/lifestyle.tsx index 86ba51f..46e83fd 100644 --- a/components/landing-page/lifestyle.tsx +++ b/components/landing-page/lifestyle.tsx @@ -7,6 +7,7 @@ import { getListArticle } from "@/service/article"; import { CommentIcon } from "../icons/sidebar-icon"; import { Button } from "../ui/button"; import { MessageCircle } from "lucide-react"; +import { getAdvertise } from "@/service/advertisement"; type Article = { id: number; @@ -15,10 +16,20 @@ type Article = { categoryName: string; createdAt: string; createdByName: string; + customCreatorName: string; thumbnailUrl: string; categories: { title: string }[]; }; +type Advertise = { + id: number; + title: string; + description: string; + placement: string; + contentFileUrl: string; + redirectLink: string; +}; + export default function Lifestyle() { const [page, setPage] = useState(1); const [totalPage, setTotalPage] = useState(1); @@ -32,6 +43,36 @@ export default function Lifestyle() { endDate: null, }); + const [bannerAd, setBannerAd] = useState(null); + + useEffect(() => { + initStateAdver(); + }, []); + + async function initStateAdver() { + const req = { + limit: 100, + page: 1, + sort: "desc", + sortBy: "created_at", + isPublish: true, + }; + + try { + const res = await getAdvertise(req); + const data: Advertise[] = res?.data?.data || [1]; + + // filter iklan dengan placement = "banner" + const banner = data.find((ad) => ad.placement === "jumbotron"); + + if (banner) { + setBannerAd(banner); + } + } catch (err) { + console.error("Error fetching advertisement:", err); + } + } + useEffect(() => { fetchArticles(); }, [page, showData, startDateValue, selectedCategories]); @@ -84,7 +125,9 @@ export default function Lifestyle() { {item.title}

- {item.createdByName} + + {item.createdByName || item?.customCreatorName} + • {new Date(item.createdAt).toLocaleDateString("id-ID", { @@ -139,12 +182,32 @@ export default function Lifestyle() {
- Berita Utama + {bannerAd ? ( + +
+ {bannerAd.title +
+
+ ) : ( + Berita Utama + )}
@@ -273,7 +336,9 @@ export default function Lifestyle() { {item.title}
- {item.createdByName} + + {item.createdByName || item?.customCreatorName} + - {new Date(item.createdAt).toLocaleDateString("id-ID", { @@ -303,12 +368,32 @@ export default function Lifestyle() {
- Kolom Iklan + {bannerAd ? ( + +
+ {bannerAd.title +
+
+ ) : ( + Berita Utama + )}
@@ -382,7 +467,9 @@ export default function Lifestyle() { {item.title}
- {item.createdByName} + + {item.createdByName || item?.customCreatorName} + - {new Date(item.createdAt).toLocaleDateString("id-ID", { @@ -400,14 +487,14 @@ export default function Lifestyle() { ))}
-
+ {/*
Berita Utama -
+
*/} {visibleCount < articles.length && (
@@ -454,7 +541,7 @@ export default function Lifestyle() {

- {item.createdByName} + {item.createdByName || item?.customCreatorName} - @@ -473,14 +560,14 @@ export default function Lifestyle() { ))}
-
+ {/*
Berita Utama -
+
*/} {visibleCount < articles.length && (
diff --git a/components/landing-page/on-the-spot.tsx b/components/landing-page/on-the-spot.tsx index 531f30a..ac004b8 100644 --- a/components/landing-page/on-the-spot.tsx +++ b/components/landing-page/on-the-spot.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import { getListArticle } from "@/service/article"; +import { getAdvertise } from "@/service/advertisement"; type Article = { id: number; @@ -12,6 +13,7 @@ type Article = { categoryName: string; createdAt: string; createdByName: string; + customCreatorName: string; thumbnailUrl: string; categories: { title: string; @@ -22,6 +24,15 @@ type Article = { }[]; }; +type Advertise = { + id: number; + title: string; + description: string; + placement: string; + contentFileUrl: string; + redirectLink: string; +}; + export default function OnTheSpot() { const [page, setPage] = useState(1); const [totalPage, setTotalPage] = useState(1); @@ -33,6 +44,35 @@ export default function OnTheSpot() { startDate: null, endDate: null, }); + const [bannerAd, setBannerAd] = useState(null); + + useEffect(() => { + initStateAdver(); + }, []); + + async function initStateAdver() { + const req = { + limit: 100, + page: 1, + sort: "desc", + sortBy: "created_at", + isPublish: true, + }; + + try { + const res = await getAdvertise(req); + const data: Advertise[] = res?.data?.data || []; + + // filter iklan dengan placement = "banner" + const banner = data.find((ad) => ad.placement === "banner"); + + if (banner) { + setBannerAd(banner); + } + } catch (err) { + console.error("Error fetching advertisement:", err); + } + } useEffect(() => { initState(); @@ -93,7 +133,7 @@ export default function OnTheSpot() { {item.title}

- {item.createdByName} + {item.createdByName || item?.customCreatorName} {new Date(item.createdAt).toLocaleDateString("en-US", { month: "long", @@ -105,13 +145,33 @@ export default function OnTheSpot() { ))}

-
- Berita Utama +
+ {bannerAd ? ( + +
+ {bannerAd.title +
+
+ ) : ( + Berita Utama + )}