Merge branch 'main' of https://gitlab.com/hanifsalafi/mediahub_redesign into prod
This commit is contained in:
commit
71ad5b6a04
|
|
@ -31,6 +31,7 @@ import {
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} 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";
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
|
|
@ -52,12 +53,85 @@ const columns: ColumnDef<any>[] = [
|
||||||
<span className="normal-case">{row.getValue("title")}</span>
|
<span className="normal-case">{row.getValue("title")}</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// accessorKey: "link",
|
||||||
|
// header: "Link Berita",
|
||||||
|
// cell: ({ row }) => (
|
||||||
|
// <span className="normal-case">{row.getValue("link")}</span>
|
||||||
|
// ),
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
accessorKey: "link",
|
accessorKey: "link",
|
||||||
header: "Link Berita",
|
header: "Link Berita",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => {
|
||||||
<span className="normal-case">{row.getValue("link")}</span>
|
const link = row.getValue<string>("link");
|
||||||
),
|
|
||||||
|
if (!link) {
|
||||||
|
return <span className="text-muted-foreground">-</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href={link}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-blue-600 underline hover:text-blue-800 break-all"
|
||||||
|
>
|
||||||
|
{link}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "validation",
|
||||||
|
header: "Validasi",
|
||||||
|
cell: ({ row, table }) => {
|
||||||
|
const original = row.original;
|
||||||
|
|
||||||
|
const isValid = original.isValid;
|
||||||
|
const link = original.link;
|
||||||
|
|
||||||
|
const updateRow = (data: Partial<any>) => {
|
||||||
|
table.options.meta?.updateData(row.index, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleValid = async () => {
|
||||||
|
await validateMediaLink(original.id, true);
|
||||||
|
updateRow({ isValid: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInvalid = async () => {
|
||||||
|
await validateMediaLink(original.id, false);
|
||||||
|
updateRow({ isValid: false, link: undefined });
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!link) {
|
||||||
|
return <span className="text-muted-foreground">-</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValid === true) {
|
||||||
|
return (
|
||||||
|
<Button size="sm" className="bg-green-600 hover:bg-green-700">
|
||||||
|
Valid
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button size="sm" variant="outline" onClick={handleValid}>
|
||||||
|
Valid
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={handleInvalid}
|
||||||
|
>
|
||||||
|
Tidak Valid
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -253,7 +253,7 @@ const NewsDetailTable = () => {
|
||||||
totalData={totalData}
|
totalData={totalData}
|
||||||
totalPage={totalPage}
|
totalPage={totalPage}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ColumnDef } from "@tanstack/react-table";
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
import { DownloadIcon, Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
|
|
@ -141,13 +141,17 @@ const columns: ColumnDef<any>[] = [
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
<Link href={`/admin/media-tracking/detail/${row.original.id}`}>
|
<Link href={`/admin/media-tracking/detail/${row.original.id}`}>
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground items-center rounded-none">
|
<DropdownMenuItem className="p-2 border-b cursor-pointer text-default-700 group focus:bg-default focus:text-primary-foreground items-center rounded-none">
|
||||||
<Eye className="w-4 h-4 me-1.5" />
|
<Eye className="w-4 h-4 me-1.5" />
|
||||||
View
|
View
|
||||||
{row.original.mediaUpload.fileType.secondaryName &&
|
{row.original.mediaUpload.fileType.secondaryName &&
|
||||||
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">
|
||||||
|
<DownloadIcon className="w-4 h-4 me-1.5"/>
|
||||||
|
<p>Download</p>
|
||||||
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,8 @@
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// export default AdvertisementPlacements;
|
// export default AdvertisementPlacements;
|
||||||
import { listDataAdvertisements } from "@/service/broadcast/broadcast";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
|
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
|
||||||
|
|
@ -93,35 +94,40 @@ interface Advertisement {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AdvertisementPlacements = (props: {
|
interface AdvertisementPlacementsProps {
|
||||||
placement: string;
|
placement: string;
|
||||||
data: Advertisement[];
|
data?: Advertisement[]; // ⬅️ PENTING: optional
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}) => {
|
}
|
||||||
const [ads, setAds] = useState<Advertisement[] | undefined[]>([]);
|
|
||||||
|
const AdvertisementPlacements = ({
|
||||||
|
placement,
|
||||||
|
data = [], // ⬅️ DEFAULT VALUE (KUNCI UTAMA)
|
||||||
|
loading,
|
||||||
|
}: AdvertisementPlacementsProps) => {
|
||||||
|
const [ads, setAds] = useState<(Advertisement | undefined)[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!props.loading && props.data.length > 0) {
|
if (loading || data.length === 0) return;
|
||||||
const filtered = props.data.filter((a) =>
|
|
||||||
a.placements.includes(props.placement)
|
|
||||||
);
|
|
||||||
|
|
||||||
let temps: Advertisement[] | undefined[] = [];
|
const filtered = data.filter((a) => a.placements?.includes(placement));
|
||||||
temps[0] = filtered.find((b) => b.placements.includes("top"));
|
|
||||||
temps[1] = filtered.find((b) => b.placements.includes("bottom"));
|
const temps: (Advertisement | undefined)[] = [];
|
||||||
setAds(temps);
|
temps[0] = filtered.find((b) => b.placements?.includes("top"));
|
||||||
}
|
temps[1] = filtered.find((b) => b.placements?.includes("bottom"));
|
||||||
}, [props.data, props.loading, props.placement]);
|
|
||||||
|
setAds(temps);
|
||||||
|
}, [data, loading, placement]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`sticky top-0 space-y-4 ${
|
className={`sticky top-0 space-y-4 ${
|
||||||
props.placement === "left" ? "ml-14" : "mr-14"
|
placement === "left" ? "ml-14" : "mr-14"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{props.loading && <p className="text-sm text-gray-500">Loading...</p>}
|
{loading && <p className="text-sm text-gray-500">Loading...</p>}
|
||||||
|
|
||||||
{ads?.map(
|
{ads.map(
|
||||||
(ad) =>
|
(ad) =>
|
||||||
ad && (
|
ad && (
|
||||||
<Dialog key={ad.id}>
|
<Dialog key={ad.id}>
|
||||||
|
|
@ -132,6 +138,7 @@ const AdvertisementPlacements = (props: {
|
||||||
className="w-full cursor-pointer rounded-lg shadow-md hover:opacity-90 transition"
|
className="w-full cursor-pointer rounded-lg shadow-md hover:opacity-90 transition"
|
||||||
/>
|
/>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
||||||
<DialogContent className="max-w-4xl p-0 bg-transparent border-0 shadow-none">
|
<DialogContent className="max-w-4xl p-0 bg-transparent border-0 shadow-none">
|
||||||
<img
|
<img
|
||||||
src={ad.contentFileUrl || ad.imageUrl}
|
src={ad.contentFileUrl || ad.imageUrl}
|
||||||
|
|
|
||||||
|
|
@ -68,3 +68,20 @@ export async function listDataAllNonPagination(search: string) {
|
||||||
`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 (
|
||||||
|
id: string | number,
|
||||||
|
isValid: boolean
|
||||||
|
) => {
|
||||||
|
console.log("API DUMMY:", { id, isValid });
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve({
|
||||||
|
success: true,
|
||||||
|
isValid,
|
||||||
|
});
|
||||||
|
}, 600);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
// src/types/react-table.d.ts
|
||||||
|
import "@tanstack/react-table";
|
||||||
|
|
||||||
|
declare module "@tanstack/react-table" {
|
||||||
|
interface TableMeta<TData> {
|
||||||
|
updateData: (rowIndex: number, value: Partial<TData>) => void;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue