feat: update campaign detail
This commit is contained in:
parent
968f2642cc
commit
fd021426d1
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue