fix: adjust broadcast admin
This commit is contained in:
parent
e5541c3b35
commit
e60b452780
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue