diff --git a/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/column.tsx b/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/column.tsx index e21af944..515d48fc 100644 --- a/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/column.tsx +++ b/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/column.tsx @@ -30,34 +30,34 @@ const columns: ColumnDef[] = [ accessorKey: "accountName", header: "Nama", cell: ({ row }) => ( - {row.getValue("accountName")} + {row.original.mediaBlastAccount.accountName} ), }, { accessorKey: "accountType", header: "Tipe Akun", cell: ({ row }) => ( - {row.getValue("accountType")} + {row.original.mediaBlastAccount.accountType} ), }, { accessorKey: "accountCategory", header: "Kategory", cell: ({ row }) => ( - {row.getValue("accountCategory")} + {row.original.mediaBlastAccount.accountCategory} ), }, { accessorKey: "emailAddress", header: "Email", cell: ({ row }) => ( - {row.getValue("emailAddress")} + {row.original.mediaBlastAccount.emailAddress} ), }, { accessorKey: "whatsappNumber", header: "Whatsapp", - cell: ({ row }) => {row.getValue("whatsappNumber")}, + cell: ({ row }) => {row.original.mediaBlastAccount.whatsappNumber}, }, { id: "actions", diff --git a/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/table.tsx b/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/table.tsx index c08b0a36..14c30f78 100644 --- a/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/table.tsx +++ b/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/table.tsx @@ -2,7 +2,6 @@ import * as React from "react"; import { - ColumnDef, ColumnFiltersState, PaginationState, SortingState, @@ -15,7 +14,6 @@ import { useReactTable, } from "@tanstack/react-table"; import { Button } from "@/components/ui/button"; - import { Table, TableBody, @@ -24,25 +22,48 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { UserIcon } from "lucide-react"; - -import { useRouter, useSearchParams } from "next/navigation"; -import TablePagination from "@/components/table/table-pagination"; -import columns from "./column"; +import { + Dialog, + DialogContent, + DialogFooter, + 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 { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; -import { getMediaBlastAccountPage } from "@/service/broadcast/broadcast"; import { Checkbox } from "@/components/ui/checkbox"; -import { close, loading } from "@/config/swal"; -import { Link } from "@/i18n/routing"; -import { Icon } from "@iconify/react/dist/iconify.js"; +import { Icon } from "@iconify/react"; +import { useParams, useSearchParams } from "next/navigation"; +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 router = useRouter(); + const params = useParams(); const searchParams = useSearchParams(); + const campaignId = params?.id as string; + const [dataTable, setDataTable] = React.useState([]); const [totalData, setTotalData] = React.useState(1); const [sorting, setSorting] = React.useState([]); @@ -56,10 +77,15 @@ const AccountListTable = () => { pageIndex: 0, pageSize: 10, }); - const [page, setPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1); const [filtered, setFiltered] = React.useState([]); + + // --- state utk Dialog Pilih Akun --- + const [accountCategory, setAccountCategory] = React.useState(""); + const [selectedAccount, setSelectedAccount] = React.useState([]); + const [selectedCategory, setSelectedCategory] = React.useState(""); + const table = useReactTable({ data: dataTable, columns, @@ -83,24 +109,24 @@ const AccountListTable = () => { React.useEffect(() => { const pageFromUrl = searchParams?.get("page"); - if (pageFromUrl) { - setPage(Number(pageFromUrl)); - } + if (pageFromUrl) setPage(Number(pageFromUrl)); }, [searchParams]); React.useEffect(() => { fetchData(); - }, [page]); + }, [page, filtered]); async function fetchData() { try { loading(); - const res = await getMediaBlastAccountPage( + const res = await getMediaBlastCampaignAccountList( page - 1, - filtered ? filtered.join(",") : "" + filtered ? filtered.join(",") : "", + campaignId ); + const data = res?.data?.data; - const contentData = data?.content; + const contentData = data?.content || []; contentData.forEach((item: any, index: number) => { item.no = (page - 1) * 10 + index + 1; }); @@ -109,20 +135,43 @@ const AccountListTable = () => { setTotalData(data?.totalElements); setTotalPage(data?.totalPages); close(); - } catch (error) { - console.error("Error fetching tasks:", error); + } catch (err) { + 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) => { let temp = [...filtered]; - if (checked) { - temp = [...temp, id]; - } else { - temp = temp.filter((a) => a !== id); - } + if (checked) temp = [...temp, id]; + else temp = temp.filter((a) => a !== id); setFiltered(temp); - console.log("sss", temp); }; return ( @@ -130,20 +179,103 @@ const AccountListTable = () => {

Daftar Akun

- - - - {/* - - */} + {/* === Dialog Pilih Akun === */} + + + + + + + Pilih Akun Untuk Campaign Ini + + + setAccountCategory(val)} + className="space-y-3" + > +
+ + +
+
+ + +
+
+ + +
+
+ +
+ {accountCategory === "custom" && ( + <> + + {selectedAccount.length < 1 && ( +

+ Pilih minimal 1 akun +

+ )} + + )} + + {accountCategory === "kategori" && ( + + )} + + {accountCategory === "all-account" && ( +

Semua akun dipilih

+ )} +
+ + + + + + + +
+
+ + {/* === Filter Akun === */}
@@ -163,65 +295,28 @@ const AccountListTable = () => {
-
- handleFilter("polri", Boolean(e))} - /> - -
-
- - handleFilter("jurnalis", Boolean(e)) - } - /> - -
-
- handleFilter("umum", Boolean(e))} - /> - -
-
- handleFilter("ksp", Boolean(e))} - /> - -
+ {["polri", "jurnalis", "umum", "ksp"].map((cat) => ( +
+ handleFilter(cat, Boolean(e))} + /> + +
+ ))}
+ + {/* === Table Data === */} {table.getHeaderGroups().map((headerGroup) => ( @@ -263,6 +358,7 @@ const AccountListTable = () => { )}
+ ("sent"); const { page, size } = searchParams; - const [calenderState, setCalenderState] = useState(false); const [typeFilter, setTypeFilter] = useState("email"); const [dateRange, setDateRange] = useState<[Date, Date]>([ @@ -91,10 +90,8 @@ export default function BroadcastCampaignDetail({ new Date(), ]); const [startDate, endDate] = dateRange; - const [startDateString, setStartDateString] = useState(); const [endDateString, setEndDateString] = useState(); - // Table state const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); @@ -104,7 +101,6 @@ export default function BroadcastCampaignDetail({ pageIndex: 0, pageSize: parseInt(size || "10"), }); - const pages = page ? parseInt(page) - 1 : 0; const currentPage = page ? parseInt(page) : 1; const pageSize = parseInt(size || "10"); diff --git a/app/[locale]/(public)/contact/page.tsx b/app/[locale]/(public)/contact/page.tsx index 710da1bf..601c7336 100644 --- a/app/[locale]/(public)/contact/page.tsx +++ b/app/[locale]/(public)/contact/page.tsx @@ -1,59 +1,73 @@ "use client"; import { Reveal } from "@/components/landing-page/Reveal"; import { getCookiesDecrypt } from "@/lib/utils"; -import { useRouter } from "next/navigation"; import React, { useEffect, useState } from "react"; -import { yupResolver } from "@hookform/resolvers/yup"; -import * as Yup from "yup"; 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 { sendMessage } from "@/service/landing/landing"; 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 router = useRouter(); const userId = getCookiesDecrypt("uie"); - const [subjects, setSubjects] = useState(); + const [subjects, setSubjects] = useState([]); const [isOtherActive, setIsOtherActive] = useState(false); const t = useTranslations("LandingPage"); - const form = document.getElementById("form") as HTMLFormElement; - - // Form Handling - const validationSchema = Yup.object().shape({ - name: Yup.string().required("Nama tidak boleh kosong"), - email: Yup.string().required("Email tidak boleh kosong"), - subjects: Yup.string().required("Subjek tidak boleh kosong"), - message: Yup.string().required("Pesan tidak boleh kosong"), + const validationSchema = z.object({ + name: z.string().min(1, "Nama tidak boleh kosong"), + email: z.string().email("Email tidak valid"), + phone: z.string().optional(), + subjects: z.string().min(1, "Subjek tidak boleh kosong"), + othersubject: z.string().optional(), + message: z.string().min(1, "Pesan tidak boleh kosong"), }); - const formOptions = { - resolver: yupResolver(validationSchema), - }; + type IFormInput = z.infer; - const { register, handleSubmit, formState, setValue } = useForm(formOptions); - - const { errors } = formState; + const { + register, + handleSubmit, + formState: { errors }, + setValue, + reset, + } = useForm({ + resolver: zodResolver(validationSchema), + }); + // Init state useEffect(() => { async function initState() { const response = await getInfoProfile(); const responseSubject = await getSubjects(); const profile = response?.data?.data; - setSubjects(responseSubject?.data?.data); - console.log(response); - setValue("name", profile?.fullname); - setValue("email", profile?.email); // setValue('name', profile?.fullname); - // setValue('name', profile?.fullname); + setSubjects(responseSubject?.data?.data || []); + if (profile) { + setValue("name", profile?.fullname || ""); + setValue("email", profile?.email || ""); + } } initState(); - }, []); + }, [setValue]); - async function save(data: any) { + async function save(data: IFormInput) { loading(); + const finalData = { name: data.name, email: data.email, @@ -65,29 +79,24 @@ const ContactForm = () => { const response = await sendMessage(finalData); if (response?.error) { error(response?.message); - return false; + return; } close(); successCallback("Terima kasih, pesan Anda telah terkirim"); - // $("#form")[0].onreset(); - if (form) { - form.reset(); - } + reset(); } - async function onSubmit(data: any) { + async function onSubmit(data: IFormInput) { if (userId == undefined) { - router.push("/auth/login"); + router.push("/auth"); } else { save(data); } } - const handleSubjects = (e: any) => { - const id = e.target.value; - - if (id == "Lainnya") { + const handleSubjects = (e: React.ChangeEvent) => { + if (e.target.value === "Lainnya") { setIsOtherActive(true); } else { setIsOtherActive(false); @@ -95,63 +104,163 @@ const ContactForm = () => { }; return ( -
+ {/* Header */}
contact -

{t("contactUs", { defaultValue: "Contact Us" })}

+

+ {t("contactUs", { defaultValue: "Contact Us" })} +

-

{t("writeMessage", { defaultValue: "Write Message" })}

-

{t("leaveMessage", { defaultValue: "Leave Message" })}

+

+ {t("writeMessage", { defaultValue: "Write Message" })} +

+

+ {t("leaveMessage", { defaultValue: "Leave Message" })} +

{/* Form */} - +
+ {/* Name */}
- - + + + {errors.name && ( +

{errors.name.message}

+ )}
+ {/* Email */}
- - + + + {errors.email && ( +

{errors.email.message}

+ )}
+ {/* Phone */}
- - + +
+ {/* Subjects */}
- + + {errors.subjects && ( +

{errors.subjects.message}

+ )}
+ {/* Other Subject */} + {isOtherActive && ( +
+ + + {errors.othersubject && ( +

+ {errors.othersubject.message} +

+ )} +
+ )} + + {/* Message */}
- - + + + {errors.message && ( +

{errors.message.message}

+ )}
- - +
); diff --git a/components/form/broadcast/content-blast-form.tsx b/components/form/broadcast/content-blast-form.tsx index 8fb97ded..1997b475 100644 --- a/components/form/broadcast/content-blast-form.tsx +++ b/components/form/broadcast/content-blast-form.tsx @@ -290,7 +290,7 @@ export default function ContentBlast(props: { type: string }) { - Email Terkirim + Terkirim !!