Merge branch 'dev-rama' of https://gitlab.com/hanifsalafi/mediahub_redesign
This commit is contained in:
commit
da819910dd
|
|
@ -0,0 +1,91 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import {
|
||||||
|
formatDateToIndonesian,
|
||||||
|
getOnlyDate,
|
||||||
|
htmlToString,
|
||||||
|
} from "@/utils/globals";
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
|
} from "@/components/ui/accordion";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
accessorKey: "name",
|
||||||
|
header: "Nama",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("name")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "region",
|
||||||
|
header: "Wilayah",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("region")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "skills",
|
||||||
|
header: "Bidang Keahlian",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("skills")}</span>,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
accessorKey: "experience",
|
||||||
|
header: "Pengalaman",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("experience")}</span>,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: "actions",
|
||||||
|
accessorKey: "action",
|
||||||
|
header: "Actions",
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
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>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<Link href="#">Detail</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,276 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { UserIcon } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
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 { listEnableCategory } from "@/service/content/content";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { close, loading } from "@/config/swal";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
|
||||||
|
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();
|
||||||
|
const [showData, setShowData] = React.useState("10");
|
||||||
|
const [categories, setCategories] = React.useState<any>();
|
||||||
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
const [categoryFilter, setCategoryFilter] = React.useState<number[]>([]);
|
||||||
|
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let typingTimer: any;
|
||||||
|
const doneTypingInterval = 1500;
|
||||||
|
|
||||||
|
const handleKeyUp = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function doneTyping() {
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get("page");
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
setPagination({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
}, [page, showData]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
loading();
|
||||||
|
|
||||||
|
const contentData = dummyData;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(contentData?.length);
|
||||||
|
setTotalPage(1);
|
||||||
|
close();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
getCategories();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
async function getCategories() {
|
||||||
|
const category = await listEnableCategory("");
|
||||||
|
const resCategory = category.data.data.content;
|
||||||
|
setCategories(resCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
|
<div className="flex justify-between mb-10 items-center">
|
||||||
|
<p className="text-xl font-medium text-default-900">Tenaga Ahli</p>
|
||||||
|
<Link href="/admin/add-experts/create">
|
||||||
|
<Button color="primary" size="md" className="text-sm">
|
||||||
|
<UserIcon />
|
||||||
|
Tambah Tenaga Ahli
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between ">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search"
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
className="max-w-[300px]"
|
||||||
|
/>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
1 - {showData} Data
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56">
|
||||||
|
<DropdownMenuRadioGroup
|
||||||
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="20">
|
||||||
|
1 - 20 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="25">
|
||||||
|
1 - 25 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table className="overflow-hidden">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddExpertTable;
|
||||||
|
|
@ -0,0 +1,258 @@
|
||||||
|
"use client";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import { useRouter } from "@/i18n/routing";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
name: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
phoneNumber: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
email: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
region: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
skills: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
experience: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
company: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function AddExpertForm() {
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const router = useRouter();
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||||
|
console.log("data", data);
|
||||||
|
|
||||||
|
// successSubmit();
|
||||||
|
};
|
||||||
|
|
||||||
|
function successSubmit() {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Sukses",
|
||||||
|
icon: "success",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "OK",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
router.push("/admin/add-experts");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="space-y-3 bg-white rounded-sm p-4"
|
||||||
|
>
|
||||||
|
<p className="fonnt-semibold">Campaign</p>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nama Lengkap</FormLabel>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Nama Lengkap"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="phoneNumber"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>No. HP</FormLabel>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Nama Lengkap"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="email"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Email</FormLabel>
|
||||||
|
<Input
|
||||||
|
type="email"
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Nama Lengkap"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="region"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Wilayah</FormLabel>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Pilih Region" />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="nasional">Nasional</SelectItem>
|
||||||
|
<SelectItem value="jakarta">DKI Jakarta</SelectItem>
|
||||||
|
<SelectItem value="jabar">Jawa Barat</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="skills"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Bidang Keahlian</FormLabel>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Pilih Bidang Keahlian" />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="komunikasi">Komunikasi</SelectItem>
|
||||||
|
<SelectItem value="hukum">Hukum</SelectItem>
|
||||||
|
<SelectItem value="bahasa">Bahasa</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="experience"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Pengalaman</FormLabel>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Pilih Pengalaman" />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="akademisi">Akademisi</SelectItem>
|
||||||
|
<SelectItem value="praktisi">Praktisi</SelectItem>
|
||||||
|
<SelectItem value="akademisi+praktisi">
|
||||||
|
Akademisi + Praktisi
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="company"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nama Institusi/Perusahaan</FormLabel>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Nama Institusi/Perusahaan"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex flex-row justify-end 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import AddExpertTable from "./component/table";
|
||||||
|
|
||||||
|
export default function AddExpert() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<AddExpertTable />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import { close, error, loading } from "@/config/swal";
|
||||||
|
import { deleteMediaBlastAccount } from "@/service/broadcast/broadcast";
|
||||||
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
accessorKey: "accountName",
|
||||||
|
header: "Nama",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("accountName")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "accountType",
|
||||||
|
header: "Tipe Akun",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("accountType")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "accountCategory",
|
||||||
|
header: "Kategory",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="uppercase">{row.getValue("accountCategory")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "emailAddress",
|
||||||
|
header: "Email",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("emailAddress")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "whatsappNumber",
|
||||||
|
header: "Whatsapp",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("whatsappNumber")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "actions",
|
||||||
|
accessorKey: "action",
|
||||||
|
header: "Actions",
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const { toast } = useToast();
|
||||||
|
const router = useRouter();
|
||||||
|
const handleDelete = (id: string) => {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Apakah anda ingin menghapus data?",
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: "#dc3545",
|
||||||
|
confirmButtonText: "Iya",
|
||||||
|
cancelButtonText: "Tidak",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
doDeleteAccount(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
async function succes() {
|
||||||
|
toast({
|
||||||
|
title: "Sukses",
|
||||||
|
description: "Berhasil Delete",
|
||||||
|
});
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doDeleteAccount(id: string) {
|
||||||
|
loading();
|
||||||
|
const response = await deleteMediaBlastAccount(id);
|
||||||
|
close();
|
||||||
|
if (response?.error) {
|
||||||
|
error(response.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
succes();
|
||||||
|
}
|
||||||
|
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>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<Link
|
||||||
|
href={`/admin/broadcast/campaign-list/account-list/edit/${row.original.id}`}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<a onClick={() => handleDelete(row.original.id)}>Delete</a>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,275 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { UserIcon } from "lucide-react";
|
||||||
|
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import columns from "./column";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
import { getMediaBlastAccountPage } from "@/service/broadcast/broadcast";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { close, loading } from "@/config/swal";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
|
||||||
|
const AccountListTable = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const [filtered, setFiltered] = React.useState<string[]>([]);
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get("page");
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [page]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
loading();
|
||||||
|
const res = await getMediaBlastAccountPage(
|
||||||
|
page - 1,
|
||||||
|
filtered ? filtered.join(",") : ""
|
||||||
|
);
|
||||||
|
const data = res.data?.data;
|
||||||
|
const contentData = data?.content;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * 10 + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(data?.totalElements);
|
||||||
|
setTotalPage(data?.totalPages);
|
||||||
|
close();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFilter = (id: string, checked: boolean) => {
|
||||||
|
let temp = [...filtered];
|
||||||
|
if (checked) {
|
||||||
|
temp = [...temp, id];
|
||||||
|
} else {
|
||||||
|
temp = temp.filter((a) => a !== id);
|
||||||
|
}
|
||||||
|
setFiltered(temp);
|
||||||
|
console.log("sss", temp);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
|
<div className="flex justify-between mb-10 items-center">
|
||||||
|
<p className="text-xl font-medium text-default-900">Daftar Akun</p>
|
||||||
|
<div className="flex flex-row gap-3">
|
||||||
|
<Link href="/admin/broadcast/campaign-list/account-list/create">
|
||||||
|
<Button color="primary" size="md" className="text-sm">
|
||||||
|
<Icon icon="tdesign:user-add-filled" />
|
||||||
|
Tambah Akun
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Link href="/admin/broadcast/campaign-list/import">
|
||||||
|
<Button color="success" size="md" className="text-sm">
|
||||||
|
<UserIcon />
|
||||||
|
Import Akun
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
Filter
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-80 ">
|
||||||
|
<div className="flex flex-col gap-2 px-2">
|
||||||
|
<div className="flex justify-between text-sm">
|
||||||
|
<p>Filter</p>
|
||||||
|
<a
|
||||||
|
onClick={() => fetchData()}
|
||||||
|
className="cursor-pointer text-primary"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1 overflow-auto max-h-[300px] text-xs custom-scrollbar-table">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={filtered.includes("polri")}
|
||||||
|
onCheckedChange={(e) => handleFilter("polri", Boolean(e))}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
POLRI
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={filtered.includes("jurnalis")}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleFilter("jurnalis", Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
JURNALIS
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={filtered.includes("umum")}
|
||||||
|
onCheckedChange={(e) => handleFilter("umum", Boolean(e))}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
UMUM
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={filtered.includes("ksp")}
|
||||||
|
onCheckedChange={(e) => handleFilter("ksp", Boolean(e))}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
KSP
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<Table className="overflow-hidden">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AccountListTable;
|
||||||
|
|
@ -0,0 +1,306 @@
|
||||||
|
"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,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} 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 {
|
||||||
|
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";
|
||||||
|
|
||||||
|
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", "umumu", "ksp"], {
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
email: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
whatsapp: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function CreateAccountForBroadcast() {
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const router = useRouter();
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: { accountType: [] },
|
||||||
|
});
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
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>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>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,325 @@
|
||||||
|
"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,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} 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 {
|
||||||
|
getMediaBlastAccount,
|
||||||
|
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 { useParams } from "next/navigation";
|
||||||
|
import { useEffect } 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", "umumu", "ksp"], {
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
email: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
whatsapp: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function EditAccountForBroadcast() {
|
||||||
|
const id = useParams()?.id;
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const router = useRouter();
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: { accountType: [] },
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getDetailData() {
|
||||||
|
const response = await getMediaBlastAccount(String(id));
|
||||||
|
const details = response.data?.data;
|
||||||
|
console.log("new", details);
|
||||||
|
form.setValue("name", details.accountName);
|
||||||
|
form.setValue("email", details?.emailAddress);
|
||||||
|
form.setValue("whatsapp", details?.whatsappNumber);
|
||||||
|
form.setValue("accountCategory", details?.accountCategory);
|
||||||
|
form.setValue("accountType", details?.accountType.split(","));
|
||||||
|
}
|
||||||
|
|
||||||
|
getDetailData();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
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 = {
|
||||||
|
id: String(id),
|
||||||
|
accountName: data.name,
|
||||||
|
accountType: data.accountType.join(","),
|
||||||
|
accountCategory: data.accountCategory,
|
||||||
|
emailAddress: data.email,
|
||||||
|
whatsappNumber: data.whatsapp,
|
||||||
|
};
|
||||||
|
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}
|
||||||
|
value={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>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>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import AccountListTable from "./component/table";
|
||||||
|
|
||||||
|
export default function AdminCampaignList() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<AccountListTable />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
accessorKey: "title",
|
||||||
|
header: "Judul",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("title")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "sendTime",
|
||||||
|
header: "Tanggal & Waktu",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("sendTime")}</span>,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
accessorKey: "status",
|
||||||
|
header: "Status",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<Badge
|
||||||
|
className={`text-center items-center text-white ${
|
||||||
|
row.getValue("status") == "done"
|
||||||
|
? "bg-blue-500"
|
||||||
|
: row.getValue("status") == "waiting"
|
||||||
|
? "bg-yellow-400"
|
||||||
|
: "bg-red-600"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{row.getValue("status") == "done"
|
||||||
|
? "Selesai"
|
||||||
|
: row.getValue("status") == "waiting"
|
||||||
|
? "Proses"
|
||||||
|
: "Gagal"}
|
||||||
|
</Badge>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: "actions",
|
||||||
|
accessorKey: "action",
|
||||||
|
header: "Actions",
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
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>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<Link
|
||||||
|
href={`/admin/broadcast/campaign-list/detail/${row.original.id}`}
|
||||||
|
>
|
||||||
|
Detail
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<Link
|
||||||
|
href={`//admin/broadcast/campaign-list/edit/${row.original.id}`}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<a>Delete</a>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,215 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
Search,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
TrendingDown,
|
||||||
|
TrendingUp,
|
||||||
|
UserIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
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 {
|
||||||
|
getMediaBlastCampaignPage,
|
||||||
|
listDataMedia,
|
||||||
|
} from "@/service/broadcast/broadcast";
|
||||||
|
import { listEnableCategory } from "@/service/content/content";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { close, loading } from "@/config/swal";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
import { NewCampaignIcon } from "@/components/icon";
|
||||||
|
|
||||||
|
const CampaignListTable = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get("page");
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [page]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
loading();
|
||||||
|
const res = await getMediaBlastCampaignPage(page - 1);
|
||||||
|
const data = res.data?.data;
|
||||||
|
const contentData = data?.content;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * 10 + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("contentData : ", data);
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(data?.totalElements);
|
||||||
|
setTotalPage(data?.totalPages);
|
||||||
|
close();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
|
<div className="flex justify-between mb-10 items-center">
|
||||||
|
<p className="text-xl font-medium text-default-900">Daftar Campaign</p>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<Link href="/admin/broadcast/campaign-list/account-list">
|
||||||
|
<Button color="primary" size="md" className="text-sm">
|
||||||
|
<UserIcon />
|
||||||
|
Daftar Akun
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Link href="/admin/broadcast/campaign-list/create">
|
||||||
|
<Button color="primary" size="md" className="text-sm">
|
||||||
|
<NewCampaignIcon size={23} />
|
||||||
|
Buat Campaign Baru
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Table className="overflow-hidden">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CampaignListTable;
|
||||||
|
|
@ -0,0 +1,198 @@
|
||||||
|
"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,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} 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 { saveMediaBlastCampaign } from "@/service/broadcast/broadcast";
|
||||||
|
import { error } from "@/config/swal";
|
||||||
|
import { useRouter } from "@/i18n/routing";
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
date: z.date({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
title: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
time: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function CreateCampaign() {
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const router = useRouter();
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||||
|
const newDate = getOnlyDate(data.date).split("-");
|
||||||
|
const dates = `${newDate[2]}-${newDate[1]}-${newDate[0]}`;
|
||||||
|
const reqData = {
|
||||||
|
title: data.title,
|
||||||
|
sendTime: `${dates} ${data.time}:00`,
|
||||||
|
status: "waiting",
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await saveMediaBlastCampaign(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">Campaign</p>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="title"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Judul</FormLabel>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Judul"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<p className="text-sm">Jadwal Kirim</p>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="date"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant={"outline"}
|
||||||
|
size="md"
|
||||||
|
className={cn(
|
||||||
|
"w-[200px] justify-start text-left font-normal border-gray-200",
|
||||||
|
!field.value && "text-muted-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||||
|
{field.value ? (
|
||||||
|
format(field.value, "dd-MM-yyyy")
|
||||||
|
) : (
|
||||||
|
<span>Masukkan Bulan</span>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-auto p-0">
|
||||||
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
selected={field.value}
|
||||||
|
onSelect={field.onChange}
|
||||||
|
initialFocus
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="time"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<Input
|
||||||
|
type="time"
|
||||||
|
className="max-w-[100px]"
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Judul Perencanaan"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
"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,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} 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 {
|
||||||
|
getMediaBlastCampaignById,
|
||||||
|
saveMediaBlastCampaign,
|
||||||
|
} from "@/service/broadcast/broadcast";
|
||||||
|
import { error } from "@/config/swal";
|
||||||
|
import { useRouter } from "@/i18n/routing";
|
||||||
|
import { useParams } from "next/navigation";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
date: z.date({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
title: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
time: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
status: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function EditCampaign() {
|
||||||
|
const id = useParams()?.id;
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const router = useRouter();
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getInitData() {
|
||||||
|
const response = await getMediaBlastCampaignById(String(id));
|
||||||
|
const date = response?.data?.data?.sendTime.split(" ");
|
||||||
|
const dateInput: Date = new Date(date[0]);
|
||||||
|
|
||||||
|
const time = date[1].split(":");
|
||||||
|
|
||||||
|
form.setValue("title", response?.data?.data?.title);
|
||||||
|
form.setValue("status", response?.data?.data?.status);
|
||||||
|
form.setValue("date", new Date(format(dateInput, "dd-MM-yyyy")));
|
||||||
|
form.setValue("time", `${time[0]}:${time[1]}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id != undefined) {
|
||||||
|
getInitData();
|
||||||
|
}
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||||
|
const newDate = getOnlyDate(data.date).split("-");
|
||||||
|
const dates = `${newDate[2]}-${newDate[1]}-${newDate[0]}`;
|
||||||
|
const reqData = {
|
||||||
|
id: String(id),
|
||||||
|
title: data.title,
|
||||||
|
sendTime: `${dates} ${data.time}:00`,
|
||||||
|
status: data.status,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await saveMediaBlastCampaign(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">Campaign</p>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="title"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Judul</FormLabel>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Judul"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<p className="text-sm">Jadwal Kirim</p>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="date"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant={"outline"}
|
||||||
|
size="md"
|
||||||
|
className={cn(
|
||||||
|
"w-[200px] justify-start text-left font-normal border-gray-200",
|
||||||
|
!field.value && "text-muted-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||||
|
{field.value ? (
|
||||||
|
format(field.value, "dd-MM-yyyy")
|
||||||
|
) : (
|
||||||
|
<span>Masukkan Bulan</span>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-auto p-0">
|
||||||
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
selected={field.value}
|
||||||
|
onSelect={field.onChange}
|
||||||
|
initialFocus
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="time"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<Input
|
||||||
|
type="time"
|
||||||
|
className="max-w-[100px]"
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Judul Perencanaan"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import CampaignListTable from "./component/table";
|
||||||
|
|
||||||
|
export default function AdminCampaignList() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<CampaignListTable />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import {
|
||||||
|
formatDateToIndonesian,
|
||||||
|
getOnlyDate,
|
||||||
|
htmlToString,
|
||||||
|
} from "@/utils/globals";
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
|
} from "@/components/ui/accordion";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
accessorKey: "title",
|
||||||
|
header: "Judul",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("title")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "categoryName",
|
||||||
|
header: "Kategori",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "createdAt",
|
||||||
|
header: "Tanggal Unggah",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span>{formatDateToIndonesian(row.getValue("createdAt"))}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
accessorKey: "statusName",
|
||||||
|
header: "Status",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("statusName")}</span>,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: "actions",
|
||||||
|
accessorKey: "action",
|
||||||
|
header: "Actions",
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
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>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<Link
|
||||||
|
href={`/contributor/content/image/detail/${row.original.id}`}
|
||||||
|
>
|
||||||
|
Detail
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<Link href={`/admin/broadcast/email/${row.original.id}`}>
|
||||||
|
Email Blast
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<Link href={`/admin/broadcast/whatsapp/${row.original.id}`}>
|
||||||
|
Whatsapp Blast
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,403 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
Search,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
TrendingDown,
|
||||||
|
TrendingUp,
|
||||||
|
UserIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
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 { listEnableCategory } from "@/service/content/content";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { close, loading } from "@/config/swal";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
|
||||||
|
const BroadcastTable = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const [showData, setShowData] = React.useState("10");
|
||||||
|
const [categories, setCategories] = React.useState<any>();
|
||||||
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
const [categoryFilter, setCategoryFilter] = React.useState<number[]>([]);
|
||||||
|
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let typingTimer: any;
|
||||||
|
const doneTypingInterval = 1500;
|
||||||
|
|
||||||
|
const handleKeyUp = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function doneTyping() {
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get("page");
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
setPagination({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
}, [page, showData]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
loading();
|
||||||
|
const res = await listDataMedia(
|
||||||
|
page - 1,
|
||||||
|
showData,
|
||||||
|
"",
|
||||||
|
categoryFilter?.sort().join(","),
|
||||||
|
statusFilter?.sort().join(",")
|
||||||
|
);
|
||||||
|
const data = res.data?.data;
|
||||||
|
const contentData = data?.content;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("contentData : ", data);
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(data?.totalElements);
|
||||||
|
setTotalPage(data?.totalPages);
|
||||||
|
close();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
getCategories();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
async function getCategories() {
|
||||||
|
const category = await listEnableCategory("");
|
||||||
|
const resCategory = category.data.data.content;
|
||||||
|
setCategories(resCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChange = (type: string, id: number, checked: boolean) => {
|
||||||
|
if (type === "category") {
|
||||||
|
if (checked) {
|
||||||
|
const temp: number[] = [...categoryFilter];
|
||||||
|
temp.push(id);
|
||||||
|
setCategoryFilter(temp);
|
||||||
|
} else {
|
||||||
|
const temp = categoryFilter.filter((a) => a !== id);
|
||||||
|
setCategoryFilter(temp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (checked) {
|
||||||
|
const temp: number[] = [...statusFilter];
|
||||||
|
temp.push(id);
|
||||||
|
setStatusFilter(temp);
|
||||||
|
} else {
|
||||||
|
const temp = statusFilter.filter((a) => a !== id);
|
||||||
|
setStatusFilter(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
|
<div className="flex justify-between mb-10 items-center">
|
||||||
|
<p className="text-xl font-medium text-default-900">Brodcast</p>
|
||||||
|
<Link href="/admin/broadcast/campaign-list">
|
||||||
|
<Button color="primary" size="md" className="text-sm">
|
||||||
|
<UserIcon />
|
||||||
|
Daftar Campaign
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between ">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search"
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
className="max-w-[300px]"
|
||||||
|
/>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
1 - {showData} Data
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56">
|
||||||
|
<DropdownMenuRadioGroup
|
||||||
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="20">
|
||||||
|
1 - 20 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="25">
|
||||||
|
1 - 25 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
Filter
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-80 ">
|
||||||
|
<div className="flex flex-col gap-2 px-2">
|
||||||
|
<div className="flex justify-between text-sm">
|
||||||
|
<p>Filter</p>
|
||||||
|
<a
|
||||||
|
onClick={() => fetchData()}
|
||||||
|
className="cursor-pointer text-primary"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1 overflow-auto max-h-[300px] text-xs custom-scrollbar-table">
|
||||||
|
<p>Kategory</p>
|
||||||
|
{categories?.map((category: any) => (
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id={category.id}
|
||||||
|
checked={categoryFilter.includes(category.id)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("category", category.id, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor={category.id}
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
{category.name}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<p className="mt-3">Status</p>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(1)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 1, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Menunggu Review
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(2)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 2, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Diterima
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(3)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 3, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Minta Update{" "}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(4)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 4, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Ditolak
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table className="overflow-hidden">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BroadcastTable;
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import ContentBlast from "@/components/form/broadcast/content-blast-form";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
|
||||||
|
export default function CreateEmailBlast() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<ContentBlast type="email" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import BroadcastTable from "./component/table";
|
||||||
|
|
||||||
|
export default function AdminBroadcast() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<BroadcastTable />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import ContentBlast from "@/components/form/broadcast/content-blast-form";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
|
||||||
|
export default function CreateWABlast() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<ContentBlast type="wa" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,157 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import StatusToogle from "./status";
|
||||||
|
import { error } from "@/config/swal";
|
||||||
|
import { deleteCategory } from "@/service/settings/settings";
|
||||||
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
import EditCategoryModal from "./edit";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
import {
|
||||||
|
Menubar,
|
||||||
|
MenubarContent,
|
||||||
|
MenubarMenu,
|
||||||
|
MenubarTrigger,
|
||||||
|
} from "@/components/ui/menubar";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
accessorKey: "name",
|
||||||
|
header: "Nama Kategori",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("name")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "contentType",
|
||||||
|
header: "Tipe Konten",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("contentType")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "isInternational",
|
||||||
|
header: "Wilayah",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">
|
||||||
|
{row.getValue("isInternational") ? "INT" : "ID"}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "status",
|
||||||
|
header: "Status",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">
|
||||||
|
<StatusToogle id={row.original.id} initValue={row.original.isPublish} />
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "actions",
|
||||||
|
accessorKey: "action",
|
||||||
|
header: "Actions",
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const categoryDelete = async (id: number) => {
|
||||||
|
const response = await deleteCategory(id);
|
||||||
|
console.log(response);
|
||||||
|
if (response?.error) {
|
||||||
|
error(response.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
toast({
|
||||||
|
title: "Sukses",
|
||||||
|
description: "Berhasil Delete",
|
||||||
|
});
|
||||||
|
router.push("/admin/settings/category?dataChange=true");
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
// <Popover>
|
||||||
|
// <PopoverTrigger>
|
||||||
|
// <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>
|
||||||
|
// </PopoverTrigger>
|
||||||
|
// <PopoverContent className="w-40">
|
||||||
|
// <div className="flex flex-col">
|
||||||
|
// <EditCategoryModal />
|
||||||
|
// <a onClick={() => categoryDelete(row.original.id)}>Delete</a>
|
||||||
|
// </div>
|
||||||
|
// </PopoverContent>
|
||||||
|
// </Popover>
|
||||||
|
|
||||||
|
// <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>
|
||||||
|
// </DropdownMenuTrigger>
|
||||||
|
// <DropdownMenuContent className="p-0" align="end">
|
||||||
|
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
// <EditCategoryModal />
|
||||||
|
// </DropdownMenuItem>
|
||||||
|
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
// <a onClick={() => categoryDelete(row.original.id)}>Delete</a>
|
||||||
|
// </DropdownMenuItem>
|
||||||
|
// </DropdownMenuContent>
|
||||||
|
// </DropdownMenu>
|
||||||
|
|
||||||
|
<Menubar className="border-none">
|
||||||
|
<MenubarMenu>
|
||||||
|
<MenubarTrigger>
|
||||||
|
<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>
|
||||||
|
</MenubarTrigger>
|
||||||
|
<MenubarContent className="flex flex-col gap-2 justify-center items-start p-4">
|
||||||
|
<EditCategoryModal data={row.original} />
|
||||||
|
<a
|
||||||
|
onClick={() => categoryDelete(row.original.id)}
|
||||||
|
className="hover:underline cursor-pointer hover:text-destructive"
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</a>
|
||||||
|
</MenubarContent>
|
||||||
|
</MenubarMenu>
|
||||||
|
</Menubar>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,430 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { useRouter } from "@/i18n/routing";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { getUserRoles, postCategory } from "@/service/settings/settings";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { close, error, loading } from "@/config/swal";
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
title: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
description: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
contentType: z
|
||||||
|
.array(z.string())
|
||||||
|
.refine((value) => value.some((item) => item), {
|
||||||
|
message: "Required",
|
||||||
|
}),
|
||||||
|
selectedUser: z
|
||||||
|
.array(z.string())
|
||||||
|
.refine((value) => value.some((item) => item), {
|
||||||
|
message: "Required",
|
||||||
|
}),
|
||||||
|
publishTo: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
file: z
|
||||||
|
.instanceof(File, {
|
||||||
|
message: "Invalid file format",
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
.refine((file) => file && file.size > 0, {
|
||||||
|
message: "File is required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const listContent = [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
name: "Foto",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
name: "Audio Visual",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3",
|
||||||
|
name: "Teks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "4",
|
||||||
|
name: "Audio",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function CreateCategoryModal() {
|
||||||
|
const router = useRouter();
|
||||||
|
const [userList, setUserList] = useState<
|
||||||
|
{ id: string; name: string; isInternal: boolean }[]
|
||||||
|
>([]);
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: { contentType: [], selectedUser: [], publishTo: "national" },
|
||||||
|
});
|
||||||
|
|
||||||
|
const contentType = form.watch("contentType");
|
||||||
|
|
||||||
|
const isAllContentChecked = listContent.every((item) =>
|
||||||
|
contentType?.includes(item.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
const users = form.watch("selectedUser");
|
||||||
|
|
||||||
|
const isAllUserChecked = userList.every((item) => users?.includes(item.id));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getRoles();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
async function getRoles() {
|
||||||
|
const response = await getUserRoles();
|
||||||
|
let dataRoles = response.data.data;
|
||||||
|
for (let i = 0; i < dataRoles.length; i++) {
|
||||||
|
dataRoles[i].id = String(dataRoles[i].id);
|
||||||
|
}
|
||||||
|
setUserList(dataRoles);
|
||||||
|
console.log("dasda", dataRoles);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||||
|
if (data.file instanceof Blob) {
|
||||||
|
const formMedia = new FormData();
|
||||||
|
|
||||||
|
loading();
|
||||||
|
|
||||||
|
formMedia.append("name", data.title);
|
||||||
|
formMedia.append("description", data.description);
|
||||||
|
formMedia.append("mediaTypes", data.contentType.join(","));
|
||||||
|
formMedia.append("file", data.file);
|
||||||
|
formMedia.append("publishedFor", data.selectedUser.sort().join(","));
|
||||||
|
formMedia.append(
|
||||||
|
"isInt",
|
||||||
|
data.publishTo === "national" ? "false" : "true"
|
||||||
|
);
|
||||||
|
console.log(formMedia);
|
||||||
|
const response = await postCategory(formMedia);
|
||||||
|
close();
|
||||||
|
if (response?.error == true) {
|
||||||
|
error(response.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
router.push("/admin/settings/category?dataChange=true");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button color="primary" size="md">
|
||||||
|
Tambah Kategori
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent size="md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Tambah Kategori</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="space-y-3 bg-white rounded-sm"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="contentType"
|
||||||
|
render={() => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Tipe Konten</FormLabel>
|
||||||
|
|
||||||
|
<div className="flex flex-row items-center gap-2">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Checkbox
|
||||||
|
id="all"
|
||||||
|
checked={isAllContentChecked}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
if (checked) {
|
||||||
|
form.setValue(
|
||||||
|
"contentType",
|
||||||
|
listContent.map((item) => item.id)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
form.setValue("contentType", []);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="all" className="text-sm">
|
||||||
|
Semua
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{listContent.map((item) => (
|
||||||
|
<FormField
|
||||||
|
key={item.id}
|
||||||
|
control={form.control}
|
||||||
|
name="contentType"
|
||||||
|
render={({ field }) => {
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
key={item.id}
|
||||||
|
className="flex flex-row items-start"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
checked={field.value?.includes(item.id)}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
return checked
|
||||||
|
? field.onChange([
|
||||||
|
...field.value,
|
||||||
|
item.id,
|
||||||
|
])
|
||||||
|
: field.onChange(
|
||||||
|
field.value?.filter(
|
||||||
|
(value) => value !== item.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
{item.name}
|
||||||
|
</FormLabel>
|
||||||
|
</div>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="selectedUser"
|
||||||
|
render={() => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Target Publish</FormLabel>
|
||||||
|
|
||||||
|
<div className="flex flex-row items-center gap-2">
|
||||||
|
<div className="flex gap-3 items-center">
|
||||||
|
<Checkbox
|
||||||
|
id="all"
|
||||||
|
checked={isAllUserChecked}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
if (checked) {
|
||||||
|
form.setValue(
|
||||||
|
"selectedUser",
|
||||||
|
userList.map((item) => item.id)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
form.setValue("selectedUser", []);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="all" className="text-sm">
|
||||||
|
Semua
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{userList.map((item) => (
|
||||||
|
<FormField
|
||||||
|
key={item.id}
|
||||||
|
control={form.control}
|
||||||
|
name="selectedUser"
|
||||||
|
render={({ field }) => {
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
key={item.id}
|
||||||
|
className="flex flex-row items-start "
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
checked={field.value?.includes(item.id)}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
return checked
|
||||||
|
? field.onChange([
|
||||||
|
...field.value,
|
||||||
|
item.id,
|
||||||
|
])
|
||||||
|
: field.onChange(
|
||||||
|
field.value?.filter(
|
||||||
|
(value) => value !== item.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
{item.name}
|
||||||
|
</FormLabel>
|
||||||
|
</div>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="publishTo"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="space-y-3">
|
||||||
|
<FormLabel>Wilayah Publish</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
value={field.value}
|
||||||
|
className="flex flex-row gap-2"
|
||||||
|
>
|
||||||
|
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="national" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">Nasional</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="international" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
Internasional
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="title"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nama Kategori</FormLabel>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Nama Kategori"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="file"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nama Kategori</FormLabel>
|
||||||
|
{!field.value && (
|
||||||
|
<div className="flex items-center justify-center w-full">
|
||||||
|
<label
|
||||||
|
htmlFor="dropzone-file"
|
||||||
|
className="flex flex-col items-center justify-center w-full h-28 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-gray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center justify-center pt-5 pb-6">
|
||||||
|
<p className="mb-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
<span className="font-semibold">Unggah Gambar</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
id="dropzone-file"
|
||||||
|
type="file"
|
||||||
|
className="hidden"
|
||||||
|
onChange={(e) => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (file) {
|
||||||
|
field.onChange(file);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{field.value && (
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<img
|
||||||
|
src={URL.createObjectURL(field.value)}
|
||||||
|
className="w-[30%]"
|
||||||
|
alt="thumbnail"
|
||||||
|
/>
|
||||||
|
<a onClick={() => form.setValue("file", undefined)}>
|
||||||
|
<Icon icon="fa-solid:times" color="red" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="description"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Deskripsi</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Deskripsi"
|
||||||
|
// className="resize-none"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
size="md"
|
||||||
|
className="text-xs"
|
||||||
|
>
|
||||||
|
Tambah Kategori
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,443 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { useRouter } from "@/i18n/routing";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { getUserRoles, postCategory } from "@/service/settings/settings";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { close, error, loading } from "@/config/swal";
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
title: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
description: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
contentType: z
|
||||||
|
.array(z.string())
|
||||||
|
.refine((value) => value.some((item) => item), {
|
||||||
|
message: "Required",
|
||||||
|
}),
|
||||||
|
selectedUser: z
|
||||||
|
.array(z.string())
|
||||||
|
.refine((value) => value.some((item) => item), {
|
||||||
|
message: "Required",
|
||||||
|
}),
|
||||||
|
publishTo: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
file: z
|
||||||
|
.instanceof(File, {
|
||||||
|
message: "Invalid file format",
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
.refine((file) => file && file.size > 0, {
|
||||||
|
message: "File is required",
|
||||||
|
}),
|
||||||
|
id: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const listContent = [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
name: "Foto",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
name: "Audio Visual",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3",
|
||||||
|
name: "Teks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "4",
|
||||||
|
name: "Audio",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function EditCategoryModal(props: { data: any }) {
|
||||||
|
const { data } = props;
|
||||||
|
const router = useRouter();
|
||||||
|
const [openModal, setOpenModal] = useState(false);
|
||||||
|
const [userList, setUserList] = useState<
|
||||||
|
{ id: string; name: string; isInternal: boolean }[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: { contentType: [], selectedUser: [], publishTo: "national" },
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
form.setValue("id", String(data?.id));
|
||||||
|
form.setValue("title", String(data?.name));
|
||||||
|
form.setValue("description", String(data?.description));
|
||||||
|
form.setValue("contentType", data?.mediaTypes.split(","));
|
||||||
|
form.setValue("selectedUser", data.publishedFor.split(","));
|
||||||
|
form.setValue("publishTo", data?.isInt ? "international" : "national");
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
const contentType = form.watch("contentType");
|
||||||
|
|
||||||
|
const isAllContentChecked = listContent.every((item) =>
|
||||||
|
contentType?.includes(item.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
const users = form.watch("selectedUser");
|
||||||
|
|
||||||
|
const isAllUserChecked = userList.every((item) => users?.includes(item.id));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getRoles();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
async function getRoles() {
|
||||||
|
const response = await getUserRoles();
|
||||||
|
let dataRoles = response.data.data;
|
||||||
|
for (let i = 0; i < dataRoles.length; i++) {
|
||||||
|
dataRoles[i].id = String(dataRoles[i].id);
|
||||||
|
}
|
||||||
|
setUserList(dataRoles);
|
||||||
|
console.log("dasda", dataRoles);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||||
|
if (data.file instanceof Blob) {
|
||||||
|
const formMedia = new FormData();
|
||||||
|
|
||||||
|
loading();
|
||||||
|
formMedia.append("id", data.id);
|
||||||
|
formMedia.append("name", data.title);
|
||||||
|
formMedia.append("description", data.description);
|
||||||
|
formMedia.append("mediaTypes", data.contentType.join(","));
|
||||||
|
formMedia.append("file", data.file);
|
||||||
|
formMedia.append("publishedFor", data.selectedUser.sort().join(","));
|
||||||
|
formMedia.append(
|
||||||
|
"isInt",
|
||||||
|
data.publishTo === "national" ? "false" : "true"
|
||||||
|
);
|
||||||
|
console.log(formMedia);
|
||||||
|
const response = await postCategory(formMedia);
|
||||||
|
close();
|
||||||
|
if (response?.error == true) {
|
||||||
|
error(response.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
router.push("/admin/settings/category?dataChange=true");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger>
|
||||||
|
<a className="hover:underline">Edit Kategori</a>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent size="md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Edit Kategori</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="space-y-3 bg-white rounded-sm"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="contentType"
|
||||||
|
render={() => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Tipe Konten</FormLabel>
|
||||||
|
|
||||||
|
<div className="flex flex-row items-center gap-2">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Checkbox
|
||||||
|
id="all"
|
||||||
|
checked={isAllContentChecked}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
if (checked) {
|
||||||
|
form.setValue(
|
||||||
|
"contentType",
|
||||||
|
listContent.map((item) => item.id)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
form.setValue("contentType", []);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="all" className="text-sm">
|
||||||
|
Semua
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{listContent.map((item) => (
|
||||||
|
<FormField
|
||||||
|
key={item.id}
|
||||||
|
control={form.control}
|
||||||
|
name="contentType"
|
||||||
|
render={({ field }) => {
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
key={item.id}
|
||||||
|
className="flex flex-row items-start"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
checked={field.value?.includes(item.id)}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
return checked
|
||||||
|
? field.onChange([
|
||||||
|
...field.value,
|
||||||
|
item.id,
|
||||||
|
])
|
||||||
|
: field.onChange(
|
||||||
|
field.value?.filter(
|
||||||
|
(value) => value !== item.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
{item.name}
|
||||||
|
</FormLabel>
|
||||||
|
</div>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="selectedUser"
|
||||||
|
render={() => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Target Publish</FormLabel>
|
||||||
|
|
||||||
|
<div className="flex flex-row items-center gap-2">
|
||||||
|
<div className="flex gap-3 items-center">
|
||||||
|
<Checkbox
|
||||||
|
id="all"
|
||||||
|
checked={isAllUserChecked}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
if (checked) {
|
||||||
|
form.setValue(
|
||||||
|
"selectedUser",
|
||||||
|
userList.map((item) => item.id)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
form.setValue("selectedUser", []);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="all" className="text-sm">
|
||||||
|
Semua
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{userList.map((item) => (
|
||||||
|
<FormField
|
||||||
|
key={item.id}
|
||||||
|
control={form.control}
|
||||||
|
name="selectedUser"
|
||||||
|
render={({ field }) => {
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
key={item.id}
|
||||||
|
className="flex flex-row items-start "
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
checked={field.value?.includes(item.id)}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
return checked
|
||||||
|
? field.onChange([
|
||||||
|
...field.value,
|
||||||
|
item.id,
|
||||||
|
])
|
||||||
|
: field.onChange(
|
||||||
|
field.value?.filter(
|
||||||
|
(value) => value !== item.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
{item.name}
|
||||||
|
</FormLabel>
|
||||||
|
</div>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="publishTo"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="space-y-3">
|
||||||
|
<FormLabel>Wilayah Publish</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
value={field.value}
|
||||||
|
className="flex flex-row gap-2"
|
||||||
|
>
|
||||||
|
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="national" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">Nasional</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="international" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
Internasional
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="title"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nama Kategori</FormLabel>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan Nama Kategori"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="file"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nama Kategori</FormLabel>
|
||||||
|
{!field.value && (
|
||||||
|
<div className="flex items-center justify-center w-full">
|
||||||
|
<label
|
||||||
|
htmlFor="dropzone-file"
|
||||||
|
className="flex flex-col items-center justify-center w-full h-28 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-gray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center justify-center pt-5 pb-6">
|
||||||
|
<p className="mb-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
<span className="font-semibold">Unggah Gambar</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
id="dropzone-file"
|
||||||
|
type="file"
|
||||||
|
className="hidden"
|
||||||
|
onChange={(e) => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (file) {
|
||||||
|
field.onChange(file);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{field.value && (
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<img
|
||||||
|
src={URL.createObjectURL(field.value)}
|
||||||
|
className="w-[30%]"
|
||||||
|
alt="thumbnail"
|
||||||
|
/>
|
||||||
|
<a onClick={() => form.setValue("file", undefined)}>
|
||||||
|
<Icon icon="fa-solid:times" color="red" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="description"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Deskripsi</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Deskripsi"
|
||||||
|
// className="resize-none"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
size="md"
|
||||||
|
className="text-xs"
|
||||||
|
>
|
||||||
|
Edit Kategori
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
"use client";
|
||||||
|
import { Switch } from "@/components/ui/switch";
|
||||||
|
import { error } from "@/config/swal";
|
||||||
|
import { useRouter } from "@/i18n/routing";
|
||||||
|
import { publishUnpublishCategory } from "@/service/settings/settings";
|
||||||
|
|
||||||
|
export default function StatusToogle(props: {
|
||||||
|
id: number;
|
||||||
|
initValue: boolean;
|
||||||
|
}) {
|
||||||
|
const { id, initValue } = props;
|
||||||
|
const router = useRouter();
|
||||||
|
const publishCategory = async (id: number, status: string) => {
|
||||||
|
const response = await publishUnpublishCategory(id, status);
|
||||||
|
console.log(response);
|
||||||
|
if (response?.error) {
|
||||||
|
error(response.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
router.push("/admin/settings/category?dataChange=true");
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Switch
|
||||||
|
id={String(id)}
|
||||||
|
checked={initValue}
|
||||||
|
onCheckedChange={(e) => publishCategory(id, String(e))}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,184 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import columns from "./column";
|
||||||
|
|
||||||
|
import { listEnableCategory } from "@/service/content/content";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { close, loading } from "@/config/swal";
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import { NewCampaignIcon } from "@/components/icon";
|
||||||
|
import { getCategories } from "@/service/settings/settings";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import CreateCategoryModal from "./create";
|
||||||
|
|
||||||
|
const AdminCategoryTable = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const dataChange = searchParams?.get("dataChange");
|
||||||
|
const [openModal, setOpenModal] = React.useState(false);
|
||||||
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 50,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (dataChange) {
|
||||||
|
router.push("/admin/settings/category");
|
||||||
|
}
|
||||||
|
fetchData();
|
||||||
|
}, [dataChange]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get("page");
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [page]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
loading();
|
||||||
|
const response = await getCategories();
|
||||||
|
const data = response.data?.data;
|
||||||
|
const contentData = data?.content;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * 50 + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(data?.totalElements);
|
||||||
|
setTotalPage(1);
|
||||||
|
close();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
|
<div className="flex justify-between mb-10 items-center">
|
||||||
|
<p className="text-xl font-medium text-default-900">Kategori</p>
|
||||||
|
<CreateCategoryModal />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Table className="overflow-hidden">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
{/* <TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/> */}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AdminCategoryTable;
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
"use client";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import AdminCategoryTable from "./component/table";
|
||||||
|
|
||||||
|
export default function AdminCategory() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<AdminCategoryTable />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
|
||||||
|
export default function TagCategory() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -7,11 +7,8 @@ import DashCodeHeader from "@/components/partials/header";
|
||||||
import MountedProvider from "@/providers/mounted.provider";
|
import MountedProvider from "@/providers/mounted.provider";
|
||||||
|
|
||||||
const layout = async ({ children }: { children: React.ReactNode }) => {
|
const layout = async ({ children }: { children: React.ReactNode }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MountedProvider
|
<MountedProvider isProtected={true}>
|
||||||
isProtected={true}
|
|
||||||
>
|
|
||||||
<LayoutProvider>
|
<LayoutProvider>
|
||||||
<ThemeCustomize />
|
<ThemeCustomize />
|
||||||
<DashCodeHeader />
|
<DashCodeHeader />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,293 @@
|
||||||
|
"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 { useRouter } from "@/i18n/routing";
|
||||||
|
import JoditEditor from "jodit-react";
|
||||||
|
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";
|
||||||
|
|
||||||
|
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",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
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([]);
|
||||||
|
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;
|
||||||
|
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='${
|
||||||
|
details?.pageUrl
|
||||||
|
}'>${details?.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
|
||||||
|
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} />
|
||||||
|
) : (
|
||||||
|
<JoditEditor
|
||||||
|
ref={editor}
|
||||||
|
value={field.value}
|
||||||
|
className="dark:text-black"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<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>Email 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">
|
||||||
|
<Button type="button" color="success">
|
||||||
|
Menu "Sent"
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={() => router.push("/admin/broadcast")}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
Okay
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ import {
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { loading } from "@/config/swal";
|
import { close, loading } from "@/config/swal";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import { error } from "@/lib/swal";
|
import { error } from "@/lib/swal";
|
||||||
|
|
@ -122,6 +122,7 @@ export default function FormImageDetail() {
|
||||||
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
|
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
|
||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
const [main, setMain] = useState<any>([]);
|
const [main, setMain] = useState<any>([]);
|
||||||
|
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
|
||||||
const [detailThumb, setDetailThumb] = useState<any>([]);
|
const [detailThumb, setDetailThumb] = useState<any>([]);
|
||||||
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
||||||
|
|
||||||
|
|
@ -202,6 +203,14 @@ export default function FormImageDetail() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setupPlacementCheck = (length: number) => {
|
||||||
|
const temp = [];
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
temp.push([]);
|
||||||
|
}
|
||||||
|
setFilePlacements(temp);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function initState() {
|
async function initState() {
|
||||||
if (id) {
|
if (id) {
|
||||||
|
|
@ -216,6 +225,7 @@ export default function FormImageDetail() {
|
||||||
names: details?.files[0]?.fileName,
|
names: details?.files[0]?.fileName,
|
||||||
format: details?.files[0]?.format,
|
format: details?.files[0]?.format,
|
||||||
});
|
});
|
||||||
|
setupPlacementCheck(details?.files?.length);
|
||||||
|
|
||||||
if (details.publishedForObject) {
|
if (details.publishedForObject) {
|
||||||
const publisherIds = details.publishedForObject.map(
|
const publisherIds = details.publishedForObject.map(
|
||||||
|
|
@ -245,9 +255,16 @@ export default function FormImageDetail() {
|
||||||
}, [refresh, setValue]);
|
}, [refresh, setValue]);
|
||||||
|
|
||||||
const actionApproval = (e: string) => {
|
const actionApproval = (e: string) => {
|
||||||
|
const temp = [];
|
||||||
|
for (const element of detail.files) {
|
||||||
|
temp.push([]);
|
||||||
|
}
|
||||||
|
setFilePlacements(temp);
|
||||||
setStatus(e);
|
setStatus(e);
|
||||||
setModalOpen(true);
|
setFiles(detail.files);
|
||||||
|
|
||||||
setDescription("");
|
setDescription("");
|
||||||
|
setModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
|
|
@ -256,31 +273,44 @@ export default function FormImageDetail() {
|
||||||
Number(status) == 2 ||
|
Number(status) == 2 ||
|
||||||
Number(status) == 4
|
Number(status) == 4
|
||||||
) {
|
) {
|
||||||
MySwal.fire({
|
save();
|
||||||
title: "Simpan Approval",
|
|
||||||
text: "",
|
// MySwal.fire({
|
||||||
icon: "warning",
|
// title: "Simpan Approval",
|
||||||
showCancelButton: true,
|
// text: "",
|
||||||
cancelButtonColor: "#d33",
|
// icon: "warning",
|
||||||
confirmButtonColor: "#3085d6",
|
// showCancelButton: true,
|
||||||
confirmButtonText: "Simpan",
|
// cancelButtonColor: "#d33",
|
||||||
}).then((result) => {
|
// confirmButtonColor: "#3085d6",
|
||||||
if (result.isConfirmed) {
|
// confirmButtonText: "Simpan",
|
||||||
save();
|
// }).then((result) => {
|
||||||
}
|
// if (result.isConfirmed) {
|
||||||
});
|
// save();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getPlacement = () => {
|
||||||
|
console.log("getPlaa", filePlacements);
|
||||||
|
const temp = [];
|
||||||
|
for (let i = 0; i < filePlacements?.length; i++) {
|
||||||
|
if (filePlacements[i].length !== 0) {
|
||||||
|
const now = filePlacements[i].filter((a) => a !== "all");
|
||||||
|
const data = { mediaFileId: files[i].id, placement: now.join(",") };
|
||||||
|
temp.push(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return temp;
|
||||||
|
};
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
const data = {
|
const data = {
|
||||||
mediaUploadId: id,
|
mediaUploadId: id,
|
||||||
statusId: status,
|
statusId: status,
|
||||||
message: description,
|
message: description,
|
||||||
files: [],
|
files: isMabesApprover ? getPlacement() : [],
|
||||||
// files: isMabesApprover ? getPlacement() : [],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
loading();
|
loading();
|
||||||
const response = await submitApproval(data);
|
const response = await submitApproval(data);
|
||||||
|
|
||||||
|
|
@ -293,6 +323,8 @@ export default function FormImageDetail() {
|
||||||
listFiles: rejectedFiles,
|
listFiles: rejectedFiles,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log("reject", dataReject);
|
||||||
|
|
||||||
const resReject = await rejectFiles(dataReject);
|
const resReject = await rejectFiles(dataReject);
|
||||||
|
|
||||||
if (resReject?.error) {
|
if (resReject?.error) {
|
||||||
|
|
@ -304,6 +336,38 @@ export default function FormImageDetail() {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const setupPlacement = (
|
||||||
|
index: number,
|
||||||
|
placement: string,
|
||||||
|
checked: boolean
|
||||||
|
) => {
|
||||||
|
let temp = [...filePlacements];
|
||||||
|
if (checked) {
|
||||||
|
if (placement === "all") {
|
||||||
|
temp[index] = ["all", "mabes", "polda", "international"];
|
||||||
|
} else {
|
||||||
|
const now = temp[index];
|
||||||
|
now.push(placement);
|
||||||
|
if (now.length === 3 && !now.includes("all")) {
|
||||||
|
now.push("all");
|
||||||
|
}
|
||||||
|
temp[index] = now;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (placement === "all") {
|
||||||
|
temp[index] = [];
|
||||||
|
} else {
|
||||||
|
const now = temp[index].filter((a) => a !== placement);
|
||||||
|
console.log("now", now);
|
||||||
|
temp[index] = now;
|
||||||
|
if (now.length === 3 && now.includes("all")) {
|
||||||
|
const newData = now.filter((b) => b !== "all");
|
||||||
|
temp[index] = newData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFilePlacements(temp);
|
||||||
|
};
|
||||||
|
|
||||||
function handleDeleteFileApproval(id: number) {
|
function handleDeleteFileApproval(id: number) {
|
||||||
const selectedFiles = files.filter((file) => file.id != id);
|
const selectedFiles = files.filter((file) => file.id != id);
|
||||||
|
|
@ -602,7 +666,7 @@ export default function FormImageDetail() {
|
||||||
<DialogTitle>Berikan Komentar</DialogTitle>
|
<DialogTitle>Berikan Komentar</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{status == "2"
|
{status == "2"
|
||||||
? files?.map((file) => (
|
? files?.map((file, index) => (
|
||||||
<div
|
<div
|
||||||
key={file.id}
|
key={file.id}
|
||||||
className="flex flex-row gap-2 items-center"
|
className="flex flex-row gap-2 items-center"
|
||||||
|
|
@ -611,13 +675,26 @@ export default function FormImageDetail() {
|
||||||
<div className="flex flex-col gap-2 w-full">
|
<div className="flex flex-col gap-2 w-full">
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
{file.fileName}
|
{file.fileName}
|
||||||
<a>
|
<a
|
||||||
|
onClick={() =>
|
||||||
|
handleDeleteFileApproval(file.id)
|
||||||
|
}
|
||||||
|
>
|
||||||
<Icon icon="humbleicons:times" color="red" />
|
<Icon icon="humbleicons:times" color="red" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row gap-2">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox id="terms" />
|
<Checkbox
|
||||||
|
id="terms"
|
||||||
|
value="all"
|
||||||
|
checked={filePlacements[index]?.includes(
|
||||||
|
"all"
|
||||||
|
)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
setupPlacement(index, "all", Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="terms"
|
htmlFor="terms"
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
|
@ -626,7 +703,15 @@ export default function FormImageDetail() {
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox id="terms" />
|
<Checkbox
|
||||||
|
id="terms"
|
||||||
|
checked={filePlacements[index]?.includes(
|
||||||
|
"mabes"
|
||||||
|
)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
setupPlacement(index, "mabes", Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="terms"
|
htmlFor="terms"
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
|
@ -635,7 +720,15 @@ export default function FormImageDetail() {
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox id="terms" />
|
<Checkbox
|
||||||
|
id="terms"
|
||||||
|
checked={filePlacements[index]?.includes(
|
||||||
|
"polda"
|
||||||
|
)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
setupPlacement(index, "polda", Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="terms"
|
htmlFor="terms"
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
|
@ -645,7 +738,19 @@ export default function FormImageDetail() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox id="terms" />
|
<Checkbox
|
||||||
|
id="terms"
|
||||||
|
checked={filePlacements[index]?.includes(
|
||||||
|
"international"
|
||||||
|
)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
setupPlacement(
|
||||||
|
index,
|
||||||
|
"international",
|
||||||
|
Boolean(e)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="terms"
|
htmlFor="terms"
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
|
@ -736,14 +841,16 @@ export default function FormImageDetail() {
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={submitApprovalSuccesss}
|
onClick={() => submit()}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
color="destructive"
|
color="destructive"
|
||||||
onClick={() => setModalOpen(false)}
|
onClick={() => {
|
||||||
|
setModalOpen(false);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -128,3 +128,37 @@ export const XIcon = ({
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const NewCampaignIcon = ({
|
||||||
|
size,
|
||||||
|
height = 24,
|
||||||
|
width = 24,
|
||||||
|
fill = "currentColor",
|
||||||
|
...props
|
||||||
|
}: IconSvgProps) => (
|
||||||
|
<svg
|
||||||
|
height={size || height}
|
||||||
|
width={size || width}
|
||||||
|
viewBox="0 0 18 18"
|
||||||
|
fill={fill}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<g clipPath="url(#clip0_4_200)">
|
||||||
|
<path
|
||||||
|
d="M12 2.25C12.1989 2.25 12.3897 2.32902 12.5303 2.46967C12.671 2.61032 12.75 2.80109 12.75 3V3.75H14.25C14.6284 3.74988 14.9929 3.8928 15.2704 4.15012C15.5479 4.40744 15.7179 4.76013 15.7463 5.1375L15.75 5.25V14.25C15.7501 14.6284 15.6072 14.9929 15.3499 15.2704C15.0926 15.5479 14.7399 15.7179 14.3625 15.7463L14.25 15.75H3.75C3.37157 15.7501 3.00708 15.6072 2.72959 15.3499C2.4521 15.0926 2.28213 14.7399 2.25375 14.3625L2.25 14.25V5.25C2.24988 4.87157 2.3928 4.50708 2.65012 4.22959C2.90744 3.9521 3.26013 3.78213 3.6375 3.75375L3.75 3.75H5.25V3C5.25 2.80109 5.32902 2.61032 5.46967 2.46967C5.61032 2.32902 5.80109 2.25 6 2.25C6.19891 2.25 6.38968 2.32902 6.53033 2.46967C6.67098 2.61032 6.75 2.80109 6.75 3V3.75H11.25V3C11.25 2.80109 11.329 2.61032 11.4697 2.46967C11.6103 2.32902 11.8011 2.25 12 2.25ZM11.118 7.03425L7.9365 10.2158L6.87525 9.15525C6.7338 9.01863 6.54435 8.94304 6.3477 8.94474C6.15105 8.94645 5.96294 9.02533 5.82389 9.16439C5.68483 9.30344 5.60595 9.49155 5.60424 9.6882C5.60254 9.88485 5.67813 10.0743 5.81475 10.2158L7.40025 11.802C7.4706 11.8724 7.55414 11.9283 7.64608 11.9664C7.73803 12.0045 7.83659 12.0241 7.93612 12.0241C8.03566 12.0241 8.13422 12.0045 8.22617 11.9664C8.31811 11.9283 8.40165 11.8724 8.472 11.802L12.1785 8.09475C12.2501 8.02557 12.3073 7.94281 12.3466 7.8513C12.3859 7.7598 12.4066 7.66139 12.4074 7.5618C12.4083 7.46222 12.3893 7.36346 12.3516 7.27129C12.3139 7.17911 12.2582 7.09537 12.1878 7.02495C12.1174 6.95453 12.0336 6.89884 11.9415 6.86113C11.8493 6.82342 11.7505 6.80445 11.6509 6.80531C11.5514 6.80618 11.4529 6.82687 11.3614 6.86617C11.2699 6.90548 11.1872 6.96262 11.118 7.03425Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<rect width="10" height="10" transform="translate(4 5)" fill="white" />
|
||||||
|
<path
|
||||||
|
d="M13.2857 10.7143H9.71429V14.2857C9.71429 14.6786 9.39286 15 9 15C8.60714 15 8.28571 14.6786 8.28571 14.2857V10.7143H4.71429C4.32143 10.7143 4 10.3929 4 10C4 9.60714 4.32143 9.28571 4.71429 9.28571H8.28571V5.71429C8.28571 5.32143 8.60714 5 9 5C9.39286 5 9.71429 5.32143 9.71429 5.71429V9.28571H13.2857C13.6786 9.28571 14 9.60714 14 10C14 10.3929 13.6786 10.7143 13.2857 10.7143Z"
|
||||||
|
fill="#0d6efd"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_4_200">
|
||||||
|
<rect width="18" height="18" fill="white" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
|
||||||
54
lib/menus.ts
54
lib/menus.ts
|
|
@ -2560,7 +2560,7 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
||||||
submenus: [
|
submenus: [
|
||||||
{
|
{
|
||||||
href: "/admin/media-tracking/media-online",
|
href: "/admin/media-tracking/media-online",
|
||||||
label: "Media Onlinne",
|
label: "Media Online",
|
||||||
active: pathname === "/media-tracking/media-online",
|
active: pathname === "/media-tracking/media-online",
|
||||||
icon: "heroicons:arrow-trending-up",
|
icon: "heroicons:arrow-trending-up",
|
||||||
children: [],
|
children: [],
|
||||||
|
|
@ -2656,7 +2656,57 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
||||||
label: t("settings"),
|
label: t("settings"),
|
||||||
active: pathname.includes("/settinng"),
|
active: pathname.includes("/settinng"),
|
||||||
icon: "material-symbols:settings",
|
icon: "material-symbols:settings",
|
||||||
submenus: [],
|
submenus: [
|
||||||
|
{
|
||||||
|
href: "/admin/settings/category",
|
||||||
|
label: t("category"),
|
||||||
|
active: pathname === "/admin/settings/category",
|
||||||
|
icon: "heroicons:arrow-trending-up",
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "/admin/settings/tag",
|
||||||
|
label: "Tag",
|
||||||
|
active: pathname === "/admin/settings/tag",
|
||||||
|
icon: "heroicons:arrow-trending-up",
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "/admin/settings/banner",
|
||||||
|
label: "Banner",
|
||||||
|
active: pathname === "/admin/settings/banner",
|
||||||
|
icon: "heroicons:arrow-trending-up",
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "/admin/settings/feedback",
|
||||||
|
label: "Feedback",
|
||||||
|
active: pathname === "/admin/settings/feedback",
|
||||||
|
icon: "heroicons:arrow-trending-up",
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "/admin/settings/faq",
|
||||||
|
label: "FAQ",
|
||||||
|
active: pathname === "/admin/settings/faq",
|
||||||
|
icon: "heroicons:arrow-trending-up",
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "https://nat-mediahub.polri.go.id/",
|
||||||
|
label: "Mediahub 2022",
|
||||||
|
active: pathname === "/admin/settings/mediahub-2022",
|
||||||
|
icon: "heroicons:arrow-trending-up",
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "/admin/settings/privacy",
|
||||||
|
label: t("privacy"),
|
||||||
|
active: pathname === "/admin/settings/privacy",
|
||||||
|
icon: "heroicons:arrow-trending-up",
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -298,7 +298,9 @@
|
||||||
"performance-polda": "Performance Polda",
|
"performance-polda": "Performance Polda",
|
||||||
"analysis": "Analysis",
|
"analysis": "Analysis",
|
||||||
"management-content": "Content Management ",
|
"management-content": "Content Management ",
|
||||||
"add-experts": "Add Experts"
|
"add-experts": "Add Experts",
|
||||||
|
"category": "Category",
|
||||||
|
"privacy": "Privacy Policy"
|
||||||
},
|
},
|
||||||
"Changelog": {
|
"Changelog": {
|
||||||
"version": "Version's",
|
"version": "Version's",
|
||||||
|
|
|
||||||
|
|
@ -297,8 +297,10 @@
|
||||||
"colors": "Colors",
|
"colors": "Colors",
|
||||||
"performance-polda": "Performa Polda",
|
"performance-polda": "Performa Polda",
|
||||||
"analysis": "Analisa",
|
"analysis": "Analisa",
|
||||||
"content-management": "Manajemen Konten",
|
"management-content": "Manajemen Konten",
|
||||||
"add-experts": "Tambah Tenaga Ahli"
|
"add-experts": "Tambah Tenaga Ahli",
|
||||||
|
"category": "Kategori",
|
||||||
|
"privacy": "Kebijakan Privacy"
|
||||||
},
|
},
|
||||||
"Changelog": {
|
"Changelog": {
|
||||||
"version": "Version's",
|
"version": "Version's",
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,6 @@
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
||||||
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
|
|
@ -865,7 +864,6 @@
|
||||||
"version": "8.0.2",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||||
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"string-width": "^5.1.2",
|
"string-width": "^5.1.2",
|
||||||
"string-width-cjs": "npm:string-width@^4.2.0",
|
"string-width-cjs": "npm:string-width@^4.2.0",
|
||||||
|
|
@ -882,7 +880,6 @@
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||||
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
@ -894,7 +891,6 @@
|
||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^6.0.1"
|
"ansi-regex": "^6.0.1"
|
||||||
},
|
},
|
||||||
|
|
@ -1230,7 +1226,6 @@
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nodelib/fs.stat": "2.0.5",
|
"@nodelib/fs.stat": "2.0.5",
|
||||||
"run-parallel": "^1.1.9"
|
"run-parallel": "^1.1.9"
|
||||||
|
|
@ -1243,7 +1238,6 @@
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||||
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
|
|
@ -1252,7 +1246,6 @@
|
||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||||
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nodelib/fs.scandir": "2.1.5",
|
"@nodelib/fs.scandir": "2.1.5",
|
||||||
"fastq": "^1.6.0"
|
"fastq": "^1.6.0"
|
||||||
|
|
@ -1274,7 +1267,6 @@
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
|
|
@ -3473,7 +3465,6 @@
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
|
|
@ -3487,7 +3478,6 @@
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-convert": "^2.0.1"
|
"color-convert": "^2.0.1"
|
||||||
},
|
},
|
||||||
|
|
@ -3501,14 +3491,12 @@
|
||||||
"node_modules/any-promise": {
|
"node_modules/any-promise": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
||||||
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
|
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/anymatch": {
|
"node_modules/anymatch": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
"picomatch": "^2.0.4"
|
"picomatch": "^2.0.4"
|
||||||
|
|
@ -3553,8 +3541,7 @@
|
||||||
"node_modules/arg": {
|
"node_modules/arg": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
|
|
@ -3845,14 +3832,12 @@
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
},
|
},
|
||||||
|
|
@ -3874,7 +3859,6 @@
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fill-range": "^7.1.1"
|
"fill-range": "^7.1.1"
|
||||||
},
|
},
|
||||||
|
|
@ -3939,7 +3923,6 @@
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||||
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
|
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
|
|
@ -4039,7 +4022,6 @@
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"anymatch": "~3.1.2",
|
"anymatch": "~3.1.2",
|
||||||
"braces": "~3.0.2",
|
"braces": "~3.0.2",
|
||||||
|
|
@ -4063,7 +4045,6 @@
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-glob": "^4.0.1"
|
"is-glob": "^4.0.1"
|
||||||
},
|
},
|
||||||
|
|
@ -4257,7 +4238,6 @@
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"path-key": "^3.1.0",
|
"path-key": "^3.1.0",
|
||||||
"shebang-command": "^2.0.0",
|
"shebang-command": "^2.0.0",
|
||||||
|
|
@ -4281,7 +4261,6 @@
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"cssesc": "bin/cssesc"
|
"cssesc": "bin/cssesc"
|
||||||
},
|
},
|
||||||
|
|
@ -4962,8 +4941,7 @@
|
||||||
"node_modules/didyoumean": {
|
"node_modules/didyoumean": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||||
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
|
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/diff": {
|
"node_modules/diff": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
|
|
@ -4988,8 +4966,7 @@
|
||||||
"node_modules/dlv": {
|
"node_modules/dlv": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
||||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
|
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/doctrine": {
|
"node_modules/doctrine": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
|
|
@ -5090,8 +5067,7 @@
|
||||||
"node_modules/eastasianwidth": {
|
"node_modules/eastasianwidth": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/elkjs": {
|
"node_modules/elkjs": {
|
||||||
"version": "0.9.3",
|
"version": "0.9.3",
|
||||||
|
|
@ -5139,8 +5115,7 @@
|
||||||
"node_modules/emoji-regex": {
|
"node_modules/emoji-regex": {
|
||||||
"version": "9.2.2",
|
"version": "9.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/enhanced-resolve": {
|
"node_modules/enhanced-resolve": {
|
||||||
"version": "5.17.1",
|
"version": "5.17.1",
|
||||||
|
|
@ -5980,7 +5955,6 @@
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
|
||||||
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
|
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nodelib/fs.stat": "^2.0.2",
|
"@nodelib/fs.stat": "^2.0.2",
|
||||||
"@nodelib/fs.walk": "^1.2.3",
|
"@nodelib/fs.walk": "^1.2.3",
|
||||||
|
|
@ -5996,7 +5970,6 @@
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-glob": "^4.0.1"
|
"is-glob": "^4.0.1"
|
||||||
},
|
},
|
||||||
|
|
@ -6020,7 +5993,6 @@
|
||||||
"version": "1.17.1",
|
"version": "1.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
||||||
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
|
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"reusify": "^1.0.4"
|
"reusify": "^1.0.4"
|
||||||
}
|
}
|
||||||
|
|
@ -6064,7 +6036,6 @@
|
||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"to-regex-range": "^5.0.1"
|
"to-regex-range": "^5.0.1"
|
||||||
},
|
},
|
||||||
|
|
@ -6155,7 +6126,6 @@
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
|
||||||
"integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
|
"integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-spawn": "^7.0.0",
|
"cross-spawn": "^7.0.0",
|
||||||
"signal-exit": "^4.0.1"
|
"signal-exit": "^4.0.1"
|
||||||
|
|
@ -6366,7 +6336,6 @@
|
||||||
"version": "10.3.10",
|
"version": "10.3.10",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
|
||||||
"integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
|
"integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"foreground-child": "^3.1.0",
|
"foreground-child": "^3.1.0",
|
||||||
"jackspeak": "^2.3.5",
|
"jackspeak": "^2.3.5",
|
||||||
|
|
@ -6388,7 +6357,6 @@
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||||
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-glob": "^4.0.3"
|
"is-glob": "^4.0.3"
|
||||||
},
|
},
|
||||||
|
|
@ -6400,7 +6368,6 @@
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0"
|
"balanced-match": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -6409,7 +6376,6 @@
|
||||||
"version": "9.0.5",
|
"version": "9.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||||
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^2.0.1"
|
"brace-expansion": "^2.0.1"
|
||||||
},
|
},
|
||||||
|
|
@ -7377,7 +7343,6 @@
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"binary-extensions": "^2.0.0"
|
"binary-extensions": "^2.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -7509,7 +7474,6 @@
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
|
@ -7533,7 +7497,6 @@
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
|
|
@ -7557,7 +7520,6 @@
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-extglob": "^2.1.1"
|
"is-extglob": "^2.1.1"
|
||||||
},
|
},
|
||||||
|
|
@ -7602,7 +7564,6 @@
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
|
|
@ -7856,7 +7817,6 @@
|
||||||
"version": "2.3.6",
|
"version": "2.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
|
||||||
"integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
|
"integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@isaacs/cliui": "^8.0.2"
|
"@isaacs/cliui": "^8.0.2"
|
||||||
},
|
},
|
||||||
|
|
@ -7874,7 +7834,6 @@
|
||||||
"version": "1.21.6",
|
"version": "1.21.6",
|
||||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz",
|
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz",
|
||||||
"integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==",
|
"integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==",
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"jiti": "bin/jiti.js"
|
"jiti": "bin/jiti.js"
|
||||||
}
|
}
|
||||||
|
|
@ -8135,7 +8094,6 @@
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||||
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
|
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
},
|
},
|
||||||
|
|
@ -8298,8 +8256,7 @@
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "10.4.3",
|
"version": "10.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/lucide-react": {
|
"node_modules/lucide-react": {
|
||||||
"version": "0.390.0",
|
"version": "0.390.0",
|
||||||
|
|
@ -9013,7 +8970,6 @@
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
|
|
@ -9801,7 +9757,6 @@
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"braces": "^3.0.3",
|
"braces": "^3.0.3",
|
||||||
"picomatch": "^2.3.1"
|
"picomatch": "^2.3.1"
|
||||||
|
|
@ -9862,7 +9817,6 @@
|
||||||
"version": "7.1.2",
|
"version": "7.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
|
|
@ -9911,7 +9865,6 @@
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||||
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
|
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"any-promise": "^1.0.0",
|
"any-promise": "^1.0.0",
|
||||||
"object-assign": "^4.0.1",
|
"object-assign": "^4.0.1",
|
||||||
|
|
@ -10207,7 +10160,6 @@
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
|
@ -10254,7 +10206,6 @@
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
||||||
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
|
|
@ -10553,7 +10504,6 @@
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
|
|
@ -10567,7 +10517,6 @@
|
||||||
"version": "1.11.1",
|
"version": "1.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
||||||
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lru-cache": "^10.2.0",
|
"lru-cache": "^10.2.0",
|
||||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||||
|
|
@ -10606,7 +10555,6 @@
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
},
|
},
|
||||||
|
|
@ -10618,7 +10566,6 @@
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||||
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
|
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
|
@ -10627,7 +10574,6 @@
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
|
||||||
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
|
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
|
|
@ -10645,7 +10591,6 @@
|
||||||
"version": "8.4.49",
|
"version": "8.4.49",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
|
||||||
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
|
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
|
@ -10673,7 +10618,6 @@
|
||||||
"version": "15.1.0",
|
"version": "15.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
|
||||||
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
|
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"postcss-value-parser": "^4.0.0",
|
"postcss-value-parser": "^4.0.0",
|
||||||
"read-cache": "^1.0.0",
|
"read-cache": "^1.0.0",
|
||||||
|
|
@ -10690,7 +10634,6 @@
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
|
||||||
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
|
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"camelcase-css": "^2.0.1"
|
"camelcase-css": "^2.0.1"
|
||||||
},
|
},
|
||||||
|
|
@ -10709,7 +10652,6 @@
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
|
||||||
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
|
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
|
@ -10744,7 +10686,6 @@
|
||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz",
|
||||||
"integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==",
|
"integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==",
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"yaml": "bin.mjs"
|
"yaml": "bin.mjs"
|
||||||
},
|
},
|
||||||
|
|
@ -10756,7 +10697,6 @@
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
|
||||||
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
|
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
|
@ -10781,7 +10721,6 @@
|
||||||
"version": "6.1.2",
|
"version": "6.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
||||||
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssesc": "^3.0.0",
|
"cssesc": "^3.0.0",
|
||||||
"util-deprecate": "^1.0.2"
|
"util-deprecate": "^1.0.2"
|
||||||
|
|
@ -10793,8 +10732,7 @@
|
||||||
"node_modules/postcss-value-parser": {
|
"node_modules/postcss-value-parser": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/preact": {
|
"node_modules/preact": {
|
||||||
"version": "10.12.1",
|
"version": "10.12.1",
|
||||||
|
|
@ -10908,7 +10846,6 @@
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
|
@ -11500,7 +11437,6 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
|
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pify": "^2.3.0"
|
"pify": "^2.3.0"
|
||||||
}
|
}
|
||||||
|
|
@ -11509,7 +11445,6 @@
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"picomatch": "^2.2.1"
|
"picomatch": "^2.2.1"
|
||||||
},
|
},
|
||||||
|
|
@ -11951,7 +11886,6 @@
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||||
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"iojs": ">=1.0.0",
|
"iojs": ">=1.0.0",
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
|
|
@ -12024,7 +11958,6 @@
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||||
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
|
@ -12216,7 +12149,6 @@
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"shebang-regex": "^3.0.0"
|
"shebang-regex": "^3.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -12228,7 +12160,6 @@
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
|
|
@ -12265,7 +12196,6 @@
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
},
|
},
|
||||||
|
|
@ -12395,7 +12325,6 @@
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eastasianwidth": "^0.2.0",
|
"eastasianwidth": "^0.2.0",
|
||||||
"emoji-regex": "^9.2.2",
|
"emoji-regex": "^9.2.2",
|
||||||
|
|
@ -12413,7 +12342,6 @@
|
||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"emoji-regex": "^8.0.0",
|
"emoji-regex": "^8.0.0",
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
|
@ -12426,14 +12354,12 @@
|
||||||
"node_modules/string-width-cjs/node_modules/emoji-regex": {
|
"node_modules/string-width-cjs/node_modules/emoji-regex": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/string-width/node_modules/ansi-regex": {
|
"node_modules/string-width/node_modules/ansi-regex": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||||
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
@ -12445,7 +12371,6 @@
|
||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^6.0.1"
|
"ansi-regex": "^6.0.1"
|
||||||
},
|
},
|
||||||
|
|
@ -12581,7 +12506,6 @@
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1"
|
"ansi-regex": "^5.0.1"
|
||||||
},
|
},
|
||||||
|
|
@ -12594,7 +12518,6 @@
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1"
|
"ansi-regex": "^5.0.1"
|
||||||
},
|
},
|
||||||
|
|
@ -12699,7 +12622,6 @@
|
||||||
"version": "3.35.0",
|
"version": "3.35.0",
|
||||||
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
|
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
|
||||||
"integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
|
"integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/gen-mapping": "^0.3.2",
|
"@jridgewell/gen-mapping": "^0.3.2",
|
||||||
"commander": "^4.0.0",
|
"commander": "^4.0.0",
|
||||||
|
|
@ -12721,7 +12643,6 @@
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||||
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
|
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
|
|
@ -12895,7 +12816,6 @@
|
||||||
"version": "3.4.16",
|
"version": "3.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.16.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.16.tgz",
|
||||||
"integrity": "sha512-TI4Cyx7gDiZ6r44ewaJmt0o6BrMCT5aK5e0rmJ/G9Xq3w7CX/5VXl/zIPEJZFUK5VEqwByyhqNPycPlvcK4ZNw==",
|
"integrity": "sha512-TI4Cyx7gDiZ6r44ewaJmt0o6BrMCT5aK5e0rmJ/G9Xq3w7CX/5VXl/zIPEJZFUK5VEqwByyhqNPycPlvcK4ZNw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alloc/quick-lru": "^5.2.0",
|
"@alloc/quick-lru": "^5.2.0",
|
||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
|
|
@ -12955,7 +12875,6 @@
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||||
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
|
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"any-promise": "^1.0.0"
|
"any-promise": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -12964,7 +12883,6 @@
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
|
||||||
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
|
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"thenify": ">= 3.1.0 < 4"
|
"thenify": ">= 3.1.0 < 4"
|
||||||
},
|
},
|
||||||
|
|
@ -13077,7 +12995,6 @@
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-number": "^7.0.0"
|
"is-number": "^7.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -13145,8 +13062,7 @@
|
||||||
"node_modules/ts-interface-checker": {
|
"node_modules/ts-interface-checker": {
|
||||||
"version": "0.1.13",
|
"version": "0.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
||||||
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
|
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/tsconfig-paths": {
|
"node_modules/tsconfig-paths": {
|
||||||
"version": "3.15.0",
|
"version": "3.15.0",
|
||||||
|
|
@ -13661,8 +13577,7 @@
|
||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/uuid": {
|
"node_modules/uuid": {
|
||||||
"version": "9.0.1",
|
"version": "9.0.1",
|
||||||
|
|
@ -13915,7 +13830,6 @@
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"isexe": "^2.0.0"
|
"isexe": "^2.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -14022,7 +13936,6 @@
|
||||||
"version": "8.1.0",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-styles": "^6.1.0",
|
"ansi-styles": "^6.1.0",
|
||||||
"string-width": "^5.0.1",
|
"string-width": "^5.0.1",
|
||||||
|
|
@ -14040,7 +13953,6 @@
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-styles": "^4.0.0",
|
"ansi-styles": "^4.0.0",
|
||||||
"string-width": "^4.1.0",
|
"string-width": "^4.1.0",
|
||||||
|
|
@ -14056,14 +13968,12 @@
|
||||||
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
|
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
|
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
|
||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"emoji-regex": "^8.0.0",
|
"emoji-regex": "^8.0.0",
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
|
@ -14077,7 +13987,6 @@
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||||
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
@ -14089,7 +13998,6 @@
|
||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
@ -14101,7 +14009,6 @@
|
||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^6.0.1"
|
"ansi-regex": "^6.0.1"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
|
|
@ -0,0 +1,79 @@
|
||||||
|
import {
|
||||||
|
httpDeleteInterceptor,
|
||||||
|
httpGetInterceptor,
|
||||||
|
httpPostInterceptor,
|
||||||
|
} from "../http-config/http-interceptor-service";
|
||||||
|
|
||||||
|
export async function listDataMedia(
|
||||||
|
page: number,
|
||||||
|
limit: string,
|
||||||
|
search: string,
|
||||||
|
categoryFilter: string,
|
||||||
|
statusFilter: string
|
||||||
|
) {
|
||||||
|
const name = search || "";
|
||||||
|
const url = `media/list?isForAdmin=true&title=${name}&enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=1&categoryId=${categoryFilter}&statusId=${statusFilter}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMediaBlastCampaignPage(page: number) {
|
||||||
|
const url = `media/blast/campaign/list?enablePage=1&page=${page}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMediaBlastAccountPage(page: number, category: string) {
|
||||||
|
const url = `media/blast/account/list?enablePage=1&size=10&page=${page}&category=${category}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveMediaBlastCampaign(data: {
|
||||||
|
title: string;
|
||||||
|
sendTime: string;
|
||||||
|
status: string;
|
||||||
|
id?: string;
|
||||||
|
}) {
|
||||||
|
const url = `media/blast/campaign`;
|
||||||
|
return httpPostInterceptor(url, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMediaBlastCampaignById(id: string) {
|
||||||
|
const url = `media/blast/campaign?id=${id}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMediaBlastCampaignList() {
|
||||||
|
const url = `media/blast/campaign/list?enablePage=0&status=waiting`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function detailMediaSummary(id: string) {
|
||||||
|
const url = `media?id=${id}&isSummary=true`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveMediaBlastAccount(data: {
|
||||||
|
accountName: string;
|
||||||
|
accountType: string;
|
||||||
|
accountCategory: string;
|
||||||
|
emailAddress: string;
|
||||||
|
whatsappNumber: string;
|
||||||
|
id?: string;
|
||||||
|
}) {
|
||||||
|
const url = `media/blast/account`;
|
||||||
|
return httpPostInterceptor(url, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMediaBlastAccount(id: string) {
|
||||||
|
const url = `media/blast/account?id=${id}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteMediaBlastAccount(id: string) {
|
||||||
|
const url = `media/blast/account?id=${id}`;
|
||||||
|
return httpDeleteInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveMediaBlastBroadcast(data: any) {
|
||||||
|
const url = `media/blast/broadcast`;
|
||||||
|
return httpPostInterceptor(url, data);
|
||||||
|
}
|
||||||
|
|
@ -30,7 +30,7 @@ export async function httpGetInterceptor(pathUrl: any) {
|
||||||
|
|
||||||
export async function httpPostInterceptor(
|
export async function httpPostInterceptor(
|
||||||
pathUrl: any,
|
pathUrl: any,
|
||||||
data: any,
|
data?: any,
|
||||||
headers?: any
|
headers?: any
|
||||||
) {
|
) {
|
||||||
const response = await axiosInterceptorInstance
|
const response = await axiosInterceptorInstance
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import {
|
||||||
|
httpDeleteInterceptor,
|
||||||
|
httpGetInterceptor,
|
||||||
|
httpPostInterceptor,
|
||||||
|
} from "../http-config/http-interceptor-service";
|
||||||
|
|
||||||
|
export async function getCategories() {
|
||||||
|
const url = "media/categories/list?enablePage=1&size=50&sort=desc&sortBy=id";
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function publishUnpublishCategory(id: number, status: string) {
|
||||||
|
const url = `media/categories/publish?id=${id}&status=${status}`;
|
||||||
|
return httpPostInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteCategory(id: number) {
|
||||||
|
const url = `media/categories/${id}`;
|
||||||
|
return httpDeleteInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserRoles() {
|
||||||
|
const url = "users/roles";
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function postCategory(data: any) {
|
||||||
|
const url = "media/categories";
|
||||||
|
return httpPostInterceptor(url, data);
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,11 @@ export const generateLocalizedPath = (href: string, locale: string): string => {
|
||||||
return `/${locale}${href}`;
|
return `/${locale}${href}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function textEllipsis(str: string, maxLength: number, { side = "end", ellipsis = "..." } = {}) {
|
export function textEllipsis(
|
||||||
|
str: string,
|
||||||
|
maxLength: number,
|
||||||
|
{ side = "end", ellipsis = "..." } = {}
|
||||||
|
) {
|
||||||
if (str !== undefined && str?.length > maxLength) {
|
if (str !== undefined && str?.length > maxLength) {
|
||||||
switch (side) {
|
switch (side) {
|
||||||
case "start":
|
case "start":
|
||||||
|
|
@ -67,7 +71,10 @@ export function getOnlyMonthAndYear(d: Date) {
|
||||||
|
|
||||||
export function getPublicLocaleTimestamp(d: any) {
|
export function getPublicLocaleTimestamp(d: any) {
|
||||||
const pad = (n: any, s = 2) => `${new Array(s).fill(0)}${n}`.slice(-s);
|
const pad = (n: any, s = 2) => `${new Array(s).fill(0)}${n}`.slice(-s);
|
||||||
return `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${pad(d.getFullYear(), 4)} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
return `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${pad(
|
||||||
|
d.getFullYear(),
|
||||||
|
4
|
||||||
|
)} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function capitalize(s: any) {
|
export function capitalize(s: any) {
|
||||||
|
|
@ -79,3 +86,17 @@ export function capitalize(s: any) {
|
||||||
return splitStr.join(" ");
|
return splitStr.join(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getLocaleTimestamp(d: Date): string {
|
||||||
|
const pad = (n: number, s: number = 2): string =>
|
||||||
|
`${new Array(s).fill(0)}${n}`.slice(-s);
|
||||||
|
|
||||||
|
return `${pad(d.getDate())}-${pad(d.getMonth() + 1)}-${pad(
|
||||||
|
d.getFullYear(),
|
||||||
|
4
|
||||||
|
)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLocaleTime(d: Date) {
|
||||||
|
const pad = (n: number, s = 2) => `${new Array(s).fill(0)}${n}`.slice(-s);
|
||||||
|
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue