fix: adjust broadcast admin

This commit is contained in:
Sabda Yagra 2025-08-30 09:28:28 +07:00
parent e5541c3b35
commit e60b452780
6 changed files with 379 additions and 163 deletions

View File

@ -30,34 +30,34 @@ const columns: ColumnDef<any>[] = [
accessorKey: "accountName", accessorKey: "accountName",
header: "Nama", header: "Nama",
cell: ({ row }) => ( cell: ({ row }) => (
<span className="normal-case">{row.getValue("accountName")}</span> <span className="normal-case">{row.original.mediaBlastAccount.accountName}</span>
), ),
}, },
{ {
accessorKey: "accountType", accessorKey: "accountType",
header: "Tipe Akun", header: "Tipe Akun",
cell: ({ row }) => ( cell: ({ row }) => (
<span className="normal-case">{row.getValue("accountType")}</span> <span className="normal-case">{row.original.mediaBlastAccount.accountType}</span>
), ),
}, },
{ {
accessorKey: "accountCategory", accessorKey: "accountCategory",
header: "Kategory", header: "Kategory",
cell: ({ row }) => ( cell: ({ row }) => (
<span className="uppercase">{row.getValue("accountCategory")}</span> <span className="uppercase">{row.original.mediaBlastAccount.accountCategory}</span>
), ),
}, },
{ {
accessorKey: "emailAddress", accessorKey: "emailAddress",
header: "Email", header: "Email",
cell: ({ row }) => ( cell: ({ row }) => (
<span className="normal-case">{row.getValue("emailAddress")}</span> <span className="normal-case">{row.original.mediaBlastAccount.emailAddress}</span>
), ),
}, },
{ {
accessorKey: "whatsappNumber", accessorKey: "whatsappNumber",
header: "Whatsapp", header: "Whatsapp",
cell: ({ row }) => <span>{row.getValue("whatsappNumber")}</span>, cell: ({ row }) => <span>{row.original.mediaBlastAccount.whatsappNumber}</span>,
}, },
{ {
id: "actions", id: "actions",

View File

@ -2,7 +2,6 @@
import * as React from "react"; import * as React from "react";
import { import {
ColumnDef,
ColumnFiltersState, ColumnFiltersState,
PaginationState, PaginationState,
SortingState, SortingState,
@ -15,7 +14,6 @@ import {
useReactTable, useReactTable,
} from "@tanstack/react-table"; } from "@tanstack/react-table";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Table, Table,
TableBody, TableBody,
@ -24,25 +22,48 @@ import {
TableHeader, TableHeader,
TableRow, TableRow,
} from "@/components/ui/table"; } from "@/components/ui/table";
import { UserIcon } from "lucide-react"; import {
Dialog,
import { useRouter, useSearchParams } from "next/navigation"; DialogContent,
import TablePagination from "@/components/table/table-pagination"; DialogFooter,
import columns from "./column"; DialogHeader,
DialogTitle,
DialogTrigger,
DialogClose,
} from "@/components/ui/dialog";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { import {
Popover, Popover,
PopoverContent, PopoverContent,
PopoverTrigger, PopoverTrigger,
} from "@/components/ui/popover"; } from "@/components/ui/popover";
import { getMediaBlastAccountPage } from "@/service/broadcast/broadcast";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { close, loading } from "@/config/swal"; import { Icon } from "@iconify/react";
import { Link } from "@/i18n/routing"; import { useParams, useSearchParams } from "next/navigation";
import { Icon } from "@iconify/react/dist/iconify.js"; import { UserIcon } from "lucide-react";
import columns from "./column";
import TablePagination from "@/components/table/table-pagination";
import {
getMediaBlastCampaignAccountList,
deleteMediaBlastCampaignAccount,
saveMediaBlastCampaignAccount,
} from "@/service/broadcast/broadcast";
import { close, loading, error } from "@/config/swal";
const AccountListTable = () => { const AccountListTable = () => {
const router = useRouter(); const params = useParams();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const campaignId = params?.id as string;
const [dataTable, setDataTable] = React.useState<any[]>([]); const [dataTable, setDataTable] = React.useState<any[]>([]);
const [totalData, setTotalData] = React.useState<number>(1); const [totalData, setTotalData] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]); const [sorting, setSorting] = React.useState<SortingState>([]);
@ -56,10 +77,15 @@ const AccountListTable = () => {
pageIndex: 0, pageIndex: 0,
pageSize: 10, pageSize: 10,
}); });
const [page, setPage] = React.useState(1); const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1);
const [filtered, setFiltered] = React.useState<string[]>([]); const [filtered, setFiltered] = React.useState<string[]>([]);
// --- state utk Dialog Pilih Akun ---
const [accountCategory, setAccountCategory] = React.useState<string>("");
const [selectedAccount, setSelectedAccount] = React.useState<any[]>([]);
const [selectedCategory, setSelectedCategory] = React.useState<string>("");
const table = useReactTable({ const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,
@ -83,24 +109,24 @@ const AccountListTable = () => {
React.useEffect(() => { React.useEffect(() => {
const pageFromUrl = searchParams?.get("page"); const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) { if (pageFromUrl) setPage(Number(pageFromUrl));
setPage(Number(pageFromUrl));
}
}, [searchParams]); }, [searchParams]);
React.useEffect(() => { React.useEffect(() => {
fetchData(); fetchData();
}, [page]); }, [page, filtered]);
async function fetchData() { async function fetchData() {
try { try {
loading(); loading();
const res = await getMediaBlastAccountPage( const res = await getMediaBlastCampaignAccountList(
page - 1, page - 1,
filtered ? filtered.join(",") : "" filtered ? filtered.join(",") : "",
campaignId
); );
const data = res?.data?.data; const data = res?.data?.data;
const contentData = data?.content; const contentData = data?.content || [];
contentData.forEach((item: any, index: number) => { contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * 10 + index + 1; item.no = (page - 1) * 10 + index + 1;
}); });
@ -109,20 +135,43 @@ const AccountListTable = () => {
setTotalData(data?.totalElements); setTotalData(data?.totalElements);
setTotalPage(data?.totalPages); setTotalPage(data?.totalPages);
close(); close();
} catch (error) { } catch (err) {
console.error("Error fetching tasks:", error); console.error("Error fetching tasks:", err);
close();
} }
} }
// --- API helpers ---
async function doDeleteAccount(id: string) {
loading();
const response = await deleteMediaBlastCampaignAccount(id);
close();
if (response?.error) {
error(response.message);
return;
}
fetchData();
}
async function saveCampaignAccount() {
for (const acc of selectedAccount) {
const request = {
mediaBlastCampaignId: campaignId,
mediaBlastAccountId: acc.id,
};
const response = await saveMediaBlastCampaignAccount(request);
if (response?.error) {
error(response.message);
}
}
fetchData();
}
const handleFilter = (id: string, checked: boolean) => { const handleFilter = (id: string, checked: boolean) => {
let temp = [...filtered]; let temp = [...filtered];
if (checked) { if (checked) temp = [...temp, id];
temp = [...temp, id]; else temp = temp.filter((a) => a !== id);
} else {
temp = temp.filter((a) => a !== id);
}
setFiltered(temp); setFiltered(temp);
console.log("sss", temp);
}; };
return ( return (
@ -130,20 +179,103 @@ const AccountListTable = () => {
<div className="flex justify-between mb-3 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"> {/* === Dialog Pilih Akun === */}
<Button color="primary" size="md" className="text-sm"> <Dialog>
<Icon icon="tdesign:user-add-filled" /> <DialogTrigger asChild>
Tambah Akun <Button size="sm" className="text-sm">
</Button> <Icon icon="tdesign:user-add-filled" className="mr-2" />
</Link> Pilih Akun
{/* <Link href="/admin/broadcast/campaign-list/import"> </Button>
<Button color="success" size="md" className="text-sm"> </DialogTrigger>
<UserIcon /> <DialogContent className="max-w-lg">
Import Akun <DialogHeader>
</Button> <DialogTitle>Pilih Akun Untuk Campaign Ini</DialogTitle>
</Link> */} </DialogHeader>
<RadioGroup
value={accountCategory}
onValueChange={(val) => setAccountCategory(val)}
className="space-y-3"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="all-account" id="all-account" />
<Label htmlFor="all-account">Semua Akun</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="kategori" id="kategori" />
<Label htmlFor="kategori">Kategori</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="custom" id="custom" />
<Label htmlFor="custom">Custom</Label>
</div>
</RadioGroup>
<div className="mt-4 space-y-4">
{accountCategory === "custom" && (
<>
<Select
onValueChange={(val) =>
setSelectedAccount([...selectedAccount, { id: val }])
}
>
<SelectTrigger>
<SelectValue placeholder="Pilih akun" />
</SelectTrigger>
<SelectContent>
{dataTable.map((acc) => (
<SelectItem key={acc.id} value={acc.id}>
{acc.accountName}
</SelectItem>
))}
</SelectContent>
</Select>
{selectedAccount.length < 1 && (
<p className="text-sm text-red-500">
Pilih minimal 1 akun
</p>
)}
</>
)}
{accountCategory === "kategori" && (
<Select onValueChange={(val) => setSelectedCategory(val)}>
<SelectTrigger>
<SelectValue placeholder="Pilih kategori" />
</SelectTrigger>
<SelectContent>
<SelectItem value="umum">Umum</SelectItem>
<SelectItem value="polri">Polri</SelectItem>
<SelectItem value="ksp">KSP</SelectItem>
<SelectItem value="jurnalis">Jurnalis</SelectItem>
</SelectContent>
</Select>
)}
{accountCategory === "all-account" && (
<p className="text-sm text-gray-600">Semua akun dipilih</p>
)}
</div>
<DialogFooter>
<Button
onClick={saveCampaignAccount}
disabled={
accountCategory === "custom" && selectedAccount.length < 1
}
>
Simpan
</Button>
<DialogClose asChild>
<Button variant="outline">Batal</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
</div> </div>
</div> </div>
{/* === Filter Akun === */}
<div className="flex justify-end"> <div className="flex justify-end">
<Popover> <Popover>
<PopoverTrigger asChild> <PopoverTrigger asChild>
@ -163,65 +295,28 @@ const AccountListTable = () => {
</a> </a>
</div> </div>
<div className="flex flex-col gap-1 overflow-auto max-h-[300px] text-xs custom-scrollbar-table"> <div className="flex flex-col gap-1 overflow-auto max-h-[300px] text-xs custom-scrollbar-table">
<div className="flex items-center space-x-2"> {["polri", "jurnalis", "umum", "ksp"].map((cat) => (
<Checkbox <div key={cat} className="flex items-center space-x-2">
id="accepted" <Checkbox
checked={filtered.includes("polri")} id={cat}
onCheckedChange={(e) => handleFilter("polri", Boolean(e))} checked={filtered.includes(cat)}
/> onCheckedChange={(e) => handleFilter(cat, Boolean(e))}
<label />
htmlFor="accepted" <label
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" htmlFor={cat}
> className="text-xs font-medium leading-none"
POLRI >
</label> {cat.toUpperCase()}
</div> </label>
<div className="flex items-center space-x-2"> </div>
<Checkbox ))}
id="accepted"
checked={filtered.includes("jurnalis")}
onCheckedChange={(e) =>
handleFilter("jurnalis", Boolean(e))
}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
JURNALIS
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
checked={filtered.includes("umum")}
onCheckedChange={(e) => handleFilter("umum", Boolean(e))}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
UMUM
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
checked={filtered.includes("ksp")}
onCheckedChange={(e) => handleFilter("ksp", Boolean(e))}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
KSP
</label>
</div>
</div> </div>
</div> </div>
</PopoverContent> </PopoverContent>
</Popover> </Popover>
</div> </div>
{/* === Table Data === */}
<Table className="overflow-hidden"> <Table className="overflow-hidden">
<TableHeader> <TableHeader>
{table.getHeaderGroups().map((headerGroup) => ( {table.getHeaderGroups().map((headerGroup) => (
@ -263,6 +358,7 @@ const AccountListTable = () => {
)} )}
</TableBody> </TableBody>
</Table> </Table>
<TablePagination <TablePagination
table={table} table={table}
totalData={totalData} totalData={totalData}

View File

@ -83,7 +83,6 @@ export default function BroadcastCampaignDetail({
"sent" | "schedule" | "account-list" "sent" | "schedule" | "account-list"
>("sent"); >("sent");
const { page, size } = searchParams; const { page, size } = searchParams;
const [calenderState, setCalenderState] = useState<boolean>(false); const [calenderState, setCalenderState] = useState<boolean>(false);
const [typeFilter, setTypeFilter] = useState<string>("email"); const [typeFilter, setTypeFilter] = useState<string>("email");
const [dateRange, setDateRange] = useState<[Date, Date]>([ const [dateRange, setDateRange] = useState<[Date, Date]>([
@ -91,10 +90,8 @@ export default function BroadcastCampaignDetail({
new Date(), new Date(),
]); ]);
const [startDate, endDate] = dateRange; const [startDate, endDate] = dateRange;
const [startDateString, setStartDateString] = useState<string | undefined>(); const [startDateString, setStartDateString] = useState<string | undefined>();
const [endDateString, setEndDateString] = useState<string | undefined>(); const [endDateString, setEndDateString] = useState<string | undefined>();
// Table state // Table state
const [sorting, setSorting] = useState<SortingState>([]); const [sorting, setSorting] = useState<SortingState>([]);
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]); const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
@ -104,7 +101,6 @@ export default function BroadcastCampaignDetail({
pageIndex: 0, pageIndex: 0,
pageSize: parseInt(size || "10"), pageSize: parseInt(size || "10"),
}); });
const pages = page ? parseInt(page) - 1 : 0; const pages = page ? parseInt(page) - 1 : 0;
const currentPage = page ? parseInt(page) : 1; const currentPage = page ? parseInt(page) : 1;
const pageSize = parseInt(size || "10"); const pageSize = parseInt(size || "10");

View File

@ -1,59 +1,73 @@
"use client"; "use client";
import { Reveal } from "@/components/landing-page/Reveal"; import { Reveal } from "@/components/landing-page/Reveal";
import { getCookiesDecrypt } from "@/lib/utils"; import { getCookiesDecrypt } from "@/lib/utils";
import { useRouter } from "next/navigation";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { yupResolver } from "@hookform/resolvers/yup";
import * as Yup from "yup";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { getInfoProfile, getProfile, getSubjects } from "@/service/auth"; import { getInfoProfile, getSubjects } from "@/service/auth";
import { close, error, loading, successCallback } from "@/config/swal"; import { close, error, loading, successCallback } from "@/config/swal";
import { sendMessage } from "@/service/landing/landing"; import { sendMessage } from "@/service/landing/landing";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useRouter } from "@/i18n/routing";
interface IFormInput {
name: string;
email: string;
phone?: string | undefined;
subjects: string;
othersubject?: string | undefined;
message: string;
}
const ContactForm = () => { const ContactForm = () => {
const router = useRouter(); const router = useRouter();
const userId = getCookiesDecrypt("uie"); const userId = getCookiesDecrypt("uie");
const [subjects, setSubjects] = useState<any>(); const [subjects, setSubjects] = useState<any[]>([]);
const [isOtherActive, setIsOtherActive] = useState(false); const [isOtherActive, setIsOtherActive] = useState(false);
const t = useTranslations("LandingPage"); const t = useTranslations("LandingPage");
const form = document.getElementById("form") as HTMLFormElement; const validationSchema = z.object({
name: z.string().min(1, "Nama tidak boleh kosong"),
// Form Handling email: z.string().email("Email tidak valid"),
const validationSchema = Yup.object().shape({ phone: z.string().optional(),
name: Yup.string().required("Nama tidak boleh kosong"), subjects: z.string().min(1, "Subjek tidak boleh kosong"),
email: Yup.string().required("Email tidak boleh kosong"), othersubject: z.string().optional(),
subjects: Yup.string().required("Subjek tidak boleh kosong"), message: z.string().min(1, "Pesan tidak boleh kosong"),
message: Yup.string().required("Pesan tidak boleh kosong"),
}); });
const formOptions = { type IFormInput = z.infer<typeof validationSchema>;
resolver: yupResolver(validationSchema),
};
const { register, handleSubmit, formState, setValue } = useForm(formOptions); const {
register,
const { errors } = formState; handleSubmit,
formState: { errors },
setValue,
reset,
} = useForm<IFormInput>({
resolver: zodResolver(validationSchema),
});
// Init state
useEffect(() => { useEffect(() => {
async function initState() { async function initState() {
const response = await getInfoProfile(); const response = await getInfoProfile();
const responseSubject = await getSubjects(); const responseSubject = await getSubjects();
const profile = response?.data?.data; const profile = response?.data?.data;
setSubjects(responseSubject?.data?.data); setSubjects(responseSubject?.data?.data || []);
console.log(response); if (profile) {
setValue("name", profile?.fullname); setValue("name", profile?.fullname || "");
setValue("email", profile?.email); // setValue('name', profile?.fullname); setValue("email", profile?.email || "");
// setValue('name', profile?.fullname); }
} }
initState(); initState();
}, []); }, [setValue]);
async function save(data: any) { async function save(data: IFormInput) {
loading(); loading();
const finalData = { const finalData = {
name: data.name, name: data.name,
email: data.email, email: data.email,
@ -65,29 +79,24 @@ const ContactForm = () => {
const response = await sendMessage(finalData); const response = await sendMessage(finalData);
if (response?.error) { if (response?.error) {
error(response?.message); error(response?.message);
return false; return;
} }
close(); close();
successCallback("Terima kasih, pesan Anda telah terkirim"); successCallback("Terima kasih, pesan Anda telah terkirim");
// $("#form")[0].onreset(); reset();
if (form) {
form.reset();
}
} }
async function onSubmit(data: any) { async function onSubmit(data: IFormInput) {
if (userId == undefined) { if (userId == undefined) {
router.push("/auth/login"); router.push("/auth");
} else { } else {
save(data); save(data);
} }
} }
const handleSubjects = (e: any) => { const handleSubjects = (e: React.ChangeEvent<HTMLSelectElement>) => {
const id = e.target.value; if (e.target.value === "Lainnya") {
if (id == "Lainnya") {
setIsOtherActive(true); setIsOtherActive(true);
} else { } else {
setIsOtherActive(false); setIsOtherActive(false);
@ -95,63 +104,163 @@ const ContactForm = () => {
}; };
return ( return (
<form method="POST" id="form" onSubmit={handleSubmit(onSubmit)} className="max-w-2xl mx-auto bg-white dark:bg-black p-6"> <form
method="POST"
onSubmit={handleSubmit(onSubmit)}
className="max-w-2xl mx-auto bg-white dark:bg-black p-6"
>
<Reveal> <Reveal>
{/* Header */} {/* Header */}
<div className="flex items-center justify-center mb-6"> <div className="flex items-center justify-center mb-6">
<img src="/assets/icons-contact.png" alt="contact" /> <img src="/assets/icons-contact.png" alt="contact" />
<h2 className="ml-4 text-2xl font-bold">{t("contactUs", { defaultValue: "Contact Us" })}</h2> <h2 className="ml-4 text-2xl font-bold">
{t("contactUs", { defaultValue: "Contact Us" })}
</h2>
</div> </div>
<h3 className="text-lg font-semibold text-gray-800 dark:text-white mb-1">{t("writeMessage", { defaultValue: "Write Message" })}</h3> <h3 className="text-lg font-semibold text-gray-800 dark:text-white mb-1">
<p className="text-sm text-gray-600 dark:text-white mb-6">{t("leaveMessage", { defaultValue: "Leave Message" })}</p> {t("writeMessage", { defaultValue: "Write Message" })}
</h3>
<p className="text-sm text-gray-600 dark:text-white mb-6">
{t("leaveMessage", { defaultValue: "Leave Message" })}
</p>
{/* Form */} {/* Form */}
<form> <div>
{/* Name */}
<div className="mb-4"> <div className="mb-4">
<label className="block text-sm font-medium text-gray-700 dark:text-white mb-1">{t("name", { defaultValue: "Name" })}</label> <label className="block text-sm font-medium text-gray-700 dark:text-white mb-1">
<input type="text" placeholder={t("enterName", { defaultValue: "Enter Name" })} className={`w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${errors.name ? "block" : ""}`} {...register("name")} required /> {t("name", { defaultValue: "Name" })}
</label>
<input
type="text"
placeholder={t("enterName", { defaultValue: "Enter Name" })}
className={`w-full p-2 border rounded-md focus:outline-none focus:ring-2 ${
errors.name
? "border-red-500 focus:ring-red-500"
: "border-gray-300 focus:ring-blue-500"
}`}
{...register("name")}
/>
{errors.name && (
<p className="text-red-500 text-sm">{errors.name.message}</p>
)}
</div> </div>
{/* Email */}
<div className="mb-4"> <div className="mb-4">
<label className="block text-sm font-medium text-gray-700 dark:text-white mb-1">Email</label> <label className="block text-sm font-medium text-gray-700 dark:text-white mb-1">
<input type="email" placeholder="name@mail.com" className={`w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${errors.email ? "block" : ""}`} {...register("email")} required /> Email
</label>
<input
type="email"
placeholder="name@mail.com"
className={`w-full p-2 border rounded-md focus:outline-none focus:ring-2 ${
errors.email
? "border-red-500 focus:ring-red-500"
: "border-gray-300 focus:ring-blue-500"
}`}
{...register("email")}
/>
{errors.email && (
<p className="text-red-500 text-sm">{errors.email.message}</p>
)}
</div> </div>
{/* Phone */}
<div className="mb-4"> <div className="mb-4">
<label className="block text-sm font-medium text-gray-700 dark:text-white mb-1">{t("number", { defaultValue: "Number" })} (Optional)</label> <label className="block text-sm font-medium text-gray-700 dark:text-white mb-1">
<input type="text" placeholder={t("enterNumber", { defaultValue: "Enter Number" })} className="w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" /> {t("number", { defaultValue: "Number" })} (Optional)
</label>
<input
type="text"
placeholder={t("enterNumber", { defaultValue: "Enter Number" })}
className="w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
{...register("phone")}
/>
</div> </div>
{/* Subjects */}
<div className="mb-4"> <div className="mb-4">
<label className="block text-sm font-medium text-gray-700 dark:text-white mb-1">{t("subject", { defaultValue: "Subject" })}</label> <label className="block text-sm font-medium text-gray-700 dark:text-white mb-1">
{t("subject", { defaultValue: "Subject" })}
</label>
<select <select
className={`w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${errors.subjects ? "block" : ""}`} className={`w-full p-2 border rounded-md focus:outline-none focus:ring-2 ${
errors.subjects
? "border-red-500 focus:ring-red-500"
: "border-gray-300 focus:ring-blue-500"
}`}
{...register("subjects", { onChange: (e) => handleSubjects(e) })} {...register("subjects", { onChange: (e) => handleSubjects(e) })}
defaultValue="" defaultValue=""
> >
<option value="" disabled> <option value="" disabled>
{t("selectSubject", { defaultValue: "Select Subject" })} {t("selectSubject", { defaultValue: "Select Subject" })}
</option> </option>
{/* <option value="1">{t("question", { defaultValue: "Question" })}</option>
<option value="2">{t("criticism", { defaultValue: "Criticism" })}</option>
<option value="3">{t("suggestion", { defaultValue: "Suggestion" })}</option> */}
{subjects?.map((list: any) => ( {subjects?.map((list: any) => (
<option key={list.id} value={list.title}> <option key={list.id} value={list.title}>
{list.title} {list.title}
</option> </option>
))} ))}
<option value="Lainnya">Lainnya</option>
</select> </select>
{errors.subjects && (
<p className="text-red-500 text-sm">{errors.subjects.message}</p>
)}
</div> </div>
{/* Other Subject */}
{isOtherActive && (
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 dark:text-white mb-1">
Subjek Lainnya
</label>
<input
type="text"
placeholder="Masukkan subjek lainnya"
className={`w-full p-2 border rounded-md focus:outline-none focus:ring-2 ${
errors.othersubject
? "border-red-500 focus:ring-red-500"
: "border-gray-300 focus:ring-blue-500"
}`}
{...register("othersubject")}
/>
{errors.othersubject && (
<p className="text-red-500 text-sm">
{errors.othersubject.message}
</p>
)}
</div>
)}
{/* Message */}
<div className="mb-4"> <div className="mb-4">
<label className="block text-sm font-medium text-gray-700 dark:text-white mb-1">{t("messages", { defaultValue: "Messages" })}</label> <label className="block text-sm font-medium text-gray-700 dark:text-white mb-1">
<textarea placeholder={t("writeYourMessage", { defaultValue: "Write Your Message" })} rows={4} className="w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea> {t("messages", { defaultValue: "Messages" })}
</label>
<textarea
placeholder={t("writeYourMessage", {
defaultValue: "Write Your Message",
})}
rows={4}
className={`w-full p-2 border rounded-md focus:outline-none focus:ring-2 ${
errors.message
? "border-red-500 focus:ring-red-500"
: "border-gray-300 focus:ring-blue-500"
}`}
{...register("message")}
></textarea>
{errors.message && (
<p className="text-red-500 text-sm">{errors.message.message}</p>
)}
</div> </div>
<button type="submit" className="w-fit bg-blue-500 flex justify-self-end text-white p-2 px-8 rounded-md hover:bg-blue-600 transition"> <button
type="submit"
className="w-fit bg-blue-500 flex justify-self-end text-white p-2 px-8 rounded-md hover:bg-blue-600 transition"
>
{t("send", { defaultValue: "Send" })} {t("send", { defaultValue: "Send" })}
</button> </button>
</form> </div>
</Reveal> </Reveal>
</form> </form>
); );

View File

@ -290,7 +290,7 @@ export default function ContentBlast(props: { type: string }) {
<Dialog open={openModal}> <Dialog open={openModal}>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Email Terkirim</DialogTitle> <DialogTitle>Terkirim !!</DialogTitle>
</DialogHeader> </DialogHeader>
<div className="flex flex-col justify-center items-center gap-3 mb-3 text-sm"> <div className="flex flex-col justify-center items-center gap-3 mb-3 text-sm">
<img <img

View File

@ -43,8 +43,13 @@ export async function getMediaBlastCampaignPage(page: number) {
return httpGetInterceptor(url); return httpGetInterceptor(url);
} }
export async function getMediaBlastAccountPage(page: number, category: string) { export async function getMediaBlastAccountPage(page: number, category: string, campaignId: string) {
const url = `media/blast/account/list?enablePage=1&size=10&page=${page}&category=${category}`; const url = `media/blast/account/list?enablePage=1&size=10&page=${page}&category=${category}&campaignId=${campaignId}`;
return httpGetInterceptor(url);
}
export async function getMediaBlastCampaignAccountList(page: any, category: string, id: any) {
const url = `media/blast/campaign-account/list?enablePage=1&page=${page}&category=${category}&campaignId=${id}`;
return httpGetInterceptor(url); return httpGetInterceptor(url);
} }
@ -58,6 +63,16 @@ export async function saveMediaBlastCampaign(data: {
return httpPostInterceptor(url, data); return httpPostInterceptor(url, data);
} }
export async function saveMediaBlastCampaignAccount(data: any) {
const url = `media/blast/campaign-account`;
return httpPostInterceptor( url, data );
}
export async function deleteMediaBlastCampaignAccount(id: any) {
const url = `media/blast/campaign-account?id=${id}`;
return httpDeleteInterceptor(url);
}
export async function getMediaBlastCampaignById(id: string) { export async function getMediaBlastCampaignById(id: string) {
const url = `media/blast/campaign?id=${id}`; const url = `media/blast/campaign?id=${id}`;
return httpGetInterceptor(url); return httpGetInterceptor(url);