fix: fixing all eror in excel
This commit is contained in:
parent
dd94afc81d
commit
8f494739c6
|
|
@ -18,7 +18,7 @@ import { useRouter } from "next/navigation";
|
|||
import { deleteUser } from "@/service/management-user/management-user";
|
||||
import { stringify } from "querystring";
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
const getColumns = ({ onRefresh }: { onRefresh: () => void }): ColumnDef<any>[] => [
|
||||
{
|
||||
accessorKey: "no",
|
||||
header: "No",
|
||||
|
|
@ -30,11 +30,13 @@ const columns: ColumnDef<any>[] = [
|
|||
header: "Nama",
|
||||
cell: ({ row }) => <span>{row.getValue("fullname")}</span>,
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "address",
|
||||
header: "Wilayah",
|
||||
cell: ({ row }) => <span>MABES</span>,
|
||||
cell: () => <span>MABES</span>,
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "userRolePlacements",
|
||||
header: "Posisi",
|
||||
|
|
@ -52,6 +54,7 @@ const columns: ColumnDef<any>[] = [
|
|||
return <span>{posisi}</span>;
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "role.name",
|
||||
header: "Bidang Keahlian",
|
||||
|
|
@ -81,29 +84,24 @@ const columns: ColumnDef<any>[] = [
|
|||
|
||||
{
|
||||
id: "actions",
|
||||
accessorKey: "action",
|
||||
header: "Actions",
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
const { toast } = useToast();
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
||||
const doDelete = async (id: number) => {
|
||||
// Tampilkan loading
|
||||
Swal.fire({
|
||||
title: "Menghapus user...",
|
||||
text: "Mohon tunggu sebentar",
|
||||
text: "Mohon tunggu",
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => {
|
||||
Swal.showLoading();
|
||||
},
|
||||
didOpen: () => Swal.showLoading(),
|
||||
});
|
||||
|
||||
const response = await deleteUser(id);
|
||||
|
||||
if (response?.error) {
|
||||
Swal.close();
|
||||
Swal.close();
|
||||
|
||||
if (response?.error) {
|
||||
toast({
|
||||
title: stringify(response?.message),
|
||||
variant: "destructive",
|
||||
|
|
@ -111,88 +109,52 @@ const columns: ColumnDef<any>[] = [
|
|||
return;
|
||||
}
|
||||
|
||||
Swal.close();
|
||||
toast({ title: "Berhasil menghapus user" });
|
||||
|
||||
toast({
|
||||
title: "Berhasil menghapus user",
|
||||
});
|
||||
|
||||
router.push("?dataChange=true");
|
||||
// ⬅️ INI YANG PENTING → REFRESH TABLE TANPA RELOAD
|
||||
onRefresh();
|
||||
};
|
||||
|
||||
const handleDelete = (id: number) => {
|
||||
MySwal.fire({
|
||||
title: "Apakah anda ingin menghapus data user?",
|
||||
title: "Hapus user ini?",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#dc3545",
|
||||
confirmButtonText: "Iya",
|
||||
cancelButtonText: "Tidak",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
doDelete(id);
|
||||
}
|
||||
}).then((res) => {
|
||||
if (res.isConfirmed) doDelete(id);
|
||||
});
|
||||
};
|
||||
|
||||
// const doDelete = async (id: number) => {
|
||||
// const response = await deleteUser(id);
|
||||
// if (response?.error) {
|
||||
// toast({
|
||||
// title: stringify(response?.message),
|
||||
// variant: "destructive",
|
||||
// });
|
||||
// }
|
||||
// toast({
|
||||
// title: "Success delete",
|
||||
// });
|
||||
|
||||
// router.push("?dataChange=true");
|
||||
// };
|
||||
|
||||
// const handleDelete = (id: number) => {
|
||||
// MySwal.fire({
|
||||
// title: "Apakah anda ingin menghapus data user?",
|
||||
// showCancelButton: true,
|
||||
// confirmButtonColor: "#dc3545",
|
||||
// confirmButtonText: "Iya",
|
||||
// cancelButtonText: "Tidak",
|
||||
// }).then((result) => {
|
||||
// if (result.isConfirmed) {
|
||||
// doDelete(id);
|
||||
// }
|
||||
// });
|
||||
// };
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
size="icon"
|
||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||
>
|
||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||
<Button size="icon" variant="ghost">
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
<Link href={`/admin/add-experts/detail/${row?.original?.id}`}>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<Eye className="w-4 h-4 me-1.5" />
|
||||
View
|
||||
<DropdownMenuContent align="end">
|
||||
|
||||
<Link href={`/admin/add-experts/detail/${row.original.id}`}>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
||||
<Eye className="w-4 h-4 me-1.5" /> View
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
|
||||
<Link href={`/admin/add-experts/update/${row?.original?.id}`}>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<SquarePen className="w-4 h-4 me-1.5" />
|
||||
Edit
|
||||
<Link href={`/admin/add-experts/update/${row.original.id}`}>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
||||
<SquarePen className="w-4 h-4 me-1.5" /> Edit
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleDelete(row.original.userKeycloakId)}
|
||||
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||
className="text-red-600 cursor-pointer hover:bg-red-300"
|
||||
>
|
||||
<Trash2 className="w-4 h-4 me-1.5" />
|
||||
Delete
|
||||
<Trash2 className="w-4 h-4 me-1.5" /> Delete
|
||||
</DropdownMenuItem>
|
||||
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
@ -200,4 +162,4 @@ const columns: ColumnDef<any>[] = [
|
|||
},
|
||||
];
|
||||
|
||||
export default columns;
|
||||
export default getColumns;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -25,7 +23,6 @@ import {
|
|||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { UserIcon } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -35,43 +32,14 @@ import {
|
|||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||
import { paginationBlog } from "@/service/blog/blog";
|
||||
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import columns from "./column";
|
||||
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { listDataMedia } from "@/service/broadcast/broadcast";
|
||||
// import columns from "./column";
|
||||
import getColumns from "./column";
|
||||
import { listEnableCategory } from "@/service/content/content";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { close, loading } from "@/config/swal";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { listDataExperts } from "@/service/experts/experts";
|
||||
|
||||
const dummyData = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Prof. Dr. Ravi",
|
||||
region: "Nasional",
|
||||
skills: "Komunikasi",
|
||||
experience: "Akademisi",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Prof. Dr. Novan",
|
||||
region: "DKI Jakarta",
|
||||
skills: "Hukum",
|
||||
experience: "Akademisi + Praktisi",
|
||||
},
|
||||
];
|
||||
|
||||
const AddExpertTable = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
|
@ -97,7 +65,8 @@ const AddExpertTable = () => {
|
|||
const [limit, setLimit] = React.useState(10);
|
||||
const table = useReactTable({
|
||||
data: dataTable,
|
||||
columns,
|
||||
// columns,
|
||||
columns: getColumns({ onRefresh: fetchData }),
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
|
|
@ -283,7 +252,11 @@ const AddExpertTable = () => {
|
|||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
<TableCell
|
||||
// colSpan={columns.length}
|
||||
colSpan={table.getAllLeafColumns().length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
|
|
|||
|
|
@ -62,29 +62,34 @@ import { Eye, EyeOff } from "lucide-react";
|
|||
// }),
|
||||
// });
|
||||
|
||||
const FormSchema = z
|
||||
.object({
|
||||
name: z.string({ required_error: "Required" }),
|
||||
username: z.string({ required_error: "Required" }),
|
||||
password: z
|
||||
.string({ required_error: "Required" })
|
||||
.min(8, "Minimal 8 karakter")
|
||||
.regex(/[A-Z]/, "Harus mengandung huruf besar (A-Z)")
|
||||
.regex(/[0-9]/, "Harus mengandung angka (0-9)")
|
||||
.regex(/[^A-Za-z0-9]/, "Harus mengandung karakter spesial (!@#$%^&*)"),
|
||||
const FormSchema = z.object({
|
||||
name: z.string({ required_error: "Required" }),
|
||||
username: z
|
||||
.string({ required_error: "Required" })
|
||||
.refine((val) => !/\s/.test(val), {
|
||||
message: "Username tidak boleh mengandung spasi",
|
||||
}),
|
||||
// .transform((val) => val.toLowerCase()),
|
||||
|
||||
// confirmPassword: z.string({ required_error: "Required" }),
|
||||
password: z
|
||||
.string({ required_error: "Required" })
|
||||
.min(8, "Minimal 8 karakter")
|
||||
.regex(/[A-Z]/, "Harus mengandung huruf besar (A-Z)")
|
||||
.regex(/[0-9]/, "Harus mengandung angka (0-9)")
|
||||
.regex(/[^A-Za-z0-9]/, "Harus mengandung karakter spesial (!@#$%^&*)"),
|
||||
|
||||
phoneNumber: z.string({ required_error: "Required" }),
|
||||
email: z.string({ required_error: "Required" }),
|
||||
skills: z.string({ required_error: "Required" }),
|
||||
experiences: z.string({ required_error: "Required" }),
|
||||
company: z.string({ required_error: "Required" }),
|
||||
})
|
||||
// .refine((data) => data.password === data.confirmPassword, {
|
||||
// path: ["confirmPassword"],
|
||||
// message: "Konfirmasi password tidak sama",
|
||||
// });
|
||||
// confirmPassword: z.string({ required_error: "Required" }),
|
||||
|
||||
phoneNumber: z.string({ required_error: "Required" }),
|
||||
email: z.string({ required_error: "Required" }),
|
||||
skills: z.string({ required_error: "Required" }),
|
||||
experiences: z.string({ required_error: "Required" }),
|
||||
company: z.string({ required_error: "Required" }),
|
||||
});
|
||||
// .refine((data) => data.password === data.confirmPassword, {
|
||||
// path: ["confirmPassword"],
|
||||
// message: "Konfirmasi password tidak sama",
|
||||
// });
|
||||
|
||||
export type Placements = {
|
||||
index: number;
|
||||
|
|
@ -326,6 +331,39 @@ export default function AddExpertForm() {
|
|||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="username"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Username (huruf kecil, tanpa spasi)</FormLabel>
|
||||
|
||||
<Input
|
||||
type="text"
|
||||
value={field.value}
|
||||
placeholder="masukkan username"
|
||||
onChange={(e) => {
|
||||
let value = e.target.value;
|
||||
|
||||
// Hapus spasi otomatis
|
||||
value = value.replace(/\s+/g, "");
|
||||
|
||||
// Jadikan lowercase otomatis
|
||||
value = value.toLowerCase();
|
||||
|
||||
field.onChange(value);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Info tambahan */}
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
Username otomatis menjadi huruf kecil tanpa spasi.
|
||||
</p>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{/* <FormField
|
||||
control={form.control}
|
||||
name="username"
|
||||
render={({ field }) => (
|
||||
|
|
@ -341,7 +379,7 @@ export default function AddExpertForm() {
|
|||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
/> */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="phoneNumber"
|
||||
|
|
@ -411,7 +449,10 @@ export default function AddExpertForm() {
|
|||
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
|
||||
</button>
|
||||
</div>
|
||||
<FormLabel className="text-gray-400 text-[12px]">Password harus memiliki minimal 8 karakter, special karakter, angka dan huruf kapital</FormLabel>
|
||||
<FormLabel className="text-gray-400 text-[12px]">
|
||||
Password harus memiliki minimal 8 karakter, special karakter,
|
||||
angka dan huruf kapital
|
||||
</FormLabel>
|
||||
|
||||
{/* Strength meter */}
|
||||
{field.value && (
|
||||
|
|
|
|||
|
|
@ -59,8 +59,12 @@ import {
|
|||
deleteMediaBlastCampaignAccount,
|
||||
saveMediaBlastCampaignAccountBulk,
|
||||
} from "@/service/broadcast/broadcast";
|
||||
import { AdministrationUserList, getUserListAll } from "@/service/management-user/management-user";
|
||||
import {
|
||||
AdministrationUserList,
|
||||
getUserListAll,
|
||||
} from "@/service/management-user/management-user";
|
||||
import { close, loading, error, success, successCallback } from "@/config/swal";
|
||||
import { Link } from "@/i18n/routing";
|
||||
|
||||
// Mock data for available accounts - replace with actual API call
|
||||
const availableAccounts = [
|
||||
|
|
@ -98,7 +102,8 @@ const AccountListTable = () => {
|
|||
const [accountCategory, setAccountCategory] = React.useState<string>("");
|
||||
const [selectedAccount, setSelectedAccount] = React.useState<any[]>([]);
|
||||
const [selectedCategory, setSelectedCategory] = React.useState<string>("");
|
||||
const [availableAccountsList, setAvailableAccountsList] = React.useState<any[]>(availableAccounts);
|
||||
const [availableAccountsList, setAvailableAccountsList] =
|
||||
React.useState<any[]>(availableAccounts);
|
||||
const [usersList, setUsersList] = React.useState<any[]>([]);
|
||||
|
||||
const table = useReactTable({
|
||||
|
|
@ -171,7 +176,7 @@ const AccountListTable = () => {
|
|||
async function saveCampaignAccount() {
|
||||
try {
|
||||
loading();
|
||||
|
||||
|
||||
if (accountCategory === "all-account") {
|
||||
// Handle all accounts - send only campaignId and category "all"
|
||||
const request = {
|
||||
|
|
@ -202,7 +207,7 @@ const AccountListTable = () => {
|
|||
default:
|
||||
roleId = "5";
|
||||
}
|
||||
|
||||
|
||||
const request = {
|
||||
mediaBlastCampaignId: campaignId,
|
||||
mediaBlastAccountCategory: `role-${roleId}`,
|
||||
|
|
@ -216,7 +221,7 @@ const AccountListTable = () => {
|
|||
// Handle custom selection - send campaignId and selected user IDs
|
||||
const request = {
|
||||
mediaBlastCampaignId: campaignId,
|
||||
mediaBlastAccountIds: selectedAccount.map(acc => acc.id),
|
||||
mediaBlastAccountIds: selectedAccount.map((acc) => acc.id),
|
||||
};
|
||||
const response = await saveMediaBlastCampaignAccountBulk(request);
|
||||
if (response?.error) {
|
||||
|
|
@ -224,7 +229,7 @@ const AccountListTable = () => {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
close();
|
||||
successCallback("Akun berhasil ditambahkan ke campaign!");
|
||||
resetDialogState();
|
||||
|
|
@ -247,7 +252,7 @@ const AccountListTable = () => {
|
|||
try {
|
||||
loading();
|
||||
const response = await getUserListAll();
|
||||
|
||||
|
||||
if (response?.data?.data?.content) {
|
||||
setUsersList(response.data.data.content);
|
||||
}
|
||||
|
|
@ -265,15 +270,15 @@ const AccountListTable = () => {
|
|||
setFiltered(temp);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const removeSelectedAccount = (accountId: string) => {
|
||||
setSelectedAccount(selectedAccount.filter(acc => acc.id !== accountId));
|
||||
setSelectedAccount(selectedAccount.filter((acc) => acc.id !== accountId));
|
||||
};
|
||||
|
||||
const getFilteredAccounts = () => {
|
||||
if (accountCategory === "kategori" && selectedCategory) {
|
||||
return availableAccountsList.filter(acc => acc.category === selectedCategory);
|
||||
return availableAccountsList.filter(
|
||||
(acc) => acc.category === selectedCategory
|
||||
);
|
||||
}
|
||||
return availableAccountsList;
|
||||
};
|
||||
|
|
@ -291,7 +296,10 @@ const AccountListTable = () => {
|
|||
Pilih Akun
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent size="md" className="max-w-xl max-h-[80vh] overflow-y-auto">
|
||||
<DialogContent
|
||||
size="md"
|
||||
className="max-w-xl max-h-[80vh] overflow-y-auto"
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Pilih Akun Untuk Campaign Ini</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
|
@ -350,15 +358,17 @@ const AccountListTable = () => {
|
|||
options={usersList.map((user: any) => ({
|
||||
value: user.id,
|
||||
label: `${user.fullname} (${user.role?.name})`,
|
||||
user: user
|
||||
user: user,
|
||||
}))}
|
||||
value={selectedAccount.map((acc: any) => ({
|
||||
value: acc.id,
|
||||
label: `${acc.fullname} (${acc.role?.name})`,
|
||||
user: acc
|
||||
user: acc,
|
||||
}))}
|
||||
onChange={(selectedOptions: any) => {
|
||||
const selectedUsers = selectedOptions ? selectedOptions.map((option: any) => option.user) : [];
|
||||
const selectedUsers = selectedOptions
|
||||
? selectedOptions.map((option: any) => option.user)
|
||||
: [];
|
||||
setSelectedAccount(selectedUsers);
|
||||
}}
|
||||
placeholder="Cari dan pilih user..."
|
||||
|
|
@ -369,14 +379,17 @@ const AccountListTable = () => {
|
|||
className="react-select"
|
||||
classNamePrefix="select"
|
||||
/>
|
||||
|
||||
|
||||
{/* Selected Accounts Display */}
|
||||
{selectedAccount.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<Label>User Terpilih ({selectedAccount.length}):</Label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{selectedAccount.map((acc) => (
|
||||
<Badge key={acc.id} className="flex items-center gap-1">
|
||||
<Badge
|
||||
key={acc.id}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
{acc.fullname}
|
||||
<X
|
||||
className="h-3 w-3 cursor-pointer"
|
||||
|
|
@ -403,7 +416,8 @@ const AccountListTable = () => {
|
|||
{accountCategory === "kategori" && selectedCategory && (
|
||||
<div className="p-3 bg-green-50 rounded-md">
|
||||
<p className="text-sm text-green-700">
|
||||
Semua akun dengan role "{selectedCategory.toUpperCase()}" akan ditambahkan.
|
||||
Semua akun dengan role "{selectedCategory.toUpperCase()}"
|
||||
akan ditambahkan.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -412,7 +426,8 @@ const AccountListTable = () => {
|
|||
{accountCategory === "custom" && (
|
||||
<div className="p-3 bg-purple-50 rounded-md">
|
||||
<p className="text-sm text-purple-700">
|
||||
{selectedAccount.length} user terpilih akan ditambahkan ke campaign ini.
|
||||
{selectedAccount.length} user terpilih akan ditambahkan ke
|
||||
campaign ini.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -423,7 +438,8 @@ const AccountListTable = () => {
|
|||
onClick={saveCampaignAccount}
|
||||
disabled={
|
||||
!accountCategory ||
|
||||
(accountCategory === "custom" && selectedAccount.length < 1) ||
|
||||
(accountCategory === "custom" &&
|
||||
selectedAccount.length < 1) ||
|
||||
(accountCategory === "kategori" && !selectedCategory)
|
||||
}
|
||||
>
|
||||
|
|
@ -441,7 +457,47 @@ const AccountListTable = () => {
|
|||
</div>
|
||||
|
||||
{/* === Filter Akun === */}
|
||||
<div className="flex justify-end">
|
||||
<div className="flex flex-row justify-end">
|
||||
{/* <div className="flex flex-row gap-4">
|
||||
<Link href="/admin/broadcast/campaign-list/account-list/create">
|
||||
<Button variant="default" className="bg-[#3f37c9] gap-2">
|
||||
<span>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="22"
|
||||
height="22"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M17 13h-4v4h-2v-4H7v-2h4V7h2v4h4m2-8H5c-1.11 0-2 .89-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Tambahkan Akun
|
||||
</Button>
|
||||
</Link>
|
||||
<Button variant="default" className="bg-[#3f37c9] gap-2">
|
||||
<span>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill="none">
|
||||
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12 2v6.5a1.5 1.5 0 0 0 1.5 1.5H20v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-1h3.414l-1.121 1.121a1 1 0 1 0 1.414 1.415l2.829-2.829a1 1 0 0 0 0-1.414l-2.829-2.828a1 1 0 1 0-1.414 1.414L7.414 17H4V4a2 2 0 0 1 2-2zM4 17v2H3a1 1 0 1 1 0-2zM14 2.043a2 2 0 0 1 1 .543L19.414 7a2 2 0 0 1 .543 1H14z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
Import Akun
|
||||
</Button>
|
||||
</div> */}
|
||||
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button size="md" variant="outline">
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
|
|
@ -14,55 +13,80 @@ import {
|
|||
} from "@/components/ui/form";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { format } from "date-fns";
|
||||
import { CalendarIcon } from "lucide-react";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { getOnlyDate } from "@/utils/globals";
|
||||
import {
|
||||
getMediaBlastCampaignPage,
|
||||
saveMediaBlastAccount,
|
||||
saveMediaBlastCampaign,
|
||||
} from "@/service/broadcast/broadcast";
|
||||
import { error } from "@/config/swal";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
// ----------------------------
|
||||
// ZOD SCHEMA (dinamis)
|
||||
// ----------------------------
|
||||
|
||||
const FormSchema = z.object({
|
||||
name: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
name: z.string({ required_error: "Required" }),
|
||||
|
||||
accountType: z
|
||||
.array(z.string())
|
||||
.refine((value) => value.some((item) => item), {
|
||||
message: "Required",
|
||||
}),
|
||||
accountCategory: z.enum(["polri", "jurnalis", "umum", "ksp"], {
|
||||
required_error: "Required",
|
||||
}),
|
||||
email: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
whatsapp: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
.min(1, "Pilih minimal satu tipe akun"),
|
||||
|
||||
email: z.string().optional(),
|
||||
whatsapp: z.string().optional(),
|
||||
|
||||
campaignId: z.string({ required_error: "Required" }),
|
||||
}).refine(
|
||||
(data) => {
|
||||
if (data.accountType.includes("email") && !data.email) return false;
|
||||
return true;
|
||||
},
|
||||
{ message: "Email wajib diisi", path: ["email"] }
|
||||
).refine(
|
||||
(data) => {
|
||||
if (data.accountType.includes("wa") && !data.whatsapp) return false;
|
||||
return true;
|
||||
},
|
||||
{ message: "Whatsapp wajib diisi", path: ["whatsapp"] }
|
||||
);
|
||||
|
||||
|
||||
// ----------------------------
|
||||
// COMPONENT
|
||||
// ----------------------------
|
||||
|
||||
export default function CreateAccountForBroadcast() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: { accountType: [] },
|
||||
defaultValues: {
|
||||
accountType: [],
|
||||
email: "",
|
||||
whatsapp: "",
|
||||
},
|
||||
});
|
||||
|
||||
const selectedTypes = form.watch("accountType");
|
||||
const [campaigns, setCampaigns] = useState<any[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchCampaignList();
|
||||
}, []);
|
||||
|
||||
async function fetchCampaignList() {
|
||||
try {
|
||||
const res = await getMediaBlastCampaignPage(0);
|
||||
setCampaigns(res?.data?.data?.content ?? []);
|
||||
} catch (e) {
|
||||
console.log("Error fetch campaign:", e);
|
||||
}
|
||||
}
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
|
|
@ -85,10 +109,8 @@ export default function CreateAccountForBroadcast() {
|
|||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
router.push("/admin/broadcast/campaign-list/account-list");
|
||||
}
|
||||
}).then(() => {
|
||||
router.push("/admin/broadcast/campaign-list/account-list");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -96,20 +118,21 @@ export default function CreateAccountForBroadcast() {
|
|||
const reqData = {
|
||||
accountName: data.name,
|
||||
accountType: data.accountType.join(","),
|
||||
accountCategory: data.accountCategory,
|
||||
emailAddress: data.email,
|
||||
whatsappNumber: data.whatsapp,
|
||||
emailAddress: data.email ?? "",
|
||||
whatsappNumber: data.whatsapp ?? "",
|
||||
campaignId: data.campaignId,
|
||||
};
|
||||
console.log("data", data);
|
||||
|
||||
console.log("REQ:", reqData);
|
||||
|
||||
const response = await saveMediaBlastAccount(reqData);
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
successSubmit();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
|
|
@ -118,7 +141,9 @@ export default function CreateAccountForBroadcast() {
|
|||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-3 bg-white rounded-sm p-4"
|
||||
>
|
||||
<p className="fonnt-semibold">Account</p>
|
||||
<p className="font-semibold">Account</p>
|
||||
|
||||
{/* NAMA */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
|
|
@ -130,172 +155,125 @@ export default function CreateAccountForBroadcast() {
|
|||
placeholder="Masukkan nama"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* CHECKBOX TIPE AKUN */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="accountType"
|
||||
render={() => (
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Tipe Akun</FormLabel>
|
||||
<div className="flex flex-row gap-2">
|
||||
{" "}
|
||||
<FormField
|
||||
key="wa"
|
||||
control={form.control}
|
||||
name="accountType"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key="wa"
|
||||
className="flex flex-row items-start space-x-3 space-y-0"
|
||||
>
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value?.includes("wa")}
|
||||
onCheckedChange={(checked) => {
|
||||
return checked
|
||||
? field.onChange([...field.value, "wa"])
|
||||
: field.onChange(
|
||||
field.value?.filter(
|
||||
(value) => value !== "wa"
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
Whatsapp
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
key="email"
|
||||
control={form.control}
|
||||
name="accountType"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key="email"
|
||||
className="flex flex-row items-start space-x-3 space-y-0"
|
||||
>
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value?.includes("email")}
|
||||
onCheckedChange={(checked) => {
|
||||
return checked
|
||||
? field.onChange([...field.value, "email"])
|
||||
: field.onChange(
|
||||
field.value?.filter(
|
||||
(value) => value !== "email"
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">Email</FormLabel>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row gap-4">
|
||||
{/* WA */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={field.value.includes("wa")}
|
||||
onCheckedChange={(checked) =>
|
||||
checked
|
||||
? field.onChange([...field.value, "wa"])
|
||||
: field.onChange(field.value.filter((v) => v !== "wa"))
|
||||
}
|
||||
/>
|
||||
<label>Whatsapp</label>
|
||||
</div>
|
||||
|
||||
{/* EMAIL */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={field.value.includes("email")}
|
||||
onCheckedChange={(checked) =>
|
||||
checked
|
||||
? field.onChange([...field.value, "email"])
|
||||
: field.onChange(
|
||||
field.value.filter((v) => v !== "email")
|
||||
)
|
||||
}
|
||||
/>
|
||||
<label>Email</label>
|
||||
</div>
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* FORM WHATSAPP */}
|
||||
{selectedTypes.includes("wa") && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="whatsapp"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Whatsapp</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="Masukkan nomor Whatsapp"
|
||||
{...field}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* FORM EMAIL */}
|
||||
{selectedTypes.includes("email") && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="Masukkan email"
|
||||
{...field}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* CAMPAIGN */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="accountCategory"
|
||||
name="campaignId"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-3">
|
||||
<FormLabel>Kategori</FormLabel>
|
||||
<FormItem>
|
||||
<FormLabel>Campaign</FormLabel>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
className="flex flex-row gap-2"
|
||||
<select
|
||||
className="w-full border rounded-md p-2 text-sm"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="polri" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">POLRI</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="jurnalis" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">JURNALIS</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="umum" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">UMUM</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="ksp" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">KSP</FormLabel>
|
||||
</FormItem>
|
||||
</RadioGroup>
|
||||
<option value="" className="text-slate-400">
|
||||
Pilih campaign
|
||||
</option>
|
||||
|
||||
{campaigns.map((c: any) => (
|
||||
<option key={c.id} value={c.id}>
|
||||
{c.title || `Campaign ${c.id}`}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama</FormLabel>
|
||||
<Input
|
||||
type="email"
|
||||
value={field.value}
|
||||
placeholder="Masukkan email"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="whatsapp"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
value={field.value}
|
||||
placeholder="Masukkan whatsapp"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{/* BUTTON */}
|
||||
<div className="flex flex-row gap-2 mt-4 pt-4">
|
||||
<Button
|
||||
size="md"
|
||||
type="button"
|
||||
variant="outline"
|
||||
color="destructive"
|
||||
className="text-xs"
|
||||
>
|
||||
<Button type="button" variant="outline" color="destructive">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="md" type="submit" color="primary" className="text-xs">
|
||||
<Button type="submit" color="primary">
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -304,3 +282,380 @@ export default function CreateAccountForBroadcast() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// "use client";
|
||||
// import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
// import { z } from "zod";
|
||||
// import { useForm } from "react-hook-form";
|
||||
// import { zodResolver } from "@hookform/resolvers/zod";
|
||||
// import {
|
||||
// Form,
|
||||
// FormControl,
|
||||
// FormField,
|
||||
// FormItem,
|
||||
// FormLabel,
|
||||
// FormMessage,
|
||||
// } from "@/components/ui/form";
|
||||
// import withReactContent from "sweetalert2-react-content";
|
||||
// import Swal from "sweetalert2";
|
||||
// import { Input } from "@/components/ui/input";
|
||||
// import { Button } from "@/components/ui/button";
|
||||
// import {
|
||||
// getMediaBlastCampaignPage,
|
||||
// saveMediaBlastAccount,
|
||||
// } from "@/service/broadcast/broadcast";
|
||||
// import { error } from "@/config/swal";
|
||||
// import { useRouter } from "@/i18n/routing";
|
||||
// import { Checkbox } from "@/components/ui/checkbox";
|
||||
// import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
// import { useEffect, useState } from "react";
|
||||
|
||||
// // const FormSchema = z.object({
|
||||
// // name: z.string({
|
||||
// // required_error: "Required",
|
||||
// // }),
|
||||
// // accountType: z
|
||||
// // .array(z.string())
|
||||
// // .refine((value) => value.some((item) => item), {
|
||||
// // message: "Required",
|
||||
// // }),
|
||||
// // accountCategory: z.enum(["polri", "jurnalis", "umum", "ksp"], {
|
||||
// // required_error: "Required",
|
||||
// // }),
|
||||
// // email: z.string({
|
||||
// // required_error: "Required",
|
||||
// // }),
|
||||
// // whatsapp: z.string({
|
||||
// // required_error: "Required",
|
||||
// // }),
|
||||
// // campaignId: z.string({ required_error: "Required" }),
|
||||
// // });
|
||||
// const FormSchema = z
|
||||
// .object({
|
||||
// name: z.string().min(1, "Required"),
|
||||
|
||||
// accountType: z.array(z.string()).refine((value) => value.length > 0, {
|
||||
// message: "Pilih minimal satu tipe akun",
|
||||
// }),
|
||||
|
||||
// accountCategory: z.enum(["polri", "jurnalis", "umum", "ksp"], {
|
||||
// required_error: "Required",
|
||||
// }),
|
||||
|
||||
// email: z.string().optional(),
|
||||
// whatsapp: z.string().optional(),
|
||||
|
||||
// campaignId: z.string().min(1, "Required"),
|
||||
// })
|
||||
// .refine(
|
||||
// (data) => {
|
||||
// if (data.accountType.includes("email")) {
|
||||
// return !!data.email && data.email.trim() !== "";
|
||||
// }
|
||||
// return true;
|
||||
// },
|
||||
// { path: ["email"], message: "Email wajib diisi" }
|
||||
// )
|
||||
// .refine(
|
||||
// (data) => {
|
||||
// if (data.accountType.includes("wa")) {
|
||||
// return !!data.whatsapp && data.whatsapp.trim() !== "";
|
||||
// }
|
||||
// return true;
|
||||
// },
|
||||
// { path: ["whatsapp"], message: "Whatsapp wajib diisi" }
|
||||
// );
|
||||
|
||||
// export default function CreateAccountForBroadcast() {
|
||||
// const MySwal = withReactContent(Swal);
|
||||
// const router = useRouter();
|
||||
// const form = useForm<z.infer<typeof FormSchema>>({
|
||||
// resolver: zodResolver(FormSchema),
|
||||
// defaultValues: { accountType: [] },
|
||||
// });
|
||||
// const selectedTypes = form.watch("accountType");
|
||||
// const [campaigns, setCampaigns] = useState<any[]>([]);
|
||||
|
||||
// useEffect(() => {
|
||||
// fetchCampaignList();
|
||||
// }, []);
|
||||
|
||||
// async function fetchCampaignList() {
|
||||
// try {
|
||||
// const res = await getMediaBlastCampaignPage(0);
|
||||
// setCampaigns(res?.data?.data?.content ?? []);
|
||||
// } catch (e) {
|
||||
// console.log("Error fetch campaign:", e);
|
||||
// }
|
||||
// }
|
||||
|
||||
// const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
// MySwal.fire({
|
||||
// title: "Simpan Data",
|
||||
// text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
// icon: "warning",
|
||||
// showCancelButton: true,
|
||||
// cancelButtonColor: "#d33",
|
||||
// confirmButtonColor: "#3085d6",
|
||||
// confirmButtonText: "Simpan",
|
||||
// }).then((result) => {
|
||||
// if (result.isConfirmed) {
|
||||
// save(data);
|
||||
// }
|
||||
// });
|
||||
// };
|
||||
|
||||
// function successSubmit() {
|
||||
// MySwal.fire({
|
||||
// title: "Sukses",
|
||||
// icon: "success",
|
||||
// confirmButtonColor: "#3085d6",
|
||||
// confirmButtonText: "OK",
|
||||
// }).then((result) => {
|
||||
// if (result.isConfirmed) {
|
||||
// router.push("/admin/broadcast/campaign-list/account-list");
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// const save = async (data: z.infer<typeof FormSchema>) => {
|
||||
// const reqData = {
|
||||
// accountName: data.name,
|
||||
// accountType: data.accountType.join(","),
|
||||
// accountCategory: data.accountCategory,
|
||||
// emailAddress: data.email ?? "",
|
||||
// whatsappNumber: data.whatsapp ?? "",
|
||||
// campaignId: data.campaignId,
|
||||
// };
|
||||
// console.log("data", data);
|
||||
|
||||
// const response = await saveMediaBlastAccount(reqData);
|
||||
// if (response?.error) {
|
||||
// error(response.message);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// successSubmit();
|
||||
// };
|
||||
// return (
|
||||
// <div>
|
||||
// <SiteBreadcrumb />
|
||||
// <Form {...form}>
|
||||
// <form
|
||||
// onSubmit={form.handleSubmit(onSubmit)}
|
||||
// className="space-y-3 bg-white rounded-sm p-4"
|
||||
// >
|
||||
// <p className="fonnt-semibold">Account</p>
|
||||
// <FormField
|
||||
// control={form.control}
|
||||
// name="name"
|
||||
// render={({ field }) => (
|
||||
// <FormItem>
|
||||
// <FormLabel>Nama</FormLabel>
|
||||
// <Input
|
||||
// value={field.value}
|
||||
// placeholder="Masukkan nama"
|
||||
// onChange={field.onChange}
|
||||
// />
|
||||
|
||||
// <FormMessage />
|
||||
// </FormItem>
|
||||
// )}
|
||||
// />
|
||||
// <FormField
|
||||
// control={form.control}
|
||||
// name="accountType"
|
||||
// render={() => (
|
||||
// <FormItem>
|
||||
// <FormLabel>Tipe Akun</FormLabel>
|
||||
// <div className="flex flex-row gap-2">
|
||||
// {" "}
|
||||
// <FormField
|
||||
// key="wa"
|
||||
// control={form.control}
|
||||
// name="accountType"
|
||||
// render={({ field }) => {
|
||||
// return (
|
||||
// <FormItem
|
||||
// key="wa"
|
||||
// className="flex flex-row items-start space-x-3 space-y-0"
|
||||
// >
|
||||
// <FormControl>
|
||||
// <Checkbox
|
||||
// checked={field.value?.includes("wa")}
|
||||
// onCheckedChange={(checked) => {
|
||||
// return checked
|
||||
// ? field.onChange([...field.value, "wa"])
|
||||
// : field.onChange(
|
||||
// field.value?.filter(
|
||||
// (value) => value !== "wa"
|
||||
// )
|
||||
// );
|
||||
// }}
|
||||
// />
|
||||
// </FormControl>
|
||||
// <FormLabel className="font-normal">
|
||||
// Whatsapp
|
||||
// </FormLabel>
|
||||
// </FormItem>
|
||||
// );
|
||||
// }}
|
||||
// />
|
||||
// <FormField
|
||||
// key="email"
|
||||
// control={form.control}
|
||||
// name="accountType"
|
||||
// render={({ field }) => {
|
||||
// return (
|
||||
// <FormItem
|
||||
// key="email"
|
||||
// className="flex flex-row items-start space-x-3 space-y-0"
|
||||
// >
|
||||
// <FormControl>
|
||||
// <Checkbox
|
||||
// checked={field.value?.includes("email")}
|
||||
// onCheckedChange={(checked) => {
|
||||
// return checked
|
||||
// ? field.onChange([...field.value, "email"])
|
||||
// : field.onChange(
|
||||
// field.value?.filter(
|
||||
// (value) => value !== "email"
|
||||
// )
|
||||
// );
|
||||
// }}
|
||||
// />
|
||||
// </FormControl>
|
||||
// <FormLabel className="font-normal">Email</FormLabel>
|
||||
// </FormItem>
|
||||
// );
|
||||
// }}
|
||||
// />
|
||||
// </div>
|
||||
|
||||
// <FormMessage />
|
||||
// </FormItem>
|
||||
// )}
|
||||
// />
|
||||
// {/* <FormField
|
||||
// control={form.control}
|
||||
// name="accountCategory"
|
||||
// render={({ field }) => (
|
||||
// <FormItem className="space-y-3">
|
||||
// <FormLabel>Kategori</FormLabel>
|
||||
// <FormControl>
|
||||
// <RadioGroup
|
||||
// onValueChange={field.onChange}
|
||||
// defaultValue={field.value}
|
||||
// className="flex flex-row gap-2"
|
||||
// >
|
||||
// <FormItem className="flex items-center space-x-3 space-y-0">
|
||||
// <FormControl>
|
||||
// <RadioGroupItem value="polri" />
|
||||
// </FormControl>
|
||||
// <FormLabel className="font-normal">POLRI</FormLabel>
|
||||
// </FormItem>
|
||||
// <FormItem className="flex items-center space-x-3 space-y-0">
|
||||
// <FormControl>
|
||||
// <RadioGroupItem value="jurnalis" />
|
||||
// </FormControl>
|
||||
// <FormLabel className="font-normal">JURNALIS</FormLabel>
|
||||
// </FormItem>
|
||||
// <FormItem className="flex items-center space-x-3 space-y-0">
|
||||
// <FormControl>
|
||||
// <RadioGroupItem value="umum" />
|
||||
// </FormControl>
|
||||
// <FormLabel className="font-normal">UMUM</FormLabel>
|
||||
// </FormItem>
|
||||
// <FormItem className="flex items-center space-x-3 space-y-0">
|
||||
// <FormControl>
|
||||
// <RadioGroupItem value="ksp" />
|
||||
// </FormControl>
|
||||
// <FormLabel className="font-normal">KSP</FormLabel>
|
||||
// </FormItem>
|
||||
// </RadioGroup>
|
||||
// </FormControl>
|
||||
// <FormMessage />
|
||||
// </FormItem>
|
||||
// )}
|
||||
// /> */}
|
||||
// <FormField
|
||||
// control={form.control}
|
||||
// name="email"
|
||||
// render={({ field }) => (
|
||||
// <FormItem>
|
||||
// <FormLabel>Email</FormLabel>
|
||||
// <Input
|
||||
// type="email"
|
||||
// value={field.value}
|
||||
// placeholder="Masukkan email"
|
||||
// onChange={field.onChange}
|
||||
// />
|
||||
|
||||
// <FormMessage />
|
||||
// </FormItem>
|
||||
// )}
|
||||
// />
|
||||
// <FormField
|
||||
// control={form.control}
|
||||
// name="whatsapp"
|
||||
// render={({ field }) => (
|
||||
// <FormItem>
|
||||
// <FormLabel>Nama</FormLabel>
|
||||
// <Input
|
||||
// type="number"
|
||||
// value={field.value}
|
||||
// placeholder="Masukkan whatsapp"
|
||||
// onChange={field.onChange}
|
||||
// />
|
||||
|
||||
// <FormMessage />
|
||||
// </FormItem>
|
||||
// )}
|
||||
// />
|
||||
// <FormField
|
||||
// control={form.control}
|
||||
// name="campaignId"
|
||||
// render={({ field }) => (
|
||||
// <FormItem>
|
||||
// <FormLabel>Campaign</FormLabel>
|
||||
// <FormControl>
|
||||
// <select
|
||||
// className="w-full border rounded-md p-2"
|
||||
// value={field.value}
|
||||
// onChange={field.onChange}
|
||||
// >
|
||||
// <option value="" className="text-slate-400">
|
||||
// Pilih campaign
|
||||
// </option>
|
||||
|
||||
// {campaigns.map((c: any) => (
|
||||
// <option key={c.id} value={c.id}>
|
||||
// {c.title || `Campaign ${c.id}`}
|
||||
// </option>
|
||||
// ))}
|
||||
// </select>
|
||||
// </FormControl>
|
||||
// <FormMessage />
|
||||
// </FormItem>
|
||||
// )}
|
||||
// />
|
||||
// <div className="flex flex-row gap-2 mt-4 pt-4">
|
||||
// <Button
|
||||
// size="md"
|
||||
// type="button"
|
||||
// variant="outline"
|
||||
// color="destructive"
|
||||
// className="text-xs"
|
||||
// >
|
||||
// Cancel
|
||||
// </Button>
|
||||
// <Button size="md" type="submit" color="primary" className="text-xs">
|
||||
// Submit
|
||||
// </Button>
|
||||
// </div>
|
||||
// </form>
|
||||
// </Form>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -90,16 +90,16 @@ const columns: ColumnDef<any>[] = [
|
|||
Detail
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
<Link href={`/admin/broadcast/email/${row.original.id}`}>
|
||||
<Link href={`/admin/broadcast/create/${row.original.id}`}>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
||||
Email Blast
|
||||
Email & Whatsapp Blast
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
<Link href={`/admin/broadcast/whatsapp/${row.original.id}`}>
|
||||
{/* <Link href={`/admin/broadcast/whatsapp/${row.original.id}`}>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
||||
Whatsapp Blast
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
</Link> */}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import BroadcastTable from "./email/component/table";
|
||||
import BroadcastTable from "./create/component/table";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
|
||||
import EscalationTable from "../../shared/communication/escalation/components/escalation-table";
|
||||
|
|
@ -8,7 +8,7 @@ import InternalTable from "../../shared/communication/internal/components/intern
|
|||
import { useState } from "react";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import BroadcastEmailTable from "./email/component/table";
|
||||
import BroadcastEmailTable from "./create/component/table";
|
||||
import BroadcastWhatsAppTable from "./whatsapp/component/table";
|
||||
|
||||
export default function AdminBroadcast() {
|
||||
|
|
|
|||
|
|
@ -109,7 +109,9 @@ const EventModal = ({
|
|||
const pathname = usePathname();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [checkedLevels, setCheckedLevels] = useState<Set<number>>(new Set());
|
||||
const [expandedPolda, setExpandedPolda] = useState<Record<number, boolean>>({});
|
||||
const [expandedPolda, setExpandedPolda] = useState<Record<number, boolean>>(
|
||||
{}
|
||||
);
|
||||
const [audioFile, setAudioFile] = useState<File | null>(null);
|
||||
const [isRecording, setIsRecording] = useState(false);
|
||||
const [timer, setTimer] = useState<number>(120);
|
||||
|
|
@ -153,9 +155,11 @@ const EventModal = ({
|
|||
});
|
||||
|
||||
// State untuk melacak apakah perubahan berasal dari checkbox Jenis Agenda
|
||||
const [isUpdatingFromJenisAgenda, setIsUpdatingFromJenisAgenda] = useState(false);
|
||||
const [isUpdatingFromJenisAgenda, setIsUpdatingFromJenisAgenda] =
|
||||
useState(false);
|
||||
// State untuk melacak jenis perubahan spesifik
|
||||
const [jenisAgendaChangeType, setJenisAgendaChangeType] = useState<string>("");
|
||||
const [jenisAgendaChangeType, setJenisAgendaChangeType] =
|
||||
useState<string>("");
|
||||
|
||||
const levelNumber = Number(getCookiesDecrypt("ulne")) || 0;
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
|
|
@ -261,7 +265,11 @@ const EventModal = ({
|
|||
|
||||
// useEffect untuk sinkronisasi checkbox modal dengan Jenis Agenda
|
||||
useEffect(() => {
|
||||
if (listDest.length > 0 && isUpdatingFromJenisAgenda && jenisAgendaChangeType) {
|
||||
if (
|
||||
listDest.length > 0 &&
|
||||
isUpdatingFromJenisAgenda &&
|
||||
jenisAgendaChangeType
|
||||
) {
|
||||
syncModalWithJenisAgenda();
|
||||
}
|
||||
}, [isUpdatingFromJenisAgenda, jenisAgendaChangeType]);
|
||||
|
|
@ -273,44 +281,55 @@ const EventModal = ({
|
|||
}
|
||||
}, [checkedLevels, isUpdatingFromJenisAgenda]);
|
||||
|
||||
// Fungsi untuk update wilayahPublish berdasarkan checkbox modal
|
||||
// Fungsi untuk update wilayahPublish berdasarkan checkbox modal
|
||||
const updateWilayahPublishFromModal = () => {
|
||||
// Hanya update jika tidak sedang dalam proses update dari Jenis Agenda
|
||||
if (!isUpdatingFromJenisAgenda && listDest.length > 0) {
|
||||
// Hitung item yang dipilih berdasarkan checkedLevels
|
||||
const checkedPoldaCount = listDest.filter((item: any) =>
|
||||
item.levelNumber === 2 &&
|
||||
item.name !== "SATKER POLRI" &&
|
||||
checkedLevels.has(Number(item.id))
|
||||
const checkedPoldaCount = listDest.filter(
|
||||
(item: any) =>
|
||||
item.levelNumber === 2 &&
|
||||
item.name !== "SATKER POLRI" &&
|
||||
checkedLevels.has(Number(item.id))
|
||||
).length;
|
||||
|
||||
|
||||
const checkedPolresCount = listDest.reduce((total: number, item: any) => {
|
||||
if (item.subDestination) {
|
||||
return total + item.subDestination.filter((sub: any) => checkedLevels.has(Number(sub.id))).length;
|
||||
return (
|
||||
total +
|
||||
item.subDestination.filter((sub: any) =>
|
||||
checkedLevels.has(Number(sub.id))
|
||||
).length
|
||||
);
|
||||
}
|
||||
return total;
|
||||
}, 0);
|
||||
|
||||
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI");
|
||||
const checkedSatkerCount = satkerItem ? (
|
||||
(checkedLevels.has(Number(satkerItem.id)) ? 1 : 0) +
|
||||
(satkerItem.subDestination?.filter((sub: any) => checkedLevels.has(Number(sub.id))).length || 0)
|
||||
) : 0;
|
||||
|
||||
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
);
|
||||
const checkedSatkerCount = satkerItem
|
||||
? (checkedLevels.has(Number(satkerItem.id)) ? 1 : 0) +
|
||||
(satkerItem.subDestination?.filter((sub: any) =>
|
||||
checkedLevels.has(Number(sub.id))
|
||||
).length || 0)
|
||||
: 0;
|
||||
|
||||
// Checkbox aktif jika ADA item yang dipilih dalam kategori tersebut
|
||||
const hasSelectedPolda = checkedPoldaCount > 0;
|
||||
const hasSelectedPolres = checkedPolresCount > 0;
|
||||
const hasSelectedSatker = checkedSatkerCount > 0;
|
||||
|
||||
|
||||
// Update arrays untuk backend
|
||||
const newSelectedPolda = listDest
|
||||
.filter((item: any) =>
|
||||
item.levelNumber === 2 &&
|
||||
item.name !== "SATKER POLRI" &&
|
||||
checkedLevels.has(Number(item.id))
|
||||
.filter(
|
||||
(item: any) =>
|
||||
item.levelNumber === 2 &&
|
||||
item.name !== "SATKER POLRI" &&
|
||||
checkedLevels.has(Number(item.id))
|
||||
)
|
||||
.map((item: any) => String(item.id));
|
||||
|
||||
|
||||
const newSelectedPolres: string[] = [];
|
||||
listDest.forEach((item: any) => {
|
||||
if (item.subDestination) {
|
||||
|
|
@ -321,7 +340,7 @@ const EventModal = ({
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const newSelectedSatker: string[] = [];
|
||||
if (satkerItem) {
|
||||
if (checkedLevels.has(Number(satkerItem.id))) {
|
||||
|
|
@ -335,51 +354,56 @@ const EventModal = ({
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update state arrays
|
||||
setSelectedPolda(newSelectedPolda);
|
||||
setSelectedPolres(newSelectedPolres);
|
||||
setSelectedSatker(newSelectedSatker);
|
||||
|
||||
|
||||
// Update wilayahPublish berdasarkan yang dipilih di modal
|
||||
setWilayahPublish(prev => {
|
||||
setWilayahPublish((prev) => {
|
||||
const newState = { ...prev };
|
||||
|
||||
|
||||
// Update individual checkboxes
|
||||
newState.polda = hasSelectedPolda;
|
||||
newState.polres = hasSelectedPolres;
|
||||
newState.satker = hasSelectedSatker;
|
||||
|
||||
|
||||
// Update checkbox "semua" berdasarkan level user
|
||||
if (levelNumber === 1) {
|
||||
// Level 1: semua checkbox harus aktif (nasional, polda, polres, satker, international)
|
||||
newState.semua = newState.nasional && hasSelectedPolda && hasSelectedPolres && hasSelectedSatker && newState.international;
|
||||
newState.semua =
|
||||
newState.nasional &&
|
||||
hasSelectedPolda &&
|
||||
hasSelectedPolres &&
|
||||
hasSelectedSatker &&
|
||||
newState.international;
|
||||
} else if (levelNumber === 2) {
|
||||
// Level 2: hanya polres yang perlu aktif
|
||||
newState.semua = hasSelectedPolres;
|
||||
} else {
|
||||
newState.semua = false;
|
||||
}
|
||||
|
||||
|
||||
return newState;
|
||||
});
|
||||
|
||||
|
||||
// Update agendaType berdasarkan checkbox yang aktif
|
||||
const selectedKeys = [];
|
||||
if (hasSelectedPolda) selectedKeys.push(wilayahValueMap.polda);
|
||||
if (hasSelectedPolres) selectedKeys.push(wilayahValueMap.polres);
|
||||
if (hasSelectedSatker) selectedKeys.push(wilayahValueMap.satker);
|
||||
|
||||
|
||||
setAgendaType(selectedKeys.join(","));
|
||||
}
|
||||
};
|
||||
|
||||
// Fungsi untuk sinkronisasi checkbox modal dengan Jenis Agenda
|
||||
// Fungsi untuk sinkronisasi checkbox modal dengan Jenis Agenda
|
||||
const syncModalWithJenisAgenda = () => {
|
||||
// Hanya jalankan sinkronisasi jika perubahan berasal dari checkbox Jenis Agenda
|
||||
if (isUpdatingFromJenisAgenda) {
|
||||
const newCheckedLevels = new Set(checkedLevels);
|
||||
|
||||
|
||||
// Handle checklist actions - menambahkan semua item ke modal
|
||||
if (jenisAgendaChangeType === "polda_checked") {
|
||||
// Checklist semua polda
|
||||
|
|
@ -391,7 +415,11 @@ const EventModal = ({
|
|||
} else if (jenisAgendaChangeType === "polres_checked") {
|
||||
// Checklist semua polres, tapi hanya yang poldanya sudah di-checklist
|
||||
listDest.forEach((item: any) => {
|
||||
if (item.levelNumber === 2 && item.name !== "SATKER POLRI" && newCheckedLevels.has(Number(item.id))) {
|
||||
if (
|
||||
item.levelNumber === 2 &&
|
||||
item.name !== "SATKER POLRI" &&
|
||||
newCheckedLevels.has(Number(item.id))
|
||||
) {
|
||||
if (item.subDestination) {
|
||||
item.subDestination.forEach((polres: any) => {
|
||||
newCheckedLevels.add(Number(polres.id));
|
||||
|
|
@ -401,7 +429,9 @@ const EventModal = ({
|
|||
});
|
||||
} else if (jenisAgendaChangeType === "satker_checked") {
|
||||
// Checklist satker
|
||||
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI");
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
);
|
||||
if (satkerItem) {
|
||||
newCheckedLevels.add(Number(satkerItem.id));
|
||||
if (satkerItem.subDestination) {
|
||||
|
|
@ -434,10 +464,12 @@ const EventModal = ({
|
|||
}
|
||||
}
|
||||
});
|
||||
setWilayahPublish(prev => ({ ...prev, polres: false }));
|
||||
setWilayahPublish((prev) => ({ ...prev, polres: false }));
|
||||
} else if (jenisAgendaChangeType === "satker_unchecked") {
|
||||
// Clear satker dari checkedLevels
|
||||
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI");
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
);
|
||||
if (satkerItem) {
|
||||
newCheckedLevels.delete(Number(satkerItem.id));
|
||||
if (satkerItem.subDestination) {
|
||||
|
|
@ -447,9 +479,9 @@ const EventModal = ({
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
setCheckedLevels(newCheckedLevels);
|
||||
|
||||
|
||||
// Reset flag setelah sinkronisasi selesai
|
||||
setIsUpdatingFromJenisAgenda(false);
|
||||
setJenisAgendaChangeType("");
|
||||
|
|
@ -484,12 +516,14 @@ const EventModal = ({
|
|||
setCheckedLevels((prev) => {
|
||||
const updatedLevels = new Set(prev);
|
||||
const isCurrentlyChecked = updatedLevels.has(levelId);
|
||||
|
||||
|
||||
if (isCurrentlyChecked) {
|
||||
updatedLevels.delete(levelId);
|
||||
|
||||
|
||||
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
|
||||
const poldaItem = listDest.find((item: any) => Number(item.id) === levelId) as any;
|
||||
const poldaItem = listDest.find(
|
||||
(item: any) => Number(item.id) === levelId
|
||||
) as any;
|
||||
if (poldaItem && poldaItem.subDestination) {
|
||||
poldaItem.subDestination.forEach((polres: any) => {
|
||||
updatedLevels.delete(Number(polres.id));
|
||||
|
|
@ -514,7 +548,12 @@ const EventModal = ({
|
|||
const toggleWilayah = (key: string) => {
|
||||
// Set flag bahwa perubahan berasal dari checkbox Jenis Agenda
|
||||
setIsUpdatingFromJenisAgenda(true);
|
||||
setJenisAgendaChangeType(key + (wilayahPublish[key as keyof typeof wilayahPublish] ? "_unchecked" : "_checked"));
|
||||
setJenisAgendaChangeType(
|
||||
key +
|
||||
(wilayahPublish[key as keyof typeof wilayahPublish]
|
||||
? "_unchecked"
|
||||
: "_checked")
|
||||
);
|
||||
|
||||
setWilayahPublish((prev: any) => {
|
||||
let newState = { ...prev };
|
||||
|
|
@ -551,18 +590,21 @@ const EventModal = ({
|
|||
return newState;
|
||||
}
|
||||
|
||||
// Validasi khusus untuk POLRES
|
||||
// Validasi khusus untuk POLRES
|
||||
if (key === "polres" && !prev[key]) {
|
||||
// Cek apakah ada POLDA yang sudah dipilih di modal
|
||||
const hasSelectedPolda = listDest.some((item: any) =>
|
||||
item.levelNumber === 2 &&
|
||||
item.name !== "SATKER POLRI" &&
|
||||
checkedLevels.has(Number(item.id))
|
||||
const hasSelectedPolda = listDest.some(
|
||||
(item: any) =>
|
||||
item.levelNumber === 2 &&
|
||||
item.name !== "SATKER POLRI" &&
|
||||
checkedLevels.has(Number(item.id))
|
||||
);
|
||||
|
||||
|
||||
if (!hasSelectedPolda) {
|
||||
// Jika tidak ada POLDA yang dipilih, tampilkan peringatan dan batalkan
|
||||
alert("Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.");
|
||||
alert(
|
||||
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."
|
||||
);
|
||||
// Reset flag karena perubahan dibatalkan
|
||||
setIsUpdatingFromJenisAgenda(false);
|
||||
setJenisAgendaChangeType("");
|
||||
|
|
@ -598,7 +640,9 @@ const EventModal = ({
|
|||
});
|
||||
} else if (key === "satker") {
|
||||
// Clear satker
|
||||
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI");
|
||||
const satkerItem: any = listDest.find(
|
||||
(item: any) => item.name === "SATKER POLRI"
|
||||
);
|
||||
if (satkerItem) {
|
||||
newCheckedLevels.delete(Number(satkerItem.id));
|
||||
if (satkerItem.subDestination) {
|
||||
|
|
@ -613,9 +657,14 @@ const EventModal = ({
|
|||
|
||||
// Update checkbox "semua" berdasarkan status semua checkbox lainnya
|
||||
// Untuk level 1: semua, nasional, polda, polres, satker, international harus aktif
|
||||
// Untuk level 2: semua, polres harus aktif
|
||||
// Untuk level 2: semua, polres harus aktif
|
||||
if (levelNumber === 1) {
|
||||
newState.semua = newState.nasional && newState.polda && newState.polres && newState.satker && newState.international;
|
||||
newState.semua =
|
||||
newState.nasional &&
|
||||
newState.polda &&
|
||||
newState.polres &&
|
||||
newState.satker &&
|
||||
newState.international;
|
||||
} else if (levelNumber === 2) {
|
||||
newState.semua = newState.polres;
|
||||
} else {
|
||||
|
|
@ -770,7 +819,7 @@ const EventModal = ({
|
|||
|
||||
const onDeleteEventAction = async () => {
|
||||
try {
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const handleOpenDeleteModal = (eventId: string) => {
|
||||
|
|
@ -950,7 +999,7 @@ const EventModal = ({
|
|||
);
|
||||
};
|
||||
|
||||
const handleRemoveFile = (id: number) => { };
|
||||
const handleRemoveFile = (id: number) => {};
|
||||
|
||||
async function doDelete(id: any) {
|
||||
loading();
|
||||
|
|
@ -1106,40 +1155,42 @@ const EventModal = ({
|
|||
<div className="space-y-1.5">
|
||||
<Label htmlFor="wilayahPublish">Jenis Agenda</Label>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
|
||||
<div>
|
||||
<Checkbox
|
||||
id="semua"
|
||||
checked={wilayahPublish.semua}
|
||||
onCheckedChange={() => toggleWilayah("semua")}
|
||||
/>
|
||||
<label htmlFor="semua" className="ml-2 text-sm">
|
||||
Semua
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{levelNumber === 1 && (
|
||||
<>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="nasional"
|
||||
checked={wilayahPublish.nasional}
|
||||
onCheckedChange={() => toggleWilayah("nasional")}
|
||||
/>
|
||||
<label htmlFor="nasional" className="ml-2 text-sm mr-2">
|
||||
Nasional
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="polda"
|
||||
checked={wilayahPublish.polda}
|
||||
onCheckedChange={() => toggleWilayah("polda")}
|
||||
id="semua"
|
||||
checked={wilayahPublish.semua}
|
||||
onCheckedChange={() => toggleWilayah("semua")}
|
||||
/>
|
||||
<label htmlFor="polda" className="mx-2 text-sm mr-2">
|
||||
Polda
|
||||
<label htmlFor="semua" className="ml-2 text-sm">
|
||||
Semua
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{levelNumber === 1 && (
|
||||
<>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="nasional"
|
||||
checked={wilayahPublish.nasional}
|
||||
onCheckedChange={() => toggleWilayah("nasional")}
|
||||
/>
|
||||
<label
|
||||
htmlFor="nasional"
|
||||
className="ml-2 text-sm mr-2"
|
||||
>
|
||||
Nasional
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="polda"
|
||||
checked={wilayahPublish.polda}
|
||||
onCheckedChange={() => toggleWilayah("polda")}
|
||||
/>
|
||||
<label htmlFor="polda" className="mx-2 text-sm mr-2">
|
||||
Polda
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
|
@ -1151,39 +1202,41 @@ const EventModal = ({
|
|||
onCheckedChange={() => toggleWilayah("polres")}
|
||||
/>
|
||||
<label htmlFor="polres" className="ml-2 text-sm mr-2">
|
||||
Polres
|
||||
Polres
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{levelNumber === 1 && (
|
||||
<>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="satker"
|
||||
checked={wilayahPublish.satker}
|
||||
onCheckedChange={() => toggleWilayah("satker")}
|
||||
/>
|
||||
<label htmlFor="satker" className="mx-2 text-sm mr-2">
|
||||
Satker
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="international"
|
||||
checked={wilayahPublish.international}
|
||||
onCheckedChange={() => toggleWilayah("international")}
|
||||
/>
|
||||
<label
|
||||
htmlFor="international"
|
||||
className="ml-2 text-sm mr-2"
|
||||
>
|
||||
Internasional
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="satker"
|
||||
checked={wilayahPublish.satker}
|
||||
onCheckedChange={() => toggleWilayah("satker")}
|
||||
/>
|
||||
<label htmlFor="satker" className="mx-2 text-sm mr-2">
|
||||
Satker
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="international"
|
||||
checked={wilayahPublish.international}
|
||||
onCheckedChange={() =>
|
||||
toggleWilayah("international")
|
||||
}
|
||||
/>
|
||||
<label
|
||||
htmlFor="international"
|
||||
className="ml-2 text-sm mr-2"
|
||||
>
|
||||
Internasional
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
<div className="pl-1">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
|
|
@ -1202,7 +1255,9 @@ const EventModal = ({
|
|||
<div key={polda.id} className="border p-2">
|
||||
<Label className="flex items-center">
|
||||
<Checkbox
|
||||
checked={checkedLevels.has(Number(polda.id))}
|
||||
checked={checkedLevels.has(
|
||||
Number(polda.id)
|
||||
)}
|
||||
onCheckedChange={() =>
|
||||
handleCheckboxChange(Number(polda.id))
|
||||
}
|
||||
|
|
@ -1240,9 +1295,13 @@ const EventModal = ({
|
|||
polda?.subDestination?.forEach(
|
||||
(polres: any) => {
|
||||
if (isChecked) {
|
||||
updatedLevels.add(Number(polres.id));
|
||||
updatedLevels.add(
|
||||
Number(polres.id)
|
||||
);
|
||||
} else {
|
||||
updatedLevels.delete(Number(polres.id));
|
||||
updatedLevels.delete(
|
||||
Number(polres.id)
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
@ -1252,18 +1311,27 @@ const EventModal = ({
|
|||
/>
|
||||
Pilih Semua
|
||||
</Label>
|
||||
{polda?.subDestination?.map((polres: any) => (
|
||||
<Label key={polres.id} className="block mt-1">
|
||||
<Checkbox
|
||||
checked={checkedLevels.has(Number(polres.id))}
|
||||
onCheckedChange={() =>
|
||||
handleCheckboxChange(Number(polres.id))
|
||||
}
|
||||
className="mr-2"
|
||||
/>
|
||||
{polres.name}
|
||||
</Label>
|
||||
))}
|
||||
{polda?.subDestination?.map(
|
||||
(polres: any) => (
|
||||
<Label
|
||||
key={polres.id}
|
||||
className="block mt-1"
|
||||
>
|
||||
<Checkbox
|
||||
checked={checkedLevels.has(
|
||||
Number(polres.id)
|
||||
)}
|
||||
onCheckedChange={() =>
|
||||
handleCheckboxChange(
|
||||
Number(polres.id)
|
||||
)
|
||||
}
|
||||
className="mr-2"
|
||||
/>
|
||||
{polres.name}
|
||||
</Label>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -1304,8 +1372,7 @@ const EventModal = ({
|
|||
<Label>Video</Label>
|
||||
<FileUploader
|
||||
accept={{
|
||||
"mp4/*": [],
|
||||
"mov/*": [],
|
||||
"video/*": [],
|
||||
}}
|
||||
maxSize={100}
|
||||
label="Upload file dengan format .mp4 atau .mov."
|
||||
|
|
@ -1317,7 +1384,7 @@ const EventModal = ({
|
|||
className="object-fill h-full w-full rounded-md"
|
||||
src={file.url}
|
||||
controls
|
||||
title={`Video ${file.id}`} // Mengganti alt dengan title
|
||||
title={`Video ${file.id}`}
|
||||
/>
|
||||
<div
|
||||
key={index}
|
||||
|
|
@ -1396,10 +1463,9 @@ const EventModal = ({
|
|||
</div>
|
||||
<div>
|
||||
<Label>Teks</Label>
|
||||
|
||||
<FileUploader
|
||||
accept={{
|
||||
"pdf/*": [],
|
||||
"application/pdf": [],
|
||||
}}
|
||||
maxSize={100}
|
||||
label="Upload file dengan format .pdf."
|
||||
|
|
@ -1455,8 +1521,7 @@ const EventModal = ({
|
|||
/>
|
||||
<FileUploader
|
||||
accept={{
|
||||
"mp3/*": [],
|
||||
"wav/*": [],
|
||||
"audio/*": [],
|
||||
}}
|
||||
maxSize={100}
|
||||
label="Upload file dengan format .mp3 atau .wav."
|
||||
|
|
@ -1482,7 +1547,8 @@ const EventModal = ({
|
|||
type="button"
|
||||
onClick={onPlayPause}
|
||||
disabled={isPlaying}
|
||||
className={`flex items-center gap-2 ${isPlaying
|
||||
className={`flex items-center gap-2 ${
|
||||
isPlaying
|
||||
? "bg-gray-300 cursor-not-allowed"
|
||||
: "bg-primary text-white"
|
||||
} p-2 rounded`}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import Swal from "sweetalert2";
|
|||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
|
||||
import {
|
||||
getLocaleTime,
|
||||
|
|
@ -24,7 +25,6 @@ import {
|
|||
detailMediaSummary,
|
||||
getMediaBlastCampaignList,
|
||||
saveMediaBlastBroadcast,
|
||||
saveMediaBlastCampaign,
|
||||
} from "@/service/broadcast/broadcast";
|
||||
import { error } from "@/config/swal";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
|
|
@ -44,27 +44,16 @@ import {
|
|||
import dynamic from "next/dynamic";
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
() => import("@/components/editor/custom-editor"),
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
const animatedComponent = makeAnimated();
|
||||
|
||||
// -------------------------
|
||||
// ZOD SCHEMA (baru)
|
||||
// -------------------------
|
||||
const FormSchema = z.object({
|
||||
title: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
url: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
thumbnail: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
detail: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
selected: z
|
||||
.array(
|
||||
z.object({
|
||||
|
|
@ -73,9 +62,16 @@ const FormSchema = z.object({
|
|||
value: z.string(),
|
||||
})
|
||||
)
|
||||
.refine((value) => value.length > 0, {
|
||||
message: "Required",
|
||||
}),
|
||||
.min(1, "Pilih minimal satu campaign"),
|
||||
|
||||
title: z.string().min(1, "Required"),
|
||||
url: z.string().min(1, "Required"),
|
||||
thumbnail: z.string().min(1, "Required"),
|
||||
detail: z.string().min(1, "Required"),
|
||||
|
||||
messageType: z
|
||||
.array(z.string())
|
||||
.min(1, "Pilih minimal satu tipe pesan (WA / Email)"),
|
||||
});
|
||||
|
||||
interface Campaign {
|
||||
|
|
@ -88,66 +84,77 @@ export default function ContentBlast(props: { type: string }) {
|
|||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
const { type } = props;
|
||||
|
||||
const [dataSelectCampaign, setDataSelectCampaign] = useState<Campaign[]>([]);
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: { selected: [], detail: "" },
|
||||
defaultValues: {
|
||||
selected: [],
|
||||
detail: "",
|
||||
messageType: [], // checkbox
|
||||
},
|
||||
});
|
||||
|
||||
const selectedTypes = form.watch("messageType");
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
if (form.getValues("detail") == "") {
|
||||
form.setError("detail", {
|
||||
type: "manual",
|
||||
message: "Required",
|
||||
});
|
||||
} else {
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
MySwal.fire({
|
||||
title: "Kirim Broadcast?",
|
||||
text: "Pesan akan dikirim ke semua campaign yang dipilih",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Kirim",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) save(data);
|
||||
});
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
// SAVE LOGIC (baru)
|
||||
// -------------------------
|
||||
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||
const selectedCampaign = form.getValues("selected");
|
||||
const selectedCampaign = data.selected;
|
||||
|
||||
for (let i = 0; i < selectedCampaign.length; i++) {
|
||||
const reqData = {
|
||||
mediaUploadId: id,
|
||||
mediaBlastCampaignId: selectedCampaign[i].id,
|
||||
subject: type == "wa" ? `*${data.title}*` : data.title,
|
||||
body: data.detail?.replace(/\n/g, ""),
|
||||
type: type,
|
||||
isScheduled: false,
|
||||
thumbnail: data?.thumbnail,
|
||||
sendDate: getLocaleTimestamp(new Date()),
|
||||
sendTime: getLocaleTime(new Date()),
|
||||
contentUrl: data.url,
|
||||
};
|
||||
const campaignId = selectedCampaign[i].id;
|
||||
|
||||
console.log("req =>", reqData);
|
||||
const response = await saveMediaBlastBroadcast(reqData);
|
||||
// Loop WA / Email
|
||||
for (let mt of data.messageType) {
|
||||
const payload = {
|
||||
mediaUploadId: id,
|
||||
mediaBlastCampaignId: campaignId,
|
||||
subject: mt === "wa" ? `*${data.title}*` : data.title,
|
||||
body: data.detail.replace(/\n/g, ""),
|
||||
type: mt, // <-- WA / email
|
||||
isScheduled: false,
|
||||
thumbnail: data.thumbnail,
|
||||
sendDate: getLocaleTimestamp(new Date()),
|
||||
sendTime: getLocaleTime(new Date()),
|
||||
contentUrl: data.url,
|
||||
};
|
||||
|
||||
console.log("REQ =>", payload);
|
||||
await saveMediaBlastBroadcast(payload);
|
||||
}
|
||||
}
|
||||
|
||||
setOpenModal(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
async function init() {
|
||||
const response = await detailMediaSummary(String(id));
|
||||
const details = response?.data?.data;
|
||||
|
||||
if (!details) return;
|
||||
|
||||
form.setValue("thumbnail", details.smallThumbnailLink);
|
||||
form.setValue("title", details.title);
|
||||
form.setValue("url", details.pageUrl);
|
||||
let pageUrl = details?.pageUrl || "";
|
||||
if (pageUrl.includes("mediahub.polri.go.id")) {
|
||||
pageUrl = pageUrl.replace(
|
||||
|
|
@ -157,59 +164,43 @@ export default function ContentBlast(props: { type: string }) {
|
|||
}
|
||||
);
|
||||
}
|
||||
if (details != undefined) {
|
||||
form.setValue("thumbnail", details.smallThumbnailLink);
|
||||
let body = `<div><p>Berita hari ini !!!</p>
|
||||
<div style='margin-top:20px;border:1px solid;border-radius:10px;width:500px;background-color:#f7f7f7'>
|
||||
<div>
|
||||
<img style='width:500px;height:auto;border-radius:10px;object-fit:cover' src='${
|
||||
details?.smallThumbnailLink
|
||||
}'>
|
||||
</div>
|
||||
<a style='padding:5px 20px;margin:0;text-decoration:none' href='${pageUrl}'>${pageUrl}</a>
|
||||
<h3 style='padding:5px 20px;margin:0'>${details?.title}</h3>
|
||||
<p style='padding:0 20px;margin:0;margin-bottom:10px'>
|
||||
${textEllipsis(details?.description, 150)}
|
||||
</p>
|
||||
</div>
|
||||
</div>`;
|
||||
form.setValue("title", `${details?.title}`);
|
||||
form.setValue(
|
||||
"url",
|
||||
details?.pageUrl || "https://mediahub.polri.go.id"
|
||||
);
|
||||
if (type == "wa") {
|
||||
body = `${textEllipsis(details?.description, 150)}`;
|
||||
form.setValue("detail", body);
|
||||
} else {
|
||||
form.setValue("detail", body);
|
||||
}
|
||||
}
|
||||
|
||||
const body = `<div><p>Berita hari ini !!!</p>
|
||||
<div style='margin-top:20px;border:1px solid;border-radius:10px;width:500px;background-color:#f7f7f7'>
|
||||
<div>
|
||||
<img style='width:500px;height:auto;border-radius:10px;object-fit:cover' src='${
|
||||
details.smallThumbnailLink
|
||||
}'>
|
||||
</div>
|
||||
<a style='padding:5px 20px;margin:0;text-decoration:none' href='${pageUrl}'>${pageUrl}</a>
|
||||
|
||||
<h3 style='padding:5px 20px;margin:0'>${details.title}</h3>
|
||||
<p style='padding:0 20px;margin:0;margin-bottom:10px'>
|
||||
${textEllipsis(details.description, 150)}
|
||||
</p>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
form.setValue("detail", body);
|
||||
}
|
||||
|
||||
async function getCampaign() {
|
||||
async function loadCampaign() {
|
||||
const response = await getMediaBlastCampaignList();
|
||||
const campaign = response?.data?.data?.content;
|
||||
handleLabelCampaign(campaign);
|
||||
console.log(campaign);
|
||||
}
|
||||
|
||||
initState();
|
||||
getCampaign();
|
||||
init();
|
||||
loadCampaign();
|
||||
}, [id]);
|
||||
|
||||
function handleLabelCampaign(data: any) {
|
||||
const optionArr: any = [];
|
||||
|
||||
data.map((option: any) => {
|
||||
optionArr.push({
|
||||
id: option.id,
|
||||
label: option.title,
|
||||
value: option.title,
|
||||
});
|
||||
});
|
||||
console.log("option arr", optionArr);
|
||||
setDataSelectCampaign(optionArr);
|
||||
const arr = data.map((item: any) => ({
|
||||
id: item.id,
|
||||
label: item.title,
|
||||
value: item.title,
|
||||
}));
|
||||
setDataSelectCampaign(arr);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -218,104 +209,127 @@ export default function ContentBlast(props: { type: string }) {
|
|||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-3 bg-white rounded-sm p-4"
|
||||
>
|
||||
<p className="fonnt-semibold">Broadcast</p>
|
||||
<p className="font-semibold">Broadcast</p>
|
||||
|
||||
{/* SELECT CAMPAIGN */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="selected"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Subject</FormLabel>
|
||||
<FormLabel>Pilih Campaign</FormLabel>
|
||||
<Select
|
||||
className="z-50"
|
||||
options={dataSelectCampaign}
|
||||
closeMenuOnSelect={false}
|
||||
components={animatedComponent}
|
||||
onChange={field.onChange}
|
||||
isMulti
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* CHECKBOX MESSAGE TYPE */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="messageType"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Kirim sebagai:</FormLabel>
|
||||
|
||||
<div className="flex gap-6">
|
||||
<label className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={field.value.includes("wa")}
|
||||
onCheckedChange={(checked) => {
|
||||
checked
|
||||
? field.onChange([...field.value, "wa"])
|
||||
: field.onChange(field.value.filter((v) => v !== "wa"));
|
||||
}}
|
||||
/>
|
||||
WhatsApp
|
||||
</label>
|
||||
|
||||
<label className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={field.value.includes("email")}
|
||||
onCheckedChange={(checked) => {
|
||||
checked
|
||||
? field.onChange([...field.value, "email"])
|
||||
: field.onChange(
|
||||
field.value.filter((v) => v !== "email")
|
||||
);
|
||||
}}
|
||||
/>
|
||||
Email
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* TITLE */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Subject</FormLabel>
|
||||
<Input
|
||||
value={field.value}
|
||||
placeholder="Masukkan Judul"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormLabel>Judul</FormLabel>
|
||||
<Input {...field} placeholder="Masukkan judul" />
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* DETAIL */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
{type === "wa" ? (
|
||||
<Textarea value={field.value} onChange={field.onChange} />
|
||||
) : (
|
||||
<CustomEditor
|
||||
onChange={field.onChange}
|
||||
initialData={field.value}
|
||||
/>
|
||||
)}
|
||||
<FormLabel>Isi Pesan</FormLabel>
|
||||
|
||||
<CustomEditor
|
||||
onChange={field.onChange}
|
||||
initialData={field.value}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex flex-row gap-2 mt-4 pt-4">
|
||||
<Button
|
||||
size="md"
|
||||
type="button"
|
||||
variant="outline"
|
||||
color="destructive"
|
||||
className="text-xs"
|
||||
>
|
||||
|
||||
{/* BUTTONS */}
|
||||
<div className="flex gap-2 mt-4">
|
||||
<Button variant="outline" type="button">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="md" type="submit" color="primary" className="text-xs">
|
||||
<Button type="submit" color="primary">
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* MODAL SENT */}
|
||||
<Dialog open={openModal}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Terkirim !!</DialogTitle>
|
||||
<DialogTitle>Terkirim!</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col justify-center items-center gap-3 mb-3 text-sm">
|
||||
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<img
|
||||
src="/assets/img/illust-for-broadcast-sent.png"
|
||||
className="w-[70%]"
|
||||
/>
|
||||
Untuk melihat Email Terkirim silahkan cek menu “Sent”!
|
||||
Pesan telah dikirim sesuai pilihan Anda.
|
||||
</div>
|
||||
<DialogFooter className="flex justify-center">
|
||||
{/* <Link
|
||||
href={`/admin/broadcast/campaign-list/detail/${
|
||||
form.getValues("selected")[0]?.id
|
||||
}`}
|
||||
>
|
||||
<Button type="button" color="success">
|
||||
Menu "Sent"
|
||||
</Button>
|
||||
</Link> */}
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => router.push("/admin/broadcast")}
|
||||
color="primary"
|
||||
>
|
||||
Okay
|
||||
<DialogFooter className="flex justify-center">
|
||||
<Button onClick={() => router.push("/admin/broadcast")}>
|
||||
OK
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
|
@ -324,3 +338,330 @@ export default function ContentBlast(props: { type: string }) {
|
|||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
// "use client";
|
||||
// import { z } from "zod";
|
||||
// import { useForm } from "react-hook-form";
|
||||
// import { zodResolver } from "@hookform/resolvers/zod";
|
||||
// import {
|
||||
// Form,
|
||||
// FormField,
|
||||
// FormItem,
|
||||
// FormLabel,
|
||||
// FormMessage,
|
||||
// } from "@/components/ui/form";
|
||||
// import withReactContent from "sweetalert2-react-content";
|
||||
// import Swal from "sweetalert2";
|
||||
|
||||
// import { Input } from "@/components/ui/input";
|
||||
// import { Button } from "@/components/ui/button";
|
||||
|
||||
// import {
|
||||
// getLocaleTime,
|
||||
// getLocaleTimestamp,
|
||||
// textEllipsis,
|
||||
// } from "@/utils/globals";
|
||||
// import {
|
||||
// detailMediaSummary,
|
||||
// getMediaBlastCampaignList,
|
||||
// saveMediaBlastBroadcast,
|
||||
// saveMediaBlastCampaign,
|
||||
// } from "@/service/broadcast/broadcast";
|
||||
// import { error } from "@/config/swal";
|
||||
// import { Link, useRouter } from "@/i18n/routing";
|
||||
|
||||
// import { useEffect, useRef, useState } from "react";
|
||||
// import { useParams } from "next/navigation";
|
||||
// import Select from "react-select";
|
||||
// import makeAnimated from "react-select/animated";
|
||||
// import { Textarea } from "@/components/ui/textarea";
|
||||
// import {
|
||||
// Dialog,
|
||||
// DialogContent,
|
||||
// DialogFooter,
|
||||
// DialogHeader,
|
||||
// DialogTitle,
|
||||
// } from "@/components/ui/dialog";
|
||||
// import dynamic from "next/dynamic";
|
||||
|
||||
// const CustomEditor = dynamic(
|
||||
// () => {
|
||||
// return import("@/components/editor/custom-editor");
|
||||
// },
|
||||
// { ssr: false }
|
||||
// );
|
||||
|
||||
// const animatedComponent = makeAnimated();
|
||||
|
||||
// const FormSchema = z.object({
|
||||
// title: z.string({
|
||||
// required_error: "Required",
|
||||
// }),
|
||||
// url: z.string({
|
||||
// required_error: "Required",
|
||||
// }),
|
||||
// thumbnail: z.string({
|
||||
// required_error: "Required",
|
||||
// }),
|
||||
// detail: z.string({
|
||||
// required_error: "Required",
|
||||
// }),
|
||||
// selected: z
|
||||
// .array(
|
||||
// z.object({
|
||||
// id: z.number(),
|
||||
// label: z.string(),
|
||||
// value: z.string(),
|
||||
// })
|
||||
// )
|
||||
// .refine((value) => value.length > 0, {
|
||||
// message: "Required",
|
||||
// }),
|
||||
// });
|
||||
|
||||
// interface Campaign {
|
||||
// id: string;
|
||||
// name: string;
|
||||
// }
|
||||
|
||||
// export default function ContentBlast(props: { type: string }) {
|
||||
// const editor = useRef(null);
|
||||
// const id = useParams()?.id;
|
||||
// const MySwal = withReactContent(Swal);
|
||||
// const router = useRouter();
|
||||
// const { type } = props;
|
||||
|
||||
// const [dataSelectCampaign, setDataSelectCampaign] = useState<Campaign[]>([]);
|
||||
// const [openModal, setOpenModal] = useState(false);
|
||||
|
||||
// const form = useForm<z.infer<typeof FormSchema>>({
|
||||
// resolver: zodResolver(FormSchema),
|
||||
// defaultValues: { selected: [], detail: "" },
|
||||
// });
|
||||
|
||||
// const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
// if (form.getValues("detail") == "") {
|
||||
// form.setError("detail", {
|
||||
// type: "manual",
|
||||
// message: "Required",
|
||||
// });
|
||||
// } else {
|
||||
// MySwal.fire({
|
||||
// title: "Simpan Data",
|
||||
// text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
// icon: "warning",
|
||||
// showCancelButton: true,
|
||||
// cancelButtonColor: "#d33",
|
||||
// confirmButtonColor: "#3085d6",
|
||||
// confirmButtonText: "Simpan",
|
||||
// }).then((result) => {
|
||||
// if (result.isConfirmed) {
|
||||
// save(data);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
|
||||
// const save = async (data: z.infer<typeof FormSchema>) => {
|
||||
// const selectedCampaign = form.getValues("selected");
|
||||
|
||||
// for (let i = 0; i < selectedCampaign.length; i++) {
|
||||
// const reqData = {
|
||||
// mediaUploadId: id,
|
||||
// mediaBlastCampaignId: selectedCampaign[i].id,
|
||||
// subject: type == "wa" ? `*${data.title}*` : data.title,
|
||||
// body: data.detail?.replace(/\n/g, ""),
|
||||
// type: type,
|
||||
// isScheduled: false,
|
||||
// thumbnail: data?.thumbnail,
|
||||
// sendDate: getLocaleTimestamp(new Date()),
|
||||
// sendTime: getLocaleTime(new Date()),
|
||||
// contentUrl: data.url,
|
||||
// };
|
||||
|
||||
// console.log("req =>", reqData);
|
||||
// const response = await saveMediaBlastBroadcast(reqData);
|
||||
// }
|
||||
// setOpenModal(true);
|
||||
// };
|
||||
|
||||
// useEffect(() => {
|
||||
// async function initState() {
|
||||
// const response = await detailMediaSummary(String(id));
|
||||
// const details = response?.data?.data;
|
||||
// let pageUrl = details?.pageUrl || "";
|
||||
// if (pageUrl.includes("mediahub.polri.go.id")) {
|
||||
// pageUrl = pageUrl.replace(
|
||||
// /(\.id)(\/|$)/,
|
||||
// (match: any, p1: any, p2: any) => {
|
||||
// return p2.startsWith("/in") ? match : `${p1}/in${p2}`;
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
// if (details != undefined) {
|
||||
// form.setValue("thumbnail", details.smallThumbnailLink);
|
||||
// let body = `<div><p>Berita hari ini !!!</p>
|
||||
// <div style='margin-top:20px;border:1px solid;border-radius:10px;width:500px;background-color:#f7f7f7'>
|
||||
// <div>
|
||||
// <img style='width:500px;height:auto;border-radius:10px;object-fit:cover' src='${
|
||||
// details?.smallThumbnailLink
|
||||
// }'>
|
||||
// </div>
|
||||
// <a style='padding:5px 20px;margin:0;text-decoration:none' href='${pageUrl}'>${pageUrl}</a>
|
||||
// <h3 style='padding:5px 20px;margin:0'>${details?.title}</h3>
|
||||
// <p style='padding:0 20px;margin:0;margin-bottom:10px'>
|
||||
// ${textEllipsis(details?.description, 150)}
|
||||
// </p>
|
||||
// </div>
|
||||
// </div>`;
|
||||
// form.setValue("title", `${details?.title}`);
|
||||
// form.setValue(
|
||||
// "url",
|
||||
// details?.pageUrl || "https://mediahub.polri.go.id"
|
||||
// );
|
||||
// if (type == "wa") {
|
||||
// body = `${textEllipsis(details?.description, 150)}`;
|
||||
// form.setValue("detail", body);
|
||||
// } else {
|
||||
// form.setValue("detail", body);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// async function getCampaign() {
|
||||
// const response = await getMediaBlastCampaignList();
|
||||
// const campaign = response?.data?.data?.content;
|
||||
// handleLabelCampaign(campaign);
|
||||
// console.log(campaign);
|
||||
// }
|
||||
|
||||
// initState();
|
||||
// getCampaign();
|
||||
// }, [id]);
|
||||
|
||||
// function handleLabelCampaign(data: any) {
|
||||
// const optionArr: any = [];
|
||||
|
||||
// data.map((option: any) => {
|
||||
// optionArr.push({
|
||||
// id: option.id,
|
||||
// label: option.title,
|
||||
// value: option.title,
|
||||
// });
|
||||
// });
|
||||
// console.log("option arr", optionArr);
|
||||
// setDataSelectCampaign(optionArr);
|
||||
// }
|
||||
|
||||
// return (
|
||||
// <Form {...form}>
|
||||
// <form
|
||||
// onSubmit={form.handleSubmit(onSubmit)}
|
||||
// className="space-y-3 bg-white rounded-sm p-4"
|
||||
// >
|
||||
// <p className="fonnt-semibold">Broadcast</p>
|
||||
// <FormField
|
||||
// control={form.control}
|
||||
// name="selected"
|
||||
// render={({ field }) => (
|
||||
// <FormItem>
|
||||
// <FormLabel>Subject</FormLabel>
|
||||
// <Select
|
||||
// className="z-50"
|
||||
// options={dataSelectCampaign}
|
||||
// closeMenuOnSelect={false}
|
||||
// components={animatedComponent}
|
||||
// onChange={field.onChange}
|
||||
// isMulti
|
||||
// />
|
||||
|
||||
// <FormMessage />
|
||||
// </FormItem>
|
||||
// )}
|
||||
// />
|
||||
|
||||
// <FormField
|
||||
// control={form.control}
|
||||
// name="title"
|
||||
// render={({ field }) => (
|
||||
// <FormItem>
|
||||
// <FormLabel>Subject</FormLabel>
|
||||
// <Input
|
||||
// value={field.value}
|
||||
// placeholder="Masukkan Judul"
|
||||
// onChange={field.onChange}
|
||||
// />
|
||||
|
||||
// <FormMessage />
|
||||
// </FormItem>
|
||||
// )}
|
||||
// />
|
||||
// <FormField
|
||||
// control={form.control}
|
||||
// name="detail"
|
||||
// render={({ field }) => (
|
||||
// <FormItem>
|
||||
// <FormLabel>Detail Perencanaan</FormLabel>
|
||||
// {type === "wa" ? (
|
||||
// <Textarea value={field.value} onChange={field.onChange} />
|
||||
// ) : (
|
||||
// <CustomEditor
|
||||
// onChange={field.onChange}
|
||||
// initialData={field.value}
|
||||
// />
|
||||
// )}
|
||||
// <FormMessage />
|
||||
// </FormItem>
|
||||
// )}
|
||||
// />
|
||||
// <div className="flex flex-row gap-2 mt-4 pt-4">
|
||||
// <Button
|
||||
// size="md"
|
||||
// type="button"
|
||||
// variant="outline"
|
||||
// color="destructive"
|
||||
// className="text-xs"
|
||||
// >
|
||||
// Cancel
|
||||
// </Button>
|
||||
// <Button size="md" type="submit" color="primary" className="text-xs">
|
||||
// Submit
|
||||
// </Button>
|
||||
// </div>
|
||||
// <Dialog open={openModal}>
|
||||
// <DialogContent>
|
||||
// <DialogHeader>
|
||||
// <DialogTitle>Terkirim !!</DialogTitle>
|
||||
// </DialogHeader>
|
||||
// <div className="flex flex-col justify-center items-center gap-3 mb-3 text-sm">
|
||||
// <img
|
||||
// src="/assets/img/illust-for-broadcast-sent.png"
|
||||
// className="w-[70%]"
|
||||
// />
|
||||
// Untuk melihat Email Terkirim silahkan cek menu “Sent”!
|
||||
// </div>
|
||||
// <DialogFooter className="flex justify-center">
|
||||
// {/* <Link
|
||||
// href={`/admin/broadcast/campaign-list/detail/${
|
||||
// form.getValues("selected")[0]?.id
|
||||
// }`}
|
||||
// >
|
||||
// <Button type="button" color="success">
|
||||
// Menu "Sent"
|
||||
// </Button>
|
||||
// </Link> */}
|
||||
|
||||
// <Button
|
||||
// type="button"
|
||||
// onClick={() => router.push("/admin/broadcast")}
|
||||
// color="primary"
|
||||
// >
|
||||
// Okay
|
||||
// </Button>
|
||||
// </DialogFooter>
|
||||
// </DialogContent>
|
||||
// </Dialog>
|
||||
// </form>
|
||||
// </Form>
|
||||
// );
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ export type taskDetail = {
|
|||
is_active: string;
|
||||
isAssignmentAccepted: boolean;
|
||||
isDone: any;
|
||||
isAccept?: boolean;
|
||||
};
|
||||
|
||||
interface ListData {
|
||||
|
|
@ -244,18 +245,6 @@ export default function FormTaskTaDetail() {
|
|||
const [selectedCompetencies, setSelectedCompetencies] = useState<Set<number>>(
|
||||
new Set()
|
||||
);
|
||||
const [totalPage, setTotalPage] = React.useState(1);
|
||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
const [imageFiles, setImageFiles] = useState<FileWithPreview[]>([]);
|
||||
const [videoFiles, setVideoFiles] = useState<FileWithPreview[]>([]);
|
||||
const [textFiles, setTextFiles] = useState<FileWithPreview[]>([]);
|
||||
const [audioFiles, setAudioFiles] = useState<FileWithPreview[]>([]);
|
||||
const [isImageUploadFinish, setIsImageUploadFinish] = useState(false);
|
||||
const [isVideoUploadFinish, setIsVideoUploadFinish] = useState(false);
|
||||
const [isTextUploadFinish, setIsTextUploadFinish] = useState(false);
|
||||
const [isAudioUploadFinish, setIsAudioUploadFinish] = useState(false);
|
||||
const [voiceNoteLink, setVoiceNoteLink] = useState("");
|
||||
const [statusAcceptance, setStatusAcceptance] = useState();
|
||||
const [modalType, setModalType] = useState<"terkirim" | "diterima" | "">("");
|
||||
const [Isloading, setLoading] = useState<boolean>(false);
|
||||
|
|
@ -265,10 +254,8 @@ export default function FormTaskTaDetail() {
|
|||
const [isRecording, setIsRecording] = useState(false);
|
||||
const [timer, setTimer] = useState<number>(120);
|
||||
const [search, setSearch] = React.useState<string>("");
|
||||
|
||||
const [wavesurfer, setWavesurfer] = useState<WaveSurfer>();
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
|
||||
const [imageUploadedFiles, setImageUploadedFiles] = useState<FileUploaded[]>(
|
||||
[]
|
||||
);
|
||||
|
|
@ -293,6 +280,8 @@ export default function FormTaskTaDetail() {
|
|||
const [selectedAudio, setSelectedAudio] = useState<string | null>(
|
||||
audioUploadedFiles?.[0]?.url || null
|
||||
);
|
||||
const [acceptLoading, setAcceptLoading] = useState(false);
|
||||
const [isAccepted, setIsAccepted] = useState(false);
|
||||
|
||||
const [platformTypeVisible, setPlatformTypeVisible] = useState(false);
|
||||
const [unitSelection, setUnitSelection] = useState({
|
||||
|
|
@ -400,6 +389,18 @@ export default function FormTaskTaDetail() {
|
|||
fetchListExpert();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchAcceptanceStatus() {
|
||||
const res = await getAcceptanceAssignmentStatus(id);
|
||||
// API: /assignment/acceptance?id=103&isAccept=true
|
||||
const status = res?.data?.data?.isAccept === true;
|
||||
|
||||
setIsAccepted(status);
|
||||
}
|
||||
|
||||
fetchAcceptanceStatus();
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchExpertsForCompetencies = async () => {
|
||||
const allExperts: any[] = [];
|
||||
|
|
@ -743,18 +744,26 @@ export default function FormTaskTaDetail() {
|
|||
}
|
||||
|
||||
const handleAcceptAcceptance = async () => {
|
||||
loading();
|
||||
console.log("Id user :", userId);
|
||||
const response = await acceptAssignmentTa(id);
|
||||
if (acceptLoading || isAccepted) return;
|
||||
|
||||
if (response?.error) {
|
||||
error(response?.message);
|
||||
return false;
|
||||
setAcceptLoading(true);
|
||||
|
||||
loading();
|
||||
|
||||
const res = await acceptAssignmentTa(id);
|
||||
|
||||
close();
|
||||
setAcceptLoading(false);
|
||||
|
||||
if (res?.error) {
|
||||
error(res?.message || "Gagal menerima tugas");
|
||||
return;
|
||||
}
|
||||
|
||||
// ini penting!
|
||||
setIsAccepted(true);
|
||||
|
||||
successCallback();
|
||||
getDataAcceptance();
|
||||
return false;
|
||||
};
|
||||
|
||||
async function getListAcceptance(): Promise<void> {
|
||||
|
|
@ -970,7 +979,9 @@ export default function FormTaskTaDetail() {
|
|||
{detail !== undefined ? (
|
||||
<div className="px-6 py-6">
|
||||
<div className="flex flex-col sm:flex-row lg:flex-row justify-between">
|
||||
<p className="text-lg font-semibold mb-3">{t("detail-task", { defaultValue: "Detail Task" })}</p>
|
||||
<p className="text-lg font-semibold mb-3">
|
||||
{t("detail-task", { defaultValue: "Detail Task" })}
|
||||
</p>
|
||||
{/* <div
|
||||
className="flex gap-3"
|
||||
style={
|
||||
|
|
@ -1020,7 +1031,9 @@ export default function FormTaskTaDetail() {
|
|||
<form>
|
||||
<div className="gap-5 mb-5">
|
||||
<div className="space-y-2">
|
||||
<Label>{t("unique-code", { defaultValue: "Unique Code" })}</Label>
|
||||
<Label>
|
||||
{t("unique-code", { defaultValue: "Unique Code" })}
|
||||
</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="uniqueCode"
|
||||
|
|
@ -1072,7 +1085,9 @@ export default function FormTaskTaDetail() {
|
|||
</RadioGroup>
|
||||
</div> */}
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("areas-expertise", { defaultValue: "Areas Expertise" })}</Label>
|
||||
<Label>
|
||||
{t("areas-expertise", { defaultValue: "Areas Expertise" })}
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{userCompetencies?.map((item: any) => (
|
||||
<div className="flex items-center gap-2" key={item.id}>
|
||||
|
|
@ -1171,7 +1186,9 @@ export default function FormTaskTaDetail() {
|
|||
<div></div>
|
||||
|
||||
<div className="mt-6 space-y-2">
|
||||
<Label>{t("description", { defaultValue: "Description" })}</Label>
|
||||
<Label>
|
||||
{t("description", { defaultValue: "Description" })}
|
||||
</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="naration"
|
||||
|
|
@ -1186,11 +1203,15 @@ export default function FormTaskTaDetail() {
|
|||
)} */}
|
||||
</div>
|
||||
<div className=" mt-5 space-y-2">
|
||||
<Label htmlFor="attachment">{t("attachment", { defaultValue: "Attachment" })}</Label>
|
||||
<Label htmlFor="attachment">
|
||||
{t("attachment", { defaultValue: "Attachment" })}
|
||||
</Label>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
{videoUploadedFiles?.length > 0 && (
|
||||
<Label>{t("audio-visual", { defaultValue: "Audio Visual" })}</Label>
|
||||
<Label>
|
||||
{t("audio-visual", { defaultValue: "Audio Visual" })}
|
||||
</Label>
|
||||
)}
|
||||
<div>
|
||||
{selectedVideo && (
|
||||
|
|
@ -1466,7 +1487,7 @@ export default function FormTaskTaDetail() {
|
|||
</Button>
|
||||
</div> */}
|
||||
<div className="">
|
||||
<Button
|
||||
{/* <Button
|
||||
type="button"
|
||||
className="btn btn-primary lg:mx-3"
|
||||
style={
|
||||
|
|
@ -1481,6 +1502,21 @@ export default function FormTaskTaDetail() {
|
|||
onClick={() => handleAcceptAcceptance()}
|
||||
>
|
||||
Terima Tugas
|
||||
</Button> */}
|
||||
<Button
|
||||
type="button"
|
||||
onClick={handleAcceptAcceptance}
|
||||
disabled={isAccepted || acceptLoading}
|
||||
className="
|
||||
btn btn-primary lg:mx-3
|
||||
disabled:opacity-50 disabled:cursor-not-allowed
|
||||
"
|
||||
>
|
||||
{isAccepted
|
||||
? "Tugas Sudah Diterima"
|
||||
: acceptLoading
|
||||
? "Memproses..."
|
||||
: "Terima Tugas"}
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -514,7 +514,9 @@ export default function FormTaskTa() {
|
|||
return (
|
||||
<Card>
|
||||
<div className="px-6 py-6">
|
||||
<p className="text-lg font-semibold mb-3">{t("form-task", { defaultValue: "Form Task" })}</p>
|
||||
<p className="text-lg font-semibold mb-3">
|
||||
{t("form-task", { defaultValue: "Form Task" })}
|
||||
</p>
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="gap-5 mb-5">
|
||||
|
|
@ -540,7 +542,9 @@ export default function FormTaskTa() {
|
|||
</div>
|
||||
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("assigment-type", { defaultValue: "Assigment Type" })} </Label>
|
||||
<Label>
|
||||
{t("assigment-type", { defaultValue: "Assigment Type" })}{" "}
|
||||
</Label>
|
||||
<RadioGroup
|
||||
value={taskType}
|
||||
onValueChange={(value) => setTaskType(String(value))}
|
||||
|
|
@ -593,7 +597,9 @@ export default function FormTaskTa() {
|
|||
</Popover>
|
||||
</div>
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("areas-expertise", { defaultValue: "Areas Expertise" })}</Label>
|
||||
<Label>
|
||||
{t("areas-expertise", { defaultValue: "Areas Expertise" })}
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{userCompetencies?.map((item: any) => (
|
||||
<div className="flex items-center gap-2" key={item.id}>
|
||||
|
|
@ -608,7 +614,9 @@ export default function FormTaskTa() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("choose-expert", { defaultValue: "Choose Expert" })}</Label>
|
||||
<Label>
|
||||
{t("choose-expert", { defaultValue: "Choose Expert" })}
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
|
|
@ -699,11 +707,23 @@ export default function FormTaskTa() {
|
|||
)}
|
||||
</div>
|
||||
<div className="space-y-2.5 mt-5">
|
||||
<Label htmlFor="attachments">{t("attachment", { defaultValue: "Attachment" })}</Label>
|
||||
<Label htmlFor="attachments">
|
||||
{t("attachment", { defaultValue: "Attachment" })}
|
||||
</Label>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<Label>{t("audio-visual", { defaultValue: "Audio Visual" })}</Label>
|
||||
<Label>
|
||||
{t("audio-visual", { defaultValue: "Audio Visual" })}
|
||||
</Label>
|
||||
<FileUploader
|
||||
accept={{
|
||||
"video/*": [],
|
||||
}}
|
||||
maxSize={100}
|
||||
label="Upload file dengan format video."
|
||||
onDrop={(files) => setVideoFiles(files)}
|
||||
/>
|
||||
{/* <FileUploader
|
||||
accept={{
|
||||
"mp4/*": [],
|
||||
"mov/*": [],
|
||||
|
|
@ -711,7 +731,7 @@ export default function FormTaskTa() {
|
|||
maxSize={100}
|
||||
label="Upload file dengan format .mp4 atau .mov."
|
||||
onDrop={(files) => setVideoFiles(files)}
|
||||
/>
|
||||
/> */}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>{t("image", { defaultValue: "Image" })}</Label>
|
||||
|
|
@ -727,13 +747,21 @@ export default function FormTaskTa() {
|
|||
<div className="space-y-2">
|
||||
<Label>{t("text", { defaultValue: "Text" })}</Label>
|
||||
<FileUploader
|
||||
accept={{
|
||||
"application/pdf": [],
|
||||
}}
|
||||
maxSize={100}
|
||||
label="Upload file PDF."
|
||||
onDrop={(files) => setTextFiles(files)}
|
||||
/>
|
||||
{/* <FileUploader
|
||||
accept={{
|
||||
"pdf/*": [],
|
||||
}}
|
||||
maxSize={100}
|
||||
label="Upload file dengan format .pdf."
|
||||
onDrop={(files) => setTextFiles(files)}
|
||||
/>
|
||||
/> */}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>{t("audio", { defaultValue: "Audio" })}</Label>
|
||||
|
|
@ -747,6 +775,17 @@ export default function FormTaskTa() {
|
|||
downloadFileExtension="webm"
|
||||
/>
|
||||
<FileUploader
|
||||
accept={{
|
||||
"audio/*": [],
|
||||
}}
|
||||
maxSize={100}
|
||||
label="Upload file audio (mp3, wav, webm)."
|
||||
onDrop={(files) =>
|
||||
setAudioFiles((prev) => [...prev, ...files])
|
||||
}
|
||||
className="mt-2"
|
||||
/>
|
||||
{/* <FileUploader
|
||||
accept={{
|
||||
"mp3/*": [],
|
||||
"wav/*": [],
|
||||
|
|
@ -757,7 +796,7 @@ export default function FormTaskTa() {
|
|||
setAudioFiles((prev) => [...prev, ...files])
|
||||
}
|
||||
className="mt-2"
|
||||
/>
|
||||
/> */}
|
||||
</div>
|
||||
{audioFiles?.map((audio: any, idx: any) => (
|
||||
<div
|
||||
|
|
@ -775,9 +814,11 @@ export default function FormTaskTa() {
|
|||
</Button>
|
||||
</div>
|
||||
))}
|
||||
{/* {isRecording && <p>Recording... {timer} seconds remaining</p>}{" "}
|
||||
{isRecording && <p>Recording... {timer} seconds remaining</p>}{" "}
|
||||
<div className="mt-4 space-y-2">
|
||||
<Label className="">{t("news-links", { defaultValue: "News Links" })}</Label>
|
||||
<Label className="">
|
||||
{t("news-links", { defaultValue: "News Links" })}
|
||||
</Label>
|
||||
{links.map((link, index) => (
|
||||
<div key={index} className="flex items-center gap-2 mt-2">
|
||||
<Input
|
||||
|
|
@ -808,7 +849,7 @@ export default function FormTaskTa() {
|
|||
>
|
||||
{t("add-links", { defaultValue: "Add Links" })}
|
||||
</Button>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -87,9 +87,7 @@ const CustomEditor = dynamic(
|
|||
export default function FormTaskTaNew() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
const editor = useRef(null);
|
||||
type ImageSchema = z.infer<typeof imageSchema>;
|
||||
|
||||
const t = useTranslations("Form");
|
||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||
const taskId = Cookies.get("taskId");
|
||||
|
|
@ -97,33 +95,14 @@ export default function FormTaskTaNew() {
|
|||
const scheduleType = Cookies.get("scheduleType");
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
const { id } = useParams() as { id: string };
|
||||
console.log(id);
|
||||
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [selectedCategory, setSelectedCategory] = useState<any>();
|
||||
const [tags, setTags] = useState<any[]>([]);
|
||||
const [thumbnail, setThumbnail] = useState<File | null>(null);
|
||||
const [preview, setPreview] = useState<string | null>(null);
|
||||
const [selectedLanguage, setSelectedLanguage] = useState("");
|
||||
|
||||
const [selectedSEO, setSelectedSEO] = useState<string>("");
|
||||
const [title, setTitle] = useState<string>("");
|
||||
const [selectedAdvConfig, setSelectedAdvConfig] = useState<string>("");
|
||||
const [editingArticleId, setEditingArticleId] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [isLoadingData, setIsLoadingData] = useState<boolean>(false);
|
||||
|
||||
const [articleIds, setArticleIds] = useState<string[]>([]);
|
||||
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
|
||||
const [articleBody, setArticleBody] = useState<string>("");
|
||||
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
|
||||
null
|
||||
);
|
||||
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
|
||||
const [selectedSize, setSelectedSize] = useState("");
|
||||
const [detailData, setDetailData] = useState<any>(null);
|
||||
const [articleImages, setArticleImages] = useState<string[]>([]);
|
||||
const [isSwitchOn, setIsSwitchOn] = useState<boolean>(false);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [imageFiles, setImageFiles] = useState<FileWithPreview[]>([]);
|
||||
|
|
@ -139,26 +118,7 @@ export default function FormTaskTaNew() {
|
|||
const [links, setLinks] = useState<string[]>([""]);
|
||||
const [timer, setTimer] = useState<number>(120);
|
||||
const [fileTypeId, setFileTypeId] = useState<string>("");
|
||||
|
||||
const [content, setContent] = useState("");
|
||||
|
||||
const [selectedTarget, setSelectedTarget] = useState("");
|
||||
const [unitSelection, setUnitSelection] = useState({
|
||||
allUnit: false,
|
||||
mabes: false,
|
||||
polda: false,
|
||||
polres: false,
|
||||
});
|
||||
|
||||
let progressInfo: any = [];
|
||||
let counterUpdateProgress = 0;
|
||||
const [progressList, setProgressList] = useState<any>([]);
|
||||
let uploadPersen = 0;
|
||||
const [isStartUpload, setIsStartUpload] = useState(false);
|
||||
const [counterProgress, setCounterProgress] = useState(0);
|
||||
|
||||
const [files, setFiles] = useState<FileWithPreview[]>([]);
|
||||
const [filesTemp, setFilesTemp] = useState<File[]>([]);
|
||||
const [publishedFor, setPublishedFor] = useState<string[]>([]);
|
||||
const [detail, setDetail] = useState<taskDetail>();
|
||||
const [refresh] = useState(false);
|
||||
|
|
@ -178,7 +138,7 @@ export default function FormTaskTaNew() {
|
|||
});
|
||||
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
title: z.string().optional(),
|
||||
description: z
|
||||
.string()
|
||||
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." })
|
||||
|
|
@ -339,7 +299,7 @@ export default function FormTaskTaNew() {
|
|||
const save = async (data: ImageSchema) => {
|
||||
loading();
|
||||
const finalTags = tags.join(", ");
|
||||
const finalTitle = isSwitchOn ? title : data.title;
|
||||
const finalTitle = data.title || detail?.title || "";
|
||||
const finalDescription = articleBody || data.description;
|
||||
|
||||
if (!finalDescription.trim()) {
|
||||
|
|
@ -662,8 +622,7 @@ export default function FormTaskTaNew() {
|
|||
size="md"
|
||||
type="text"
|
||||
defaultValue={detail?.title}
|
||||
onChange={field.onChange}
|
||||
placeholder="Enter Title"
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
@ -676,7 +635,11 @@ export default function FormTaskTaNew() {
|
|||
|
||||
<div className="flex flex-col sm:flex-row lg:flex-row sm:items-center lg:items-center gap-3">
|
||||
<div className=" space-y-2 w-3/12">
|
||||
<Label>{t("assigment-type", { defaultValue: "Assigment Type" })}</Label>
|
||||
<Label>
|
||||
{t("assigment-type", {
|
||||
defaultValue: "Assigment Type",
|
||||
})}
|
||||
</Label>
|
||||
<Select onValueChange={(val) => setFileTypeId(val)}>
|
||||
<SelectTrigger size="md">
|
||||
<SelectValue placeholder="Choose" />
|
||||
|
|
@ -717,7 +680,9 @@ export default function FormTaskTaNew() {
|
|||
</div>
|
||||
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("description", { defaultValue: "Description" })}</Label>
|
||||
<Label>
|
||||
{t("description", { defaultValue: "Description" })}
|
||||
</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
|
|
@ -743,13 +708,21 @@ export default function FormTaskTaNew() {
|
|||
)}
|
||||
</div>
|
||||
<div className="space-y-2.5 mt-5">
|
||||
<Label htmlFor="attachments">{t("attachment", { defaultValue: "Attachment" })}</Label>
|
||||
<Label htmlFor="attachments">
|
||||
{t("attachment", { defaultValue: "Attachment" })}
|
||||
</Label>
|
||||
<div className="space-y-3">
|
||||
{fileTypeId === "2" && (
|
||||
<div>
|
||||
<Label>{t("audio-visual", { defaultValue: "Audio Visual" })}</Label>
|
||||
<Label>
|
||||
{t("audio-visual", {
|
||||
defaultValue: "Audio Visual",
|
||||
})}
|
||||
</Label>
|
||||
<FileUploader
|
||||
accept={{ "mp4/*": [], "mov/*": [] }}
|
||||
accept={{
|
||||
"video/*": [], // menerima mp4, mov, mkv, avi, dll (lebih aman)
|
||||
}}
|
||||
maxSize={100}
|
||||
label="Upload file dengan format .mp4 atau .mov."
|
||||
onDrop={(files) => setVideoFiles(files)}
|
||||
|
|
@ -773,7 +746,9 @@ export default function FormTaskTaNew() {
|
|||
<div className="space-y-2">
|
||||
<Label>{t("text", { defaultValue: "Text" })}</Label>
|
||||
<FileUploader
|
||||
accept={{ "pdf/*": [] }}
|
||||
accept={{
|
||||
"application/pdf": [], // lebih presisi
|
||||
}}
|
||||
maxSize={100}
|
||||
label="Upload file dengan format .pdf."
|
||||
onDrop={(files) => setTextFiles(files)}
|
||||
|
|
@ -794,7 +769,9 @@ export default function FormTaskTaNew() {
|
|||
downloadFileExtension="webm"
|
||||
/>
|
||||
<FileUploader
|
||||
accept={{ "mp3/*": [], "wav/*": [] }}
|
||||
accept={{
|
||||
"audio/*": [], // menerima mp3, wav, webm (untuk hasil rekaman)
|
||||
}}
|
||||
maxSize={100}
|
||||
label="Upload file dengan format .mp3 atau .wav."
|
||||
onDrop={(files) =>
|
||||
|
|
@ -811,7 +788,9 @@ export default function FormTaskTaNew() {
|
|||
key={idx}
|
||||
className="flex flex-row justify-between items-center"
|
||||
>
|
||||
<p>{t("voice-note", { defaultValue: "Voice Note" })}</p>
|
||||
<p>
|
||||
{t("voice-note", { defaultValue: "Voice Note" })}
|
||||
</p>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => handleDeleteAudio(idx)}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export async function detailMediaSummary(id: string) {
|
|||
export async function saveMediaBlastAccount(data: {
|
||||
accountName: string;
|
||||
accountType: string;
|
||||
accountCategory: string;
|
||||
// accountCategory: string;
|
||||
emailAddress: string;
|
||||
whatsappNumber: string;
|
||||
id?: string;
|
||||
|
|
|
|||
Loading…
Reference in New Issue