diff --git a/app/[locale]/(protected)/admin/media-tracking/detail/component/column.tsx b/app/[locale]/(protected)/admin/media-tracking/detail/component/column.tsx index 62e32d12..866579b5 100644 --- a/app/[locale]/(protected)/admin/media-tracking/detail/component/column.tsx +++ b/app/[locale]/(protected)/admin/media-tracking/detail/component/column.tsx @@ -32,6 +32,7 @@ import { } from "@/components/ui/dialog"; import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible"; import { validateMediaLink } from "@/service/media-tracking/media-tracking"; +import toast from "react-hot-toast"; const columns: ColumnDef[] = [ { @@ -82,7 +83,7 @@ const columns: ColumnDef[] = [ ); }, }, - { + { id: "validation", header: "Validasi", cell: ({ row, table }) => { @@ -96,13 +97,27 @@ const columns: ColumnDef[] = [ }; const handleValid = async () => { - await validateMediaLink(original.id, true); - updateRow({ isValid: true }); + try { + await validateMediaLink(original.id, true); + + updateRow({ + isValid: true, + }); + } catch (err: any) { + toast.error(err.message); + } }; const handleInvalid = async () => { - await validateMediaLink(original.id, false); - updateRow({ isValid: false, link: undefined }); + try { + await validateMediaLink(original.id, false); + + updateRow({ + isValid: false, + }); + } catch (err: any) { + toast.error(err.message); + } }; if (!link) { @@ -111,7 +126,11 @@ const columns: ColumnDef[] = [ if (isValid === true) { return ( - ); @@ -120,14 +139,10 @@ const columns: ColumnDef[] = [ return (
-
); diff --git a/app/[locale]/(protected)/admin/media-tracking/detail/component/table.tsx b/app/[locale]/(protected)/admin/media-tracking/detail/component/table.tsx index f6230e7f..fa0ae7c0 100644 --- a/app/[locale]/(protected)/admin/media-tracking/detail/component/table.tsx +++ b/app/[locale]/(protected)/admin/media-tracking/detail/component/table.tsx @@ -116,6 +116,15 @@ const NewsDetailTable = () => { onColumnVisibilityChange: setColumnVisibility, onRowSelectionChange: setRowSelection, onPaginationChange: setPagination, + meta: { + updateData: (rowIndex: number, value: Partial) => { + setDataTable((old) => + old.map((row, index) => + index === rowIndex ? { ...row, ...value } : row + ) + ); + }, + }, state: { sorting, columnFilters, @@ -253,7 +262,7 @@ const NewsDetailTable = () => { totalData={totalData} totalPage={totalPage} /> - + ); }; diff --git a/app/[locale]/(protected)/admin/media-tracking/results/component/column.tsx b/app/[locale]/(protected)/admin/media-tracking/results/component/column.tsx index d06179ad..b95fcd11 100644 --- a/app/[locale]/(protected)/admin/media-tracking/results/component/column.tsx +++ b/app/[locale]/(protected)/admin/media-tracking/results/component/column.tsx @@ -1,7 +1,15 @@ import * as React from "react"; import { ColumnDef } from "@tanstack/react-table"; - -import { DownloadIcon, Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react"; +import { exportMediaTrackingToExcel } from "@/utils/export-media-tracking"; +import { loading, close } from "@/config/swal"; +import { error } from "@/lib/swal"; +import { + DownloadIcon, + Eye, + MoreVertical, + SquarePen, + Trash2, +} from "lucide-react"; import { cn } from "@/lib/utils"; import { DropdownMenu, @@ -44,18 +52,39 @@ const columns: ColumnDef[] = [ header: "Judul", cell: ({ row }) => {row.getValue("title")}, }, + // { + // accessorKey: "resultTotal", + // header: () =>
Jumlah Amplifikasi
, + // cell: ({ row }) => { + // const value = row.getValue("resultTotal") as number | string | null; + + // const finalValue = + // value === null || value === undefined || value === "" + // ? 0 + // : Number(value); + + // return
{finalValue}
; + // }, + // }, { accessorKey: "resultTotal", header: () =>
Jumlah Amplifikasi
, cell: ({ row }) => { - const value = row.getValue("resultTotal") as number | string | null; + const totalRaw = row.getValue("resultTotal") as number | string | null; - const finalValue = - value === null || value === undefined || value === "" + const total = + totalRaw === null || totalRaw === undefined || totalRaw === "" ? 0 - : Number(value); + : Number(totalRaw); - return
{finalValue}
; + const invalidTotal = 0; + + return ( +
+ {total} + /{invalidTotal} +
+ ); }, }, @@ -148,8 +177,22 @@ const columns: ColumnDef[] = [ row.original.mediaUpload.fileType.secondaryName.toLowerCase()} - - + { + try { + loading(); + await exportMediaTrackingToExcel({ + mediaTrackingId: row.original.id, + }); + close(); + } catch (e: any) { + close(); + error(e.message || "Gagal export data"); + } + }} + > +

Download

diff --git a/service/media-tracking/media-tracking.ts b/service/media-tracking/media-tracking.ts index 2cff4553..e3874a3b 100644 --- a/service/media-tracking/media-tracking.ts +++ b/service/media-tracking/media-tracking.ts @@ -1,3 +1,4 @@ +import api from "@/src/lib/api"; import { httpGetInterceptor, httpPostInterceptor, @@ -62,25 +63,31 @@ export async function listDataTracking( ); } + export async function listDataAllNonPagination(search: string) { return await httpGetInterceptor( `media/public/list?enablePage=0&sort=desc&title=${search || ""}` ); } -// service/media-validation.ts export const validateMediaLink = async ( - id: string | number, - isValid: boolean + resultId: number, + isRelevant: boolean ) => { - console.log("API DUMMY:", { id, isValid }); + try { + const res = await api.put( + "/media/tracking/monitoring/results/relevant", + { + resultId, + isRelevant, + } + ); - return new Promise((resolve) => { - setTimeout(() => { - resolve({ - success: true, - isValid, - }); - }, 600); - }); -}; + return res.data; + } catch (error: any) { + throw new Error( + error?.response?.data?.messages?.[0] || + "Gagal memperbarui status relevansi" + ); + } +}; \ No newline at end of file diff --git a/src/lib/api.ts b/src/lib/api.ts new file mode 100644 index 00000000..68ee6bf0 --- /dev/null +++ b/src/lib/api.ts @@ -0,0 +1,12 @@ +import axios from "axios"; + +const api = axios.create({ + baseURL: "https://mediahub.polri.go.id/api/v2", + headers: { + "Content-Type": "application/json", + }, +}); + +export default api; + + diff --git a/utils/export-media-tracking.ts b/utils/export-media-tracking.ts new file mode 100644 index 00000000..91499aab --- /dev/null +++ b/utils/export-media-tracking.ts @@ -0,0 +1,73 @@ +import * as XLSX from "xlsx"; +import { getMediaTrackingResult } from "@/service/media-tracking/media-tracking"; +import { saveAs } from "file-saver"; + +type ExportParams = { + mediaTrackingId: number; +}; + +export async function exportMediaTrackingToExcel({ + mediaTrackingId, +}: ExportParams) { + let page = 0; + let totalPages = 1; + const allData: any[] = []; + + while (page < totalPages) { + const res = await getMediaTrackingResult({ + id: mediaTrackingId, + page, + }); + + const data = res?.data?.data; + if (!data) break; + + totalPages = data.totalPages; + allData.push(...data.content); + + page++; + } + + if (allData.length === 0) { + throw new Error("Tidak ada data untuk di-export"); + } + + function extractDomain(url?: string): string { + if (!url) return "-"; + try { + return new URL(url).hostname; + } catch { + return "-"; + } + } + + const rows = allData.map((item, index) => ({ + No: index + 1, + "Media Online": extractDomain(item.link), + "Judul Berita": item.title ?? "-", + "Link Berita": item.link ?? "-", + Validasi: item.validationStatus ?? "-", + View: item.viewCount ?? 0, + Share: item.shareCount ?? 0, + Komentar: item.commentCount ?? 0, + Tanggal: item.createdAt + ? new Date(item.createdAt).toLocaleString("id-ID") + : "-", + })); + + const worksheet = XLSX.utils.json_to_sheet(rows); + const workbook = XLSX.utils.book_new(); + + XLSX.utils.book_append_sheet(workbook, worksheet, "Media Tracking"); + + const excelBuffer = XLSX.write(workbook, { + bookType: "xlsx", + type: "array", + }); + + const blob = new Blob([excelBuffer], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }); + + saveAs(blob, `media-tracking-${mediaTrackingId}.xlsx`); +}