fix: button validate in detail media tracking

This commit is contained in:
Sabda Yagra 2026-01-09 10:22:43 +07:00
parent 280ea508e9
commit 933cdb1100
6 changed files with 195 additions and 36 deletions

View File

@ -32,6 +32,7 @@ import {
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible"; import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible";
import { validateMediaLink } from "@/service/media-tracking/media-tracking"; import { validateMediaLink } from "@/service/media-tracking/media-tracking";
import toast from "react-hot-toast";
const columns: ColumnDef<any>[] = [ const columns: ColumnDef<any>[] = [
{ {
@ -82,7 +83,7 @@ const columns: ColumnDef<any>[] = [
); );
}, },
}, },
{ {
id: "validation", id: "validation",
header: "Validasi", header: "Validasi",
cell: ({ row, table }) => { cell: ({ row, table }) => {
@ -96,13 +97,27 @@ const columns: ColumnDef<any>[] = [
}; };
const handleValid = async () => { const handleValid = async () => {
await validateMediaLink(original.id, true); try {
updateRow({ isValid: true }); await validateMediaLink(original.id, true);
updateRow({
isValid: true,
});
} catch (err: any) {
toast.error(err.message);
}
}; };
const handleInvalid = async () => { const handleInvalid = async () => {
await validateMediaLink(original.id, false); try {
updateRow({ isValid: false, link: undefined }); await validateMediaLink(original.id, false);
updateRow({
isValid: false,
});
} catch (err: any) {
toast.error(err.message);
}
}; };
if (!link) { if (!link) {
@ -111,7 +126,11 @@ const columns: ColumnDef<any>[] = [
if (isValid === true) { if (isValid === true) {
return ( return (
<Button size="sm" className="bg-green-600 hover:bg-green-700"> <Button
size="sm"
className="bg-green-600 hover:bg-green-700"
disabled
>
Valid Valid
</Button> </Button>
); );
@ -120,14 +139,10 @@ const columns: ColumnDef<any>[] = [
return ( return (
<div className="flex gap-2"> <div className="flex gap-2">
<Button size="sm" variant="outline" onClick={handleValid}> <Button size="sm" variant="outline" onClick={handleValid}>
Valid Relevan
</Button> </Button>
<Button <Button size="sm" variant="outline" onClick={handleInvalid}>
size="sm" Tidak Relevan
variant="outline"
onClick={handleInvalid}
>
Tidak Valid
</Button> </Button>
</div> </div>
); );

View File

@ -116,6 +116,15 @@ const NewsDetailTable = () => {
onColumnVisibilityChange: setColumnVisibility, onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection, onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination, onPaginationChange: setPagination,
meta: {
updateData: (rowIndex: number, value: Partial<any>) => {
setDataTable((old) =>
old.map((row, index) =>
index === rowIndex ? { ...row, ...value } : row
)
);
},
},
state: { state: {
sorting, sorting,
columnFilters, columnFilters,
@ -253,7 +262,7 @@ const NewsDetailTable = () => {
totalData={totalData} totalData={totalData}
totalPage={totalPage} totalPage={totalPage}
/> />
</div> </div>
); );
}; };

View File

@ -1,7 +1,15 @@
import * as React from "react"; import * as React from "react";
import { ColumnDef } from "@tanstack/react-table"; import { ColumnDef } from "@tanstack/react-table";
import { exportMediaTrackingToExcel } from "@/utils/export-media-tracking";
import { DownloadIcon, Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react"; 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 { cn } from "@/lib/utils";
import { import {
DropdownMenu, DropdownMenu,
@ -44,18 +52,39 @@ const columns: ColumnDef<any>[] = [
header: "Judul", header: "Judul",
cell: ({ row }) => <span>{row.getValue("title")}</span>, cell: ({ row }) => <span>{row.getValue("title")}</span>,
}, },
// {
// accessorKey: "resultTotal",
// header: () => <div className="text-center w-full">Jumlah Amplifikasi</div>,
// cell: ({ row }) => {
// const value = row.getValue("resultTotal") as number | string | null;
// const finalValue =
// value === null || value === undefined || value === ""
// ? 0
// : Number(value);
// return <div className="text-center w-full">{finalValue}</div>;
// },
// },
{ {
accessorKey: "resultTotal", accessorKey: "resultTotal",
header: () => <div className="text-center w-full">Jumlah Amplifikasi</div>, header: () => <div className="text-center w-full">Jumlah Amplifikasi</div>,
cell: ({ row }) => { cell: ({ row }) => {
const value = row.getValue("resultTotal") as number | string | null; const totalRaw = row.getValue("resultTotal") as number | string | null;
const finalValue = const total =
value === null || value === undefined || value === "" totalRaw === null || totalRaw === undefined || totalRaw === ""
? 0 ? 0
: Number(value); : Number(totalRaw);
return <div className="text-center w-full">{finalValue}</div>; const invalidTotal = 0;
return (
<div className="text-center w-full font-medium">
{total}
<span className="text-muted-foreground">/{invalidTotal}</span>
</div>
);
}, },
}, },
@ -148,8 +177,22 @@ const columns: ColumnDef<any>[] = [
row.original.mediaUpload.fileType.secondaryName.toLowerCase()} row.original.mediaUpload.fileType.secondaryName.toLowerCase()}
</DropdownMenuItem> </DropdownMenuItem>
</Link> </Link>
<DropdownMenuItem className="p-2 border-b cursor-pointer text-default-700 group focus:bg-default focus:text-primary-foreground items-center rounded-none"> <DropdownMenuItem
<DownloadIcon className="w-4 h-4 me-1.5"/> className="p-2 border-b cursor-pointer text-default-700 group rounded-none focus:bg-default focus:text-primary-foreground "
onClick={async () => {
try {
loading();
await exportMediaTrackingToExcel({
mediaTrackingId: row.original.id,
});
close();
} catch (e: any) {
close();
error(e.message || "Gagal export data");
}
}}
>
<DownloadIcon className="w-4 h-4 me-1.5" />
<p>Download</p> <p>Download</p>
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>

View File

@ -1,3 +1,4 @@
import api from "@/src/lib/api";
import { import {
httpGetInterceptor, httpGetInterceptor,
httpPostInterceptor, httpPostInterceptor,
@ -62,25 +63,31 @@ export async function listDataTracking(
); );
} }
export async function listDataAllNonPagination(search: string) { export async function listDataAllNonPagination(search: string) {
return await httpGetInterceptor( return await httpGetInterceptor(
`media/public/list?enablePage=0&sort=desc&title=${search || ""}` `media/public/list?enablePage=0&sort=desc&title=${search || ""}`
); );
} }
// service/media-validation.ts
export const validateMediaLink = async ( export const validateMediaLink = async (
id: string | number, resultId: number,
isValid: boolean 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) => { return res.data;
setTimeout(() => { } catch (error: any) {
resolve({ throw new Error(
success: true, error?.response?.data?.messages?.[0] ||
isValid, "Gagal memperbarui status relevansi"
}); );
}, 600); }
}); };
};

12
src/lib/api.ts Normal file
View File

@ -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;

View File

@ -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`);
}