From 7fc65833b0e942136a83905e9fc2010758fdfdc7 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Sat, 7 Jun 2025 02:59:54 +0700 Subject: [PATCH] feat: update advertisements and calendars in landing --- .../admin/settings/iklan/component/table.tsx | 2 - .../component/calendar-polri-table.tsx | 5 +- ...anner.tsx => advertisement-placements.tsx} | 35 +- components/landing-page/event-calender.tsx | 319 ++++++++++++++---- .../landing-page/search-section-polda.tsx | 19 +- .../landing-page/search-section-satker.tsx | 19 +- components/landing-page/search-section.tsx | 19 +- service/broadcast/broadcast.ts | 4 +- service/schedule/schedule.ts | 5 +- 9 files changed, 286 insertions(+), 141 deletions(-) rename components/landing-page/{left-banner.tsx => advertisement-placements.tsx} (62%) diff --git a/app/[locale]/(protected)/admin/settings/iklan/component/table.tsx b/app/[locale]/(protected)/admin/settings/iklan/component/table.tsx index 48ee4e8c..e588bce4 100644 --- a/app/[locale]/(protected)/admin/settings/iklan/component/table.tsx +++ b/app/[locale]/(protected)/admin/settings/iklan/component/table.tsx @@ -154,8 +154,6 @@ const AdvertisementsList = () => { page - 1, showData, "", - categoryFilter?.sort().join(","), - statusFilter?.sort().join(",") ); const data = res?.data?.data; const contentData = data?.content; diff --git a/app/[locale]/(protected)/contributor/schedule/calendar-polri/component/calendar-polri-table.tsx b/app/[locale]/(protected)/contributor/schedule/calendar-polri/component/calendar-polri-table.tsx index bc7254ce..2791eeaf 100644 --- a/app/[locale]/(protected)/contributor/schedule/calendar-polri/component/calendar-polri-table.tsx +++ b/app/[locale]/(protected)/contributor/schedule/calendar-polri/component/calendar-polri-table.tsx @@ -44,7 +44,7 @@ import { useRouter, useSearchParams } from "next/navigation"; import TablePagination from "@/components/table/table-pagination"; import columns from "./columns"; import { - paginationCalendar, + getCalendarPagination, paginationSchedule, } from "@/service/schedule/schedule"; import { CardHeader, CardTitle } from "@/components/ui/card"; @@ -120,10 +120,9 @@ const CalendarPolriTable = () => { async function fetchData() { try { - const res = await paginationCalendar( + const res = await getCalendarPagination( showData, page - 1, - 1, search, statusFilter ); diff --git a/components/landing-page/left-banner.tsx b/components/landing-page/advertisement-placements.tsx similarity index 62% rename from components/landing-page/left-banner.tsx rename to components/landing-page/advertisement-placements.tsx index 61b468e4..c8b72a35 100644 --- a/components/landing-page/left-banner.tsx +++ b/components/landing-page/advertisement-placements.tsx @@ -31,34 +31,25 @@ interface Advertisement { // }; // } -const LeftBanner = () => { +const AdvertisementPlacements = (props: { placement?: string }) => { const [ads, setAds] = useState([]); const [loading, setLoading] = useState(false); - const [showData, setShowData] = React.useState("10"); - const [categories, setCategories] = React.useState(); - const [dataTable, setDataTable] = React.useState([]); - const [totalData, setTotalData] = React.useState(1); - const [categoryFilter, setCategoryFilter] = React.useState([]); - const [statusFilter, setStatusFilter] = React.useState([]); - const [page, setPage] = React.useState(1); - const fetchData = async () => { try { setLoading(true); - const res = await listDataAdvertisements( page - 1, - showData, - "", - categoryFilter?.sort().join(","), - statusFilter?.sort().join(",")); + const res = await listDataAdvertisements( + 0, + "3", + "" + ); const data = res?.data?.data; const contentData = data?.content; - - contentData.forEach((item: Advertisement, index: number) => { - item.no = index + 1; - }); - - setAds(contentData); + if (props.placement == "left") { + setAds(contentData.slice(0,2)); + } else { + setAds(contentData.slice(2)); + } } catch (error) { console.error("Error fetching advertisements:", error); } finally { @@ -74,10 +65,10 @@ const LeftBanner = () => {
{loading &&

Loading...

} {ads.map((ad) => ( - {`Banner + {`Banner ))}
); }; -export default LeftBanner; +export default AdvertisementPlacements; diff --git a/components/landing-page/event-calender.tsx b/components/landing-page/event-calender.tsx index efd9553c..419fffa0 100644 --- a/components/landing-page/event-calender.tsx +++ b/components/landing-page/event-calender.tsx @@ -1,86 +1,285 @@ -import React from "react"; +import { getCalendarPagination } from "@/service/schedule/schedule"; +import React, { useEffect, useState } from "react"; + +interface CalendarItem { + id: number; + title: string; + description: string; + assignedTo: string; + assignedToLevel: string; + startDate: string; + endDate: string; + isActive: boolean; + createdById: number; + createdByName: string; + thumbnailUrl: string; + createdAt: string; + updatedAt: string; +} const EventCalender = () => { + // Get current date + const today = new Date(); + const currentMonth = today.getMonth(); + const currentYear = today.getFullYear(); + const currentDate = today.getDate(); + + const [events, setEvents] = useState([]); + const [selectedEvent, setSelectedEvent] = useState(null); + + // Month names in Indonesian + const monthNames = [ + "Januari", "Februari", "Maret", "April", "Mei", "Juni", + "Juli", "Agustus", "September", "Oktober", "November", "Desember" + ]; + + const fetchData = async () => { + try { + const res = await getCalendarPagination(100, 0); + const data = res?.data?.data; + const contentData = data?.content; + setEvents(contentData || []); + + // Set first event as selected by default + if (contentData && contentData.length > 0) { + setSelectedEvent(contentData[0]); + } + } catch (error) { + console.error("Error fetching calendar events:", error); + } + }; + + useEffect(() => { + fetchData(); + }, []); + + // Get first day of the month and number of days + const firstDayOfMonth = new Date(currentYear, currentMonth, 1); + const lastDayOfMonth = new Date(currentYear, currentMonth + 1, 0); + const daysInMonth = lastDayOfMonth.getDate(); + const startingDayOfWeek = firstDayOfMonth.getDay(); + + // Convert Sunday (0) to 7 for Monday-first week + const adjustedStartingDay = startingDayOfWeek === 0 ? 6 : startingDayOfWeek - 1; + + // Generate calendar days + const generateCalendarDays = () => { + const days = []; + + // Empty cells for days before the first day of month + for (let i = 0; i < adjustedStartingDay; i++) { + days.push(null); + } + + // Days of the month + for (let day = 1; day <= daysInMonth; day++) { + days.push(day); + } + + // Fill remaining cells to complete the grid (6 rows × 7 days = 42 cells) + while (days.length < 42) { + days.push(null); + } + + return days; + }; + + const calendarDays = generateCalendarDays(); + + // Helper function to extract day from date string + const getDateFromString = (dateString: string) => { + try { + const date = new Date(dateString); + if (date.getMonth() === currentMonth && date.getFullYear() === currentYear) { + return date.getDate(); + } + return null; + } catch { + return null; + } + }; + + // Helper function to format date range + const formatDateRange = (startDate: string, endDate: string) => { + try { + const start = new Date(startDate); + const end = new Date(endDate); + + const startDay = start.getDate(); + const endDay = end.getDate(); + const startMonth = monthNames[start.getMonth()]; + const endMonth = monthNames[end.getMonth()]; + + if (startDay === endDay && startMonth === endMonth) { + return `${startDay} ${startMonth}`; + } else { + return `${startDay} ${startMonth} - ${endDay} ${endMonth}`; + } + } catch { + return "Tanggal tidak valid"; + } + }; + + // Helper function to format time range + const formatTimeRange = (startDate: string, endDate: string) => { + try { + const start = new Date(startDate); + const end = new Date(endDate); + + const startTime = start.toLocaleTimeString('id-ID', { + hour: '2-digit', + minute: '2-digit', + timeZone: 'Asia/Jakarta' + }); + const endTime = end.toLocaleTimeString('id-ID', { + hour: '2-digit', + minute: '2-digit', + timeZone: 'Asia/Jakarta' + }); + + return `${startTime} - ${endTime} WIB`; + } catch { + return "Waktu tidak tersedia"; + } + }; + + // Get event dates for highlighting calendar + const eventDates = events + .map(event => getDateFromString(event.startDate)) + .filter(date => date !== null); + return (

KALENDER ACARA

-
-
-
-
Mei 2025
+
+ {/* Left Side - Calendar and Event List */} +
+ {/* Mini Calendar */} +
+
+ {monthNames[currentMonth]} {currentYear} +
{["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((d) => ( -
+
{d}
))} - {[...Array(35)].map((_, i) => ( + {calendarDays?.map((day, index) => (
- {i >= 2 && i - 1} + {day}
))}
-
-
- HUT Polwan -
-
- HUT Polwan ke-76, Kapolri... -
-
- 16 Mei - 16 Mei -
-
-
-
- Olahraga -
-
- Olahraga Bersama Pad... -
-
- 22 Mei - 22 Mei -
+ {/* Event List */} +
+

+ Daftar Acara +

+ {events?.length === 0 ? ( +
+ Tidak ada acara yang tersedia
-
+ ) : ( + events.map((event) => ( +
setSelectedEvent(event)} + className={`flex items-center rounded-xl shadow-sm p-3 cursor-pointer transition-all duration-200 hover:shadow-md ${ + selectedEvent?.id === event.id + ? "bg-red-100 dark:bg-red-900/20 border-2 border-red-500" + : "bg-gray-200 dark:bg-zinc-800 hover:bg-gray-300 dark:hover:bg-zinc-700" + }`} + > + {event.title} { + const target = e.target as HTMLImageElement; + target.src = "/images/default-event.png"; + }} + /> +
+
+ {event.title} +
+
+ {formatDateRange(event.startDate, event.endDate)} +
+
+
+
+
+
+ )) + )}
- {/* Detail Acara */} -
-
- Detail Event -

- HUT Polwan ke-76, Kapolri Apresiasi Prestasi yang Diberikan -

-

- Kapolri Jenderal Pol. Listyo Sigit Prabowo memberikan apresiasi - kepada polisi wanita yang berprestasi... -

- - Lihat Selengkapnya - + {/* Right Side - Event Detail */} +
+
+

+ Detail Acara +

+ + {selectedEvent ? ( +
+ {selectedEvent.title} { + const target = e.target as HTMLImageElement; + target.src = "/images/default-event.png"; + }} + /> + +
+

+ {selectedEvent.title} +

+ +
+
+ Tanggal: + {formatDateRange(selectedEvent.startDate, selectedEvent.endDate)} +
+
+

+ {selectedEvent.description || "Tidak ada deskripsi tersedia."} +

+
+
+ +
+ +
+
+
+ ) : ( +
+ Pilih acara untuk melihat detail +
+ )}
@@ -88,4 +287,4 @@ const EventCalender = () => { ); }; -export default EventCalender; +export default EventCalender; \ No newline at end of file diff --git a/components/landing-page/search-section-polda.tsx b/components/landing-page/search-section-polda.tsx index 399333e7..1d6108b5 100644 --- a/components/landing-page/search-section-polda.tsx +++ b/components/landing-page/search-section-polda.tsx @@ -12,20 +12,7 @@ import AreaCoverageWorkUnits from "./area-coverage-and-work-units"; import EventCalender from "./event-calender"; import UserSurveyBox from "./survey-box"; import ScrollableContentPolda from "./scrollable-content-polda"; - -const LeftBanner = () => ( -
- Banner Kiri 1 - Banner Kiri 2 -
-); - -const RightBanner = () => ( -
- Banner Kanan 1 - Banner Kanan 2 -
-); +import AdvertisementPlacements from "./advertisement-placements"; const SearchSectionPolda = () => { const [contentType, setContentType] = useState("all"); @@ -35,7 +22,7 @@ const SearchSectionPolda = () => { return (
- +
@@ -49,7 +36,7 @@ const SearchSectionPolda = () => {
- +
); diff --git a/components/landing-page/search-section-satker.tsx b/components/landing-page/search-section-satker.tsx index 9d24a8a3..e9aefa93 100644 --- a/components/landing-page/search-section-satker.tsx +++ b/components/landing-page/search-section-satker.tsx @@ -13,20 +13,7 @@ import EventCalender from "./event-calender"; import UserSurveyBox from "./survey-box"; import ScrollableContentPolda from "./scrollable-content-polda"; import ScrollableContentSatker from "./scrollable-content-satker"; - -const LeftBanner = () => ( -
- Banner Kiri 1 - Banner Kiri 2 -
-); - -const RightBanner = () => ( -
- Banner Kanan 1 - Banner Kanan 2 -
-); +import AdvertisementPlacements from "./advertisement-placements"; const SearchSectionSatker = () => { const [contentType, setContentType] = useState("all"); @@ -36,7 +23,7 @@ const SearchSectionSatker = () => { return (
- +
@@ -50,7 +37,7 @@ const SearchSectionSatker = () => {
- +
); diff --git a/components/landing-page/search-section.tsx b/components/landing-page/search-section.tsx index 5307d836..9af0b96a 100644 --- a/components/landing-page/search-section.tsx +++ b/components/landing-page/search-section.tsx @@ -18,20 +18,7 @@ import ContentCategory from "./content-category"; import AreaCoverageWorkUnits from "./area-coverage-and-work-units"; import EventCalender from "./event-calender"; import UserSurveyBox from "./survey-box"; - -const LeftBanner = () => ( -
- Banner Kiri 1 - Banner Kiri 2 -
-); - -const RightBanner = () => ( -
- Banner Kanan 1 - Banner Kanan 2 -
-); +import AdvertisementPlacements from "./advertisement-placements"; const SearchSection = () => { const [contentType, setContentType] = useState("all"); @@ -44,7 +31,7 @@ const SearchSection = () => { style={{ backgroundImage: "url('/assets/background.png')" }} >
- +
@@ -58,7 +45,7 @@ const SearchSection = () => {
- +
); diff --git a/service/broadcast/broadcast.ts b/service/broadcast/broadcast.ts index b1ca2176..745c124e 100644 --- a/service/broadcast/broadcast.ts +++ b/service/broadcast/broadcast.ts @@ -20,11 +20,9 @@ export async function listDataAdvertisements( page: number, limit: string, search: string, - categoryFilter: string, - statusFilter: string ) { const name = search || ""; - const url = `advertisements/pagination?title=${name}&enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=1&categoryId=${categoryFilter}&statusId=${statusFilter}`; + const url = `advertisements/pagination?title=${search}&enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}`; return httpGetInterceptor(url); } diff --git a/service/schedule/schedule.ts b/service/schedule/schedule.ts index 62024a8a..b18531e5 100644 --- a/service/schedule/schedule.ts +++ b/service/schedule/schedule.ts @@ -20,17 +20,16 @@ export async function paginationSchedule( ); } -export async function paginationCalendar( +export async function getCalendarPagination( size: any, page: number, - type: any, title: string = "", statusFilter: number[] = [] ) { const statusQuery = statusFilter.length > 0 ? `&statusId=${statusFilter.join(",")}` : ""; return await httpGetInterceptor( - `calendars/pagination?enablePage=1&scheduleTypeId=${type}&page=${page}&size=${size}&title=${title}${statusQuery}` + `calendars/pagination?enablePage=1&page=${page}&size=${size}&title=${title}${statusQuery}` ); }