From 7b35a9b3dac2480017b1e6efdaff40775dddbdd5 Mon Sep 17 00:00:00 2001 From: Anang Yusman Date: Tue, 3 Jun 2025 08:59:48 +0800 Subject: [PATCH 1/2] [QUDO-133,QUDO-130,QUDO-134] feat:ui kalender,ui iklan, change api AI content rewrite --- .../admin/settings/banner/component/table.tsx | 160 ++++++++++-------- .../admin/settings/banner/page.tsx | 7 +- .../admin/settings/iklan/component/table.tsx | 5 + .../(protected)/admin/settings/iklan/page.tsx | 33 +--- .../admin/settings/popup/component/table.tsx | 160 ++++++++++-------- .../component/calendar-polri-table.tsx | 8 +- .../content-management/rewrite/page.tsx | 127 +++++++++++--- components/form/content/spit-convert-form.tsx | 14 +- .../form/schedule/form-calendar-polri.tsx | 116 +++++++++++++ components/form/schedule/live-report-form.tsx | 8 +- components/form/setting/form-add-iklan.tsx | 114 +++++++++++++ service/content/ai.ts | 11 +- 12 files changed, 551 insertions(+), 212 deletions(-) create mode 100644 components/form/schedule/form-calendar-polri.tsx create mode 100644 components/form/setting/form-add-iklan.tsx diff --git a/app/[locale]/(protected)/admin/settings/banner/component/table.tsx b/app/[locale]/(protected)/admin/settings/banner/component/table.tsx index 03175fb1..5b57778c 100644 --- a/app/[locale]/(protected)/admin/settings/banner/component/table.tsx +++ b/app/[locale]/(protected)/admin/settings/banner/component/table.tsx @@ -65,13 +65,17 @@ import { listEnableCategory } from "@/service/content/content"; import { Checkbox } from "@/components/ui/checkbox"; import { close, loading } from "@/config/swal"; import { Link } from "@/i18n/routing"; +import { data } from "jquery"; +import { useToast } from "@/components/ui/use-toast"; +import { setBanner } from "@/service/settings/settings"; +import { id } from "date-fns/locale"; -const ContentListTable = () => { +const ContentListBanner = () => { const router = useRouter(); const searchParams = useSearchParams(); const [showData, setShowData] = React.useState("10"); const [categories, setCategories] = React.useState(); - const [dataTable, setDataTable] = React.useState([]); + const [data, setData] = React.useState([]); const [totalData, setTotalData] = React.useState(1); const [sorting, setSorting] = React.useState([]); const [columnFilters, setColumnFilters] = React.useState( @@ -86,28 +90,9 @@ const ContentListTable = () => { }); const [categoryFilter, setCategoryFilter] = React.useState([]); const [statusFilter, setStatusFilter] = React.useState([]); + const [selectedItems, setSelectedItems] = React.useState([]); const [page, setPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1); - const table = useReactTable({ - data: dataTable, - columns, - onSortingChange: setSorting, - onColumnFiltersChange: setColumnFilters, - getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel(), - getSortedRowModel: getSortedRowModel(), - getFilteredRowModel: getFilteredRowModel(), - onColumnVisibilityChange: setColumnVisibility, - onRowSelectionChange: setRowSelection, - onPaginationChange: setPagination, - state: { - sorting, - columnFilters, - columnVisibility, - rowSelection, - pagination, - }, - }); let typingTimer: any; const doneTypingInterval = 1500; @@ -159,7 +144,7 @@ const ContentListTable = () => { console.log("contentData : ", data); - setDataTable(contentData); + setData(contentData); setTotalData(data?.totalElements); setTotalPage(data?.totalPages); close(); @@ -200,6 +185,38 @@ const ContentListTable = () => { } }; + const handleSelect = (id: number) => { + setSelectedItems((prev) => + prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id] + ); + }; + + const handleSelectAll = () => { + if (selectedItems.length === data.length) { + setSelectedItems([]); + } else { + setSelectedItems(data.map((item: any) => Number(item.id))); + } + }; + + const { toast } = useToast(); + + const handleBanner = async (ids: number[]) => { + try { + await Promise.all(ids.map((id) => setBanner(id, true))); + toast({ + title: "Sukses", + description: `${ids.length} item berhasil dijadikan banner.`, + }); + } catch (err) { + toast({ + title: "Gagal", + description: "Terjadi kesalahan saat menjadikan banner.", + variant: "destructive", + }); + } + }; + return ( <>
@@ -210,7 +227,7 @@ const ContentListTable = () => { onKeyDown={handleKeyDown} className="max-w-[300px]" /> -
+ {/*
+
*/} +
+
+ {/* Header select all + action */} +
+
+ + Pilih Semua +
+ {selectedItems.length > 0 && ( + + )} +
+ + {/* Grid Cards */} +
+ {data?.map((item: any) => ( +
+
+ handleSelect(item.id)} + /> +
+ {item.title} +
+

{item.title}

+
+
+ ))} +
+
+
+ 1 +
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ))} - - ))} - - - {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - )) - ) : ( - - - No results. - - - )} - -
- ); }; -export default ContentListTable; +export default ContentListBanner; diff --git a/app/[locale]/(protected)/admin/settings/banner/page.tsx b/app/[locale]/(protected)/admin/settings/banner/page.tsx index c3fcbe33..8385b071 100644 --- a/app/[locale]/(protected)/admin/settings/banner/page.tsx +++ b/app/[locale]/(protected)/admin/settings/banner/page.tsx @@ -5,6 +5,7 @@ import { useState } from "react"; import BannerListTable from "./component/banner-table"; import { Button } from "@/components/ui/button"; +import ContentListBanner from "./component/table"; export default function AdminBanner() { const [selectedTab, setSelectedTab] = useState("content"); @@ -44,7 +45,11 @@ export default function AdminBanner() { - {selectedTab === "content" ? : } + {selectedTab === "content" ? ( + + ) : ( + + )} ); diff --git a/app/[locale]/(protected)/admin/settings/iklan/component/table.tsx b/app/[locale]/(protected)/admin/settings/iklan/component/table.tsx index 21f35ad3..e9d72d23 100644 --- a/app/[locale]/(protected)/admin/settings/iklan/component/table.tsx +++ b/app/[locale]/(protected)/admin/settings/iklan/component/table.tsx @@ -30,6 +30,7 @@ import { ChevronRight, Eye, MoreVertical, + Plus, Search, SquarePen, Trash2, @@ -65,6 +66,7 @@ import { listEnableCategory } from "@/service/content/content"; import { Checkbox } from "@/components/ui/checkbox"; import { close, loading } from "@/config/swal"; import { Link } from "@/i18n/routing"; +import { TambahIklanModal } from "@/components/form/setting/form-add-iklan"; const IklanList = () => { const router = useRouter(); @@ -202,6 +204,9 @@ const IklanList = () => { return ( <> +
+ +
-
- {selectedTab === "content" ? "List Media" : " List Pop Up"} - -
- - -
-
- - {selectedTab === "content" ? : } +
); diff --git a/app/[locale]/(protected)/admin/settings/popup/component/table.tsx b/app/[locale]/(protected)/admin/settings/popup/component/table.tsx index f23f6709..bd716f74 100644 --- a/app/[locale]/(protected)/admin/settings/popup/component/table.tsx +++ b/app/[locale]/(protected)/admin/settings/popup/component/table.tsx @@ -65,13 +65,17 @@ import { listEnableCategory } from "@/service/content/content"; import { Checkbox } from "@/components/ui/checkbox"; import { close, loading } from "@/config/swal"; import { Link } from "@/i18n/routing"; +import { data } from "jquery"; +import { useToast } from "@/components/ui/use-toast"; +import { setBanner } from "@/service/settings/settings"; +import { id } from "date-fns/locale"; -const PopUpList = () => { +const ContentListPopUp = () => { const router = useRouter(); const searchParams = useSearchParams(); const [showData, setShowData] = React.useState("10"); const [categories, setCategories] = React.useState(); - const [dataTable, setDataTable] = React.useState([]); + const [data, setData] = React.useState([]); const [totalData, setTotalData] = React.useState(1); const [sorting, setSorting] = React.useState([]); const [columnFilters, setColumnFilters] = React.useState( @@ -86,28 +90,9 @@ const PopUpList = () => { }); const [categoryFilter, setCategoryFilter] = React.useState([]); const [statusFilter, setStatusFilter] = React.useState([]); + const [selectedItems, setSelectedItems] = React.useState([]); const [page, setPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1); - const table = useReactTable({ - data: dataTable, - columns, - onSortingChange: setSorting, - onColumnFiltersChange: setColumnFilters, - getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel(), - getSortedRowModel: getSortedRowModel(), - getFilteredRowModel: getFilteredRowModel(), - onColumnVisibilityChange: setColumnVisibility, - onRowSelectionChange: setRowSelection, - onPaginationChange: setPagination, - state: { - sorting, - columnFilters, - columnVisibility, - rowSelection, - pagination, - }, - }); let typingTimer: any; const doneTypingInterval = 1500; @@ -159,7 +144,7 @@ const PopUpList = () => { console.log("contentData : ", data); - setDataTable(contentData); + setData(contentData); setTotalData(data?.totalElements); setTotalPage(data?.totalPages); close(); @@ -200,6 +185,38 @@ const PopUpList = () => { } }; + const handleSelect = (id: number) => { + setSelectedItems((prev) => + prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id] + ); + }; + + const handleSelectAll = () => { + if (selectedItems.length === data.length) { + setSelectedItems([]); + } else { + setSelectedItems(data.map((item: any) => Number(item.id))); + } + }; + + const { toast } = useToast(); + + const handleBanner = async (ids: number[]) => { + try { + await Promise.all(ids.map((id) => setBanner(id, true))); + toast({ + title: "Sukses", + description: `${ids.length} item berhasil dijadikan banner.`, + }); + } catch (err) { + toast({ + title: "Gagal", + description: "Terjadi kesalahan saat menjadikan banner.", + variant: "destructive", + }); + } + }; + return ( <>
@@ -210,7 +227,7 @@ const PopUpList = () => { onKeyDown={handleKeyDown} className="max-w-[300px]" /> -
+ {/*
+
*/} +
+
+ {/* Header select all + action */} +
+
+ + Pilih Semua +
+ {selectedItems.length > 0 && ( + + )} +
+ + {/* Grid Cards */} +
+ {data?.map((item: any) => ( +
+
+ handleSelect(item.id)} + /> +
+ {item.title} +
+

{item.title}

+
+
+ ))} +
+
+
+ 1 +
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ))} - - ))} - - - {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - )) - ) : ( - - - No results. - - - )} - -
- ); }; -export default PopUpList; +export default ContentListPopUp; 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 10116e1e..3dc28244 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 @@ -57,6 +57,7 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Label } from "@/components/ui/label"; +import { CalendarPolriAdd } from "@/components/form/schedule/form-calendar-polri"; const CalendarPolriTable = () => { const router = useRouter(); @@ -161,12 +162,7 @@ const CalendarPolriTable = () => { {t("calendar-polri")} {t("schedule")}
- - - +
diff --git a/app/[locale]/(public)/content-management/rewrite/page.tsx b/app/[locale]/(public)/content-management/rewrite/page.tsx index 4430eef1..6a1b1d9d 100644 --- a/app/[locale]/(public)/content-management/rewrite/page.tsx +++ b/app/[locale]/(public)/content-management/rewrite/page.tsx @@ -4,14 +4,24 @@ import HeaderManagement from "@/components/landing-page/header-management"; import SidebarManagement from "@/components/landing-page/sidebar-management"; import { close, error, loading, successCallback } from "@/config/swal"; import { getCookiesDecrypt } from "@/lib/utils"; -import { checkWishlistStatus, deleteWishlist, getContentRewritePagination, getInfoProfile, saveWishlist } from "@/service/landing/landing"; +import { + checkWishlistStatus, + deleteWishlist, + getContentRewritePagination, + getInfoProfile, + saveWishlist, +} from "@/service/landing/landing"; import { useRouter, useSearchParams } from "next/navigation"; import React, { useEffect, useState } from "react"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; import { Card, CardContent } from "@/components/ui/card"; import { Link } from "@/i18n/routing"; -import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; import { Icon } from "@iconify/react/dist/iconify.js"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; @@ -63,7 +73,10 @@ const page = () => { }, [page, category, title, refresh]); async function getData() { - const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : category || ""; + const filter = + categoryFilter?.length > 0 + ? categoryFilter?.sort().join(",") + : category || ""; const name = title == undefined ? "" : title; const format = formatFilter == undefined ? "" : formatFilter?.join(","); @@ -119,7 +132,9 @@ const page = () => { }; const copyToClip = async (url: any) => { - await navigator.clipboard.writeText(`https://mediahub.polri.go.id/image/detail/${url}`); + await navigator.clipboard.writeText( + `https://mediahub.polri.go.id/image/detail/${url}` + ); setCopySuccess("Copied"); // toast.success("Link Berhasil Di Copy"); }; @@ -211,48 +226,110 @@ const page = () => { {contentImage?.length > 0 ? (
{contentImage?.map((image: any) => ( - +
- +
- -

{image?.title}

+ +

+ {image?.title} +

- + - + -

handleSaveWishlist(image?.mediaUpload?.id)} className="cursor-pointer flex flex-row gap-2 hover:text-red-800"> - -

{t("save")}

+
+ handleSaveWishlist( + image?.mediaUpload?.id + ) + } + className="cursor-pointer flex flex-row gap-2 hover:text-red-800" + > + +

+ {t("save")} +

- + -

Content Rewrite

+

+ Content Rewrite +

-

{t("shareTo")}

+

+ {t("shareTo")} +

-

{t("destinationEmail")}

- setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder={t("pressEnter")} /> +

+ {t("destinationEmail")} +

+ + setEmailShareInput( + event.target.value + ) + } + onKeyPress={handleEmailList} + type="email" + placeholder={t("pressEnter")} + />
-
@@ -272,7 +349,13 @@ const page = () => {
) : (

- empty + empty

)}
diff --git a/components/form/content/spit-convert-form.tsx b/components/form/content/spit-convert-form.tsx index 40f96b6f..5a381f13 100644 --- a/components/form/content/spit-convert-form.tsx +++ b/components/form/content/spit-convert-form.tsx @@ -45,11 +45,16 @@ import "swiper/css"; import "swiper/css/navigation"; import { FreeMode, Navigation, Pagination, Thumbs } from "swiper/modules"; import { request } from "http"; -import { generateDataArticle, getDetailArticle } from "@/service/content/ai"; +import { + generateDataArticle, + generateDataRewrite, + getDetailArticle, +} from "@/service/content/ai"; import { getCookiesDecrypt } from "@/lib/utils"; import dynamic from "next/dynamic"; import { error } from "@/lib/swal"; import { useTranslations } from "next-intl"; +import { contextType } from "cleave.js/react"; const imageSchema = z.object({ contentTitle: z.string().min(1, { message: "Judul diperlukan" }), @@ -501,12 +506,15 @@ export default function FormConvertSPIT() { additionalKeywords: detail?.contentTitle, targetCountry: null, articleSize: "news", + contextType: "text", + context: detail?.contentDescription, projectId: 2, createdBy: roleId, - clientId: "ngDLPPiorplznw2jTqVe3YFCz5xqKfUJ", + sentiment: "Humorous", + clientId: "7QTW8cMojyayt6qnhqTOeJaBI70W4EaQ", }; - const res = await generateDataArticle(request); + const res = await generateDataRewrite(request); close(); if (res?.error) { diff --git a/components/form/schedule/form-calendar-polri.tsx b/components/form/schedule/form-calendar-polri.tsx new file mode 100644 index 00000000..b411f0f0 --- /dev/null +++ b/components/form/schedule/form-calendar-polri.tsx @@ -0,0 +1,116 @@ +// components/TambahIklanModal.tsx +"use client"; + +import * as React from "react"; +import { + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { CalendarIcon, Plus } from "lucide-react"; +import { useTranslations } from "next-intl"; +import DatePicker from "react-datepicker"; +import { id } from "date-fns/locale"; +import "react-datepicker/dist/react-datepicker.css"; + +export function CalendarPolriAdd() { + const [open, setOpen] = React.useState(false); + const t = useTranslations("Schedule"); + const [eventDate, setEventDate] = React.useState(new Date()); + return ( + + + + + + + Buat Jadwal Kalender Polri + + +
+
+

Tanggal Acara

+
+ setEventDate(date)} + dateFormat="dd MMMM yyyy" + locale={id} + className="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" + /> + +
+
+
+

Publish Area

+
+ {["Semua", "Nasional", "Polda", "Satker", "International"].map( + (label) => ( + + ) + )} +
+
+ + {/* Nama Iklan */} +
+

Nama Acara

+ +
+ + {/* Upload */} +
+ + + +
+ Drag your file(s) or{" "} + + browse + +
+
Max 10 MB files are allowed
+
+ + {/* Deskripsi */} +
+

Deskripsi

+