feat: update campaign detail

This commit is contained in:
hanif salafi 2025-07-20 23:56:02 +07:00
parent 968f2642cc
commit fd021426d1
5 changed files with 400 additions and 27 deletions

View File

@ -127,7 +127,7 @@ const AccountListTable = () => {
return ( return (
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3"> <div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
<div className="flex justify-between mb-10 items-center"> <div className="flex justify-between mb-3 items-center">
<p className="text-xl font-medium text-default-900">Daftar Akun</p> <p className="text-xl font-medium text-default-900">Daftar Akun</p>
<div className="flex flex-row gap-3"> <div className="flex flex-row gap-3">
<Link href="/admin/broadcast/campaign-list/account-list/create"> <Link href="/admin/broadcast/campaign-list/account-list/create">

View File

@ -0,0 +1,387 @@
'use client'
import "react-datepicker/dist/react-datepicker.css";
import { Icon } from "@iconify/react";
import Link from "next/link";
import { useRouter, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import { useMediaQuery } from "react-responsive";
import { close, loading } from "@/config/swal";
import ReactDatePicker from "react-datepicker";
import { getOnlyDate } from "@/utils/globals";
import AccountListTable from "../../account-list/component/table";
import {
ColumnDef,
ColumnFiltersState,
PaginationState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Button } from "@/components/ui/button";
import TablePagination from "@/components/table/table-pagination";
import {
getMediaBlastCampaignById,
getMediaBlastBroadcastList
} from "@/service/broadcast/broadcast";
// Types
interface CampaignData {
id: string;
no: number;
mediaBlastCampaignId: string;
mediaBlastCampaign: {
title: string;
};
subject: string;
type: string;
status: string;
sendDate: string;
}
interface PaginatedResponse {
content: CampaignData[];
totalPages: number;
totalElements: number;
}
interface PageProps {
params: {
id: string;
locale: string;
};
searchParams: {
page?: string;
size?: string;
};
}
export default function BroadcastCampaignDetail({ params, searchParams }: PageProps) {
const router = useRouter();
const pathname = usePathname();
const { id, locale } = params;
const [getData, setGetData] = useState<CampaignData[]>([]);
const [totalPage, setTotalPage] = useState<number>(0);
const [totalData, setTotalData] = useState<number>(0);
const [activeTab, setActiveTab] = useState<"sent" | "schedule" | "account-list">("sent");
const { page, size } = searchParams;
const [calenderState, setCalenderState] = useState<boolean>(false);
const [typeFilter, setTypeFilter] = useState<string>("email");
const [dateRange, setDateRange] = useState<[Date, Date]>([new Date(), new Date()]);
const [startDate, endDate] = dateRange;
const [startDateString, setStartDateString] = useState<string | undefined>();
const [endDateString, setEndDateString] = useState<string | undefined>();
// Table state
const [sorting, setSorting] = useState<SortingState>([]);
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
const [rowSelection, setRowSelection] = useState({});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: parseInt(size || "10"),
});
const pages = page ? parseInt(page) - 1 : 0;
const currentPage = page ? parseInt(page) : 1;
const pageSize = parseInt(size || "10");
const isFHD = useMediaQuery({
minWidth: 1920,
});
const setCurrentPage = (pageNumber: number) => {
const params = new URLSearchParams(searchParams);
params.set('page', pageNumber.toString());
router.push(`${pathname}?${params.toString()}`);
};
async function getListPaginationData() {
loading();
console.log("Type : ", typeFilter);
console.log("Date : ", startDateString, endDateString);
try {
const res = await getMediaBlastBroadcastList(
pages,
activeTab === "schedule",
startDateString || "",
endDateString || "",
typeFilter,
id
);
close();
if (res?.data?.data) {
setupData(res.data.data);
}
} catch (error) {
console.error("Error fetching data:", error);
close();
}
}
useEffect(() => {
getListPaginationData();
}, [currentPage, pageSize, activeTab, endDateString, startDateString, typeFilter]);
function setupData(rawData: PaginatedResponse) {
console.log("raw", rawData);
if (rawData !== undefined) {
const dataContent = rawData?.content;
const data: CampaignData[] = [];
dataContent.forEach((element, i) => {
element.no = (currentPage - 1) * pageSize + i + 1;
data.push(element);
});
setGetData(data);
setTotalPage(rawData?.totalPages);
setTotalData(rawData?.totalElements);
}
}
const columns: ColumnDef<CampaignData>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
{
accessorKey: "mediaBlastCampaign.title",
header: "Campaign",
cell: ({ row }) => (
<Link href={`/${locale}/admin/broadcast/campaign-list/detail/${row.original.mediaBlastCampaignId}`} className="text-dark">
<span className="font-weight-bold">{row.original.mediaBlastCampaign?.title}</span>
</Link>
),
},
{
accessorKey: "subject",
header: "Judul",
cell: ({ row }) => (
<Link href={`/${locale}/admin/broadcast/content/detail/${row.original.id}`} className="text-dark">
<span className="font-weight-bold">{row.getValue("subject")}</span>
</Link>
),
},
{
accessorKey: "type",
header: "Tipe",
cell: ({ row }) => (
<div className="text-right text-black">
{row.getValue("type")}
</div>
)
},
{
accessorKey: "status",
header: "Status",
cell: ({ row }) => (
<div className="text-right text-black">
{row.getValue("status")}
</div>
)
},
{
accessorKey: "sendDate",
header: "Tanggal & Waktu",
cell: ({ row }) => (
<div className="text-black">
{row.getValue("sendDate")}
</div>
)
}
];
const table = useReactTable({
data: getData,
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,
},
});
useEffect(() => {
function initState() {
if (startDate != null && endDate != null) {
setStartDateString(getOnlyDate(startDate));
setEndDateString(getOnlyDate(endDate));
}
}
console.log('date range', dateRange);
initState();
}, [calenderState, startDate, endDate]);
const handleTypeFilter = (type: string) => {
setTypeFilter(type);
};
return (
<div className="bg-white container-fluid rounded rounded-xl">
<div className="mt-1 p-4">
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-4">
<Button
onClick={() => setActiveTab("sent")}
size="md"
className={`hover:text-white ${
activeTab === "sent"
? "bg-indigo-600 text-white "
: "bg-white text-black "
}`}
>
Sent
</Button>
<Button
onClick={() => setActiveTab("schedule")}
size="md"
className={`hover:text-white ${
activeTab === "schedule"
? "bg-indigo-600 text-white "
: "bg-white text-black "
}`}
>
Schedule
</Button>
<Button
onClick={() => setActiveTab("account-list")}
size="md"
className={`hover:text-white ${
activeTab === "account-list"
? "bg-indigo-600 text-white "
: "bg-white text-black "
}`}
>
List Akun
</Button>
</div>
{activeTab === "account-list" ? (
<AccountListTable />
) : (
<>
<div className="broadcast-filter flex flex-column gap-3 mb-4">
<div className="flex flex-row gap-1 border-2 rounded-md w-fit h-fit">
<Button
onClick={() => handleTypeFilter("email")}
className={`hover:text-white ${
typeFilter === "email"
? "bg-black text-white "
: "bg-white text-black "
}`}
size='sm'
>
Email Blast
</Button>
<Button
onClick={() => handleTypeFilter("wa")}
className={`hover:text-white ${
typeFilter === "wa"
? "bg-black text-white "
: "bg-white text-black "
}`}
size='sm'
>
WhatsApp Blast
</Button>
</div>
<div className="dashboard-date-picker">
<div className="mx-6 my-1">
<ReactDatePicker
selectsRange
startDate={startDate}
endDate={endDate}
onChange={(update) => {
setDateRange(update as [Date, Date]);
}}
placeholderText="Pilih Tanggal"
onCalendarClose={() => setCalenderState(!calenderState)}
className="form-control rounded-pill"
/>
</div>
</div>
</div>
<div className="w-full overflow-x-auto">
<Table className="overflow-hidden mt-3">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
className="h-[75px]"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
<TablePagination
table={table}
totalData={totalData}
totalPage={totalPage}
/>
</div>
</>
)}
</div>
</div>
);
}

View File

@ -17,32 +17,6 @@ export default function AdminBroadcast() {
<div> <div>
<SiteBreadcrumb /> <SiteBreadcrumb />
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3"> <div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
<Button
rounded="md"
onClick={() => setTab("Email Blast")}
className={` hover:text-white
${
tab === "Email Blast"
? "bg-black text-white "
: "bg-white text-black "
}`}
>
Email Blast
</Button>
<Button
rounded="md"
onClick={() => setTab("WhatsApp Blast")}
className={` hover:text-white
${
tab === "WhatsApp Blast"
? "bg-black text-white "
: "bg-white text-black "
}`}
>
WhatsApp Blast
</Button>
</div>
{tab === "Email Blast" && <BroadcastEmailTable />} {tab === "Email Blast" && <BroadcastEmailTable />}
{tab === "WhatsApp Blast" && <BroadcastWhatsAppTable />} {tab === "WhatsApp Blast" && <BroadcastWhatsAppTable />}
</div> </div>

View File

@ -100,6 +100,18 @@ export async function saveMediaBlastBroadcast(data: any) {
return httpPostInterceptor(url, data); return httpPostInterceptor(url, data);
} }
export async function getMediaBlastBroadcastList(
page: number,
isScheduled: boolean,
startDate: string,
endDate: string,
type: string,
campaignId: string
) {
const url = `media/blast/broadcast/list?enablePage=1&page=${page}&isScheduled=${isScheduled}&type=${type}&startDate=${startDate}&endDate=${endDate}&campaignId=${campaignId}`;
return httpGetInterceptor(url);
}
export async function listDataPopUp( export async function listDataPopUp(
page: number, page: number,
limit: string, limit: string,