feat:update

This commit is contained in:
Anang Yusman 2024-12-29 19:13:44 +08:00
commit 0f40db9365
51 changed files with 10456 additions and 1090 deletions

View File

@ -0,0 +1,90 @@
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 { 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";
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: "date",
header: "Tanggal Masuk",
cell: ({ row }) => <span>{row.getValue("date")}</span>,
},
{
accessorKey: "duration",
header: "Durasi",
cell: ({ row }) => <span>{row.getValue("duration")}</span>,
},
{
accessorKey: "crawling",
header: "Model Crawling",
cell: ({ row }) => <span>{row.getValue("crawling")}</span>,
},
{
accessorKey: "status",
header: "Status",
cell: ({ row }) => <span>{row.getValue("status")}</span>,
},
{
id: "actions",
accessorKey: "action",
header: "Aksi",
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">
Detail
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];
export default columns;

View File

@ -0,0 +1,180 @@
"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,
} from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
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 { getMediaTrackingMonitoring } from "@/service/media-tracking/media-tracking";
const MediaTrackingTable = () => {
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 [limit, setLimit] = React.useState(10);
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, limit]);
async function fetchData() {
try {
const response = await getMediaTrackingMonitoring(page, 10);
const data = response.data?.data;
const contentData = data?.content;
contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * limit + index + 1;
});
console.log("contentData : ", data);
setDataTable(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
} catch (error) {
console.error("Error fetching tasks:", error);
}
}
return (
<div className="w-full overflow-x-auto">
<Table className="overflow-hidden mt-3">
<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 MediaTrackingTable;

View File

@ -0,0 +1,11 @@
import SiteBreadcrumb from "@/components/site-breadcrumb";
import MediaTrackingTable from "./component/table";
export default function MediaTracking() {
return (
<div>
<SiteBreadcrumb />
<MediaTrackingTable />
</div>
);
}

View File

@ -60,11 +60,21 @@ const columns: ColumnDef<any>[] = [
<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={`/curator/task-plan/mediahub/detail/${row.original.id}`}
href={`/curator/task-plan/mediahub/create-daily/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={`/curator/task-plan/mediahub/create-daily/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 className="text-red-600">Delete</a>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);

View File

@ -0,0 +1,759 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { getUserLevelForAssignments } from "@/service/task";
import { list } from "postcss";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { close, error, loading } from "@/config/swal";
import { id } from "date-fns/locale";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
getWeeklyPlanList,
savePlanning,
} from "@/service/agenda-setting/agenda-setting";
import { getOnlyDate } from "@/utils/globals";
import { useParams } from "next/navigation";
import { getPlanningById } from "@/service/planning/planning";
const FormSchema = z.object({
date: z.date({
required_error: "Required",
}),
title: z.string({
required_error: "Required",
}),
detail: z.string({
required_error: "Required",
}),
output: z.array(z.string()).refine((value) => value.some((item) => item), {
message: "Required",
}),
unit: z.array(z.string()).refine((value) => value.some((item) => item), {
message: "Required",
}),
type: z.string({
required_error: "Required",
}),
parentId: z.string({
required_error: "Required",
}),
});
const items = [
{
id: "2",
label: "Audio Visual",
},
{
id: "1",
label: "Foto",
},
{
id: "4",
label: "Audio",
},
{
id: "3",
label: "Text",
},
];
const units = [
{
id: "1",
label: "Mabes Polri",
},
{
id: "2",
label: "Polda",
},
{
id: "3",
label: "Polres",
},
];
export default function DetailDaily() {
const id = useParams()?.id;
const MySwal = withReactContent(Swal);
const [listDest, setListDest] = useState<any>([]);
const router = useRouter();
const [weeklyList, setWeeklyList] = useState<any>();
const [selected, setSelected] = useState<{ [key: string]: boolean }>({});
const [selectAll, setSelectAll] = useState<{ [key: string]: boolean }>({});
useEffect(() => {
initFetch();
}, [id]);
async function initFetch() {
if (id != undefined) {
loading();
const res = await getPlanningById(id);
close();
if (res?.data?.data != undefined) {
const data = res?.data?.data;
console.log("data");
console.log("Data :", data);
form.setValue("title", data.title);
form.setValue("detail", data.description);
form.setValue("date", new Date(data.date));
form.setValue(
"output",
data.fileTypeOutput.split(",")?.length > 1
? data.fileTypeOutput.split(",")
: [data.fileTypeOutput]
);
form.setValue(
"unit",
data.assignedToLevel.split(",")?.length > 1
? data.assignedToLevel.split(",")
: [data.assignedToLevel]
);
form.setValue("type", String(data?.assignmentTypeId));
form.setValue("parentId", String(data?.parentId));
}
}
}
useEffect(() => {
getWeeklyPlanning();
}, []);
async function getWeeklyPlanning() {
const res = await getWeeklyPlanList(new Date().getDate(), 1);
if (res.data !== null) {
const rawUser = res.data?.data;
const optionArr = rawUser.map((option: any) => ({
id: option.id,
label: option.title,
value: String(option.id),
}));
setWeeklyList(optionArr);
}
}
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
unit: [],
output: [],
detail: "",
},
});
const editor = useRef(null);
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
console.log("data", data);
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 getSelectedString = () => {
return Object.keys(selected)
.filter((key) => selected[key])
.join(", ");
};
console.log("data", data, selected);
loading();
const reqData = {
planningTypeId: 1,
time: "1",
title: data.title,
assignmentTypeId: data.type, //string
description: data.detail,
assignedToLevel: unit?.join(","), //string
assignmentPurpose: getSelectedString(), //string
fileTypeOutput: data.output?.join(","), //string
status: "Open",
date: getOnlyDate(data.date),
// date:
// isPublish || isUpdate
// ? selectedDate?.length > 10
// ? data.date?.toISOString().slice(0, 10)
// : selectedDate
// : data.date?.toISOString().slice(0, 10),
parentId: Number(data.parentId), //number
assignmentMainTypeId: 1,
};
console.log("req =>", reqData);
const response = await savePlanning(reqData);
if (response.error) {
error(response.message);
return false;
}
close();
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/curator/task-plan/mediahub");
}
});
};
const output = form.watch("output");
const isAllChecked = items.every((item) => output?.includes(item.id));
const unit = form.watch("unit");
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
const handleAllCheckedChange = (checked: boolean | string) => {
if (checked) {
form.setValue(
"output",
items.map((item) => item.id)
);
} else {
form.setValue("output", []);
}
};
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
form.setValue(
"output",
checked ? [...output, id] : output.filter((value) => value !== id)
);
};
const handleAllUnitCheckedChange = (checked: boolean | string) => {
if (checked) {
form.setValue(
"unit",
units.map((item) => item.id)
);
} else {
form.setValue("unit", []);
}
};
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
if (checked) {
form.setValue("unit", [...unit, id]);
} else {
if (id == "2") {
const temp = [];
for (const element of unit) {
if (element == "1") {
temp.push("1");
}
}
form.setValue("unit", temp);
} else {
form.setValue(
"unit",
unit.filter((value) => value !== id)
);
}
}
};
useEffect(() => {
async function initState() {
const response = await getUserLevelForAssignments();
setListDest(response.data.data.list);
}
initState();
}, []);
const handleParentChange = (listId: string) => {
setSelected((prev) => ({
...prev,
[listId]: !prev[listId],
}));
};
const handleSelectAllPolres = (listId: string, isChecked: boolean) => {
setSelectAll((prev) => ({
...prev,
[listId]: isChecked,
}));
setSelected((prev) => {
const updatedState = { ...prev };
listDest
.find((list: any) => list.id === listId)
?.subDestination.forEach((subDes: any) => {
updatedState[`${listId}${subDes.id}`] = isChecked;
});
return updatedState;
});
};
const handleChildChange = (childId: string) => {
setSelected((prev) => ({
...prev,
[childId]: !prev[childId],
}));
};
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-col gap-4">
<div className="flex flex-col bg-white gap-2 p-6">
<p className="text-lg">Perencanaan MediaHub</p>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Judul Perencanaan</FormLabel>
<Input
value={field.value}
placeholder="Masukkan Judul Perencanaan"
onChange={field.onChange}
readOnly
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="output"
render={() => (
<FormItem>
<div className="mb-4">
<FormLabel className="text-sm">Output Tugas</FormLabel>
</div>
<div className="flex flex-row gap-3 items-center ">
<div className="flex items-center gap-3">
<Checkbox
id="all"
checked={isAllChecked}
onCheckedChange={(checked) =>
handleAllCheckedChange(checked)
}
disabled
/>
<label htmlFor="all" className="text-sm">
Semua
</label>
</div>
{items.map((item) => (
<FormField
key={item.id}
control={form.control}
name="output"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex flex-row items-start space-x-3 space-y-0"
>
<FormControl>
<Checkbox
checked={output?.includes(item.id)}
onCheckedChange={(checked) =>
handleItemCheckedChange(item.id, checked)
}
disabled
/>
</FormControl>
<FormLabel className="font-normal">
{item.label}
</FormLabel>
</FormItem>
);
}}
/>
))}
</div>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="unit"
render={() => (
<FormItem>
<div className="mb-4">
<FormLabel className="text-sm">Pelaksana Tugas</FormLabel>
</div>
<div className="flex flex-row gap-3 items-center ">
<div className="flex items-center gap-3">
<Checkbox
id="all"
checked={isAllUnitChecked}
onCheckedChange={(checked) =>
handleAllUnitCheckedChange(checked)
}
disabled
/>
<label htmlFor="all" className="text-sm">
Semua
</label>
</div>
{units.map((item) => (
<FormField
key={item.id}
control={form.control}
name="unit"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex flex-row items-start space-x-3 space-y-0"
>
<FormControl>
<Checkbox
checked={unit?.includes(item.id)}
// disabled={
// item.id === "3" && !unit.includes("2")
// }
onCheckedChange={(checked) =>
handleUnitCheckedChange(item.id, checked)
}
disabled
/>
</FormControl>
<FormLabel className="font-normal">
{item.label}
</FormLabel>
</FormItem>
);
}}
/>
))}
<Dialog>
<DialogTrigger disabled>
<a className="text-primary cursor-pointer text-sm">
{`[Kustom]`}
</a>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>
Daftar Wilayah Polda dan Polres
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-4 py-4 px-2 max-h-[600px] overflow-y-auto custom-scrollbar-table">
{listDest?.map((list: any) => (
<div key={list.id}>
<Accordion
type="single"
collapsible
className="h-fit border-none"
>
<AccordionItem
value={list.name}
className="border-none"
>
<div className="flex items-center space-x-2">
<Checkbox
id={list.id}
name={`all${list.id}`}
checked={
unit.includes("2")
? true
: !!selected[list.id]
}
onCheckedChange={() =>
handleParentChange(list.id)
}
disabled={unit.includes("2")}
/>
<label
htmlFor={list.name}
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{list.name}
</label>
<AccordionTrigger className="w-fit bg-transparent"></AccordionTrigger>
</div>
<AccordionContent>
<div className="flex flex-col gap-1">
<div className="flex items-center space-x-2">
<Checkbox
checked={
unit.includes("3")
? true
: !!selectAll[list.id]
}
onCheckedChange={(e) =>
handleSelectAllPolres(
list.id,
Boolean(e)
)
}
disabled={unit.includes("3")}
/>
<label
htmlFor="all-polres"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Pilih Semua Polres
</label>
</div>
{list.subDestination.map(
(subDes: any) => (
<div
key={subDes.id}
className="flex items-center space-x-2"
>
<Checkbox
id={`${list.id}${subDes.id}`}
checked={
unit.includes("3")
? true
: !!selected[
`${list.id}${subDes.id}`
]
}
onCheckedChange={() =>
handleChildChange(
`${list.id}${subDes.id}`
)
}
disabled={unit.includes("3")}
/>
<label
htmlFor={`${list.id}${subDes.id}`}
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{subDes.name}
</label>
</div>
)
)}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
))}
</div>
</DialogContent>
</Dialog>
</div>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="type"
render={({ field }) => (
<FormItem>
<FormLabel>Jenis Penugasan</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
value={field.value}
className="flex flex-row gap-3"
disabled
>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="1" />
</FormControl>
<FormLabel className="font-normal">
Publikasi
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="2" />
</FormControl>
<FormLabel className="font-normal">
Amplifikasi
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="3" />
</FormControl>
<FormLabel className="font-normal">Kontra</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="date"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Tanggal</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!field.value && "text-muted-foreground"
)}
disabled
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value ? (
format(field.value, "PPP")
) : (
<span>Masukkan Tanggal</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="parentId"
render={({ field }) => (
<FormItem>
<FormLabel>Perencanaan Mingguan</FormLabel>
<Select
value={field.value}
onValueChange={field.onChange}
disabled
>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{weeklyList?.map((list: any) => (
<SelectItem key={list.id} value={list.value}>
{list.label}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="detail"
render={({ field }) => (
<FormItem>
<FormLabel>Detail Perencanaan</FormLabel>
<JoditEditor
ref={editor}
value={field.value}
config={{ readonly: true }}
className="dark:text-black"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
<Button
onClick={() => router.back()}
variant="outline"
color="destructive"
type="button"
>
Back
</Button>
</div>
</form>
</Form>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,764 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { getUserLevelForAssignments } from "@/service/task";
import { list } from "postcss";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { close, error, loading } from "@/config/swal";
import { id, te } from "date-fns/locale";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
getWeeklyPlanList,
savePlanning,
} from "@/service/agenda-setting/agenda-setting";
import { getOnlyDate } from "@/utils/globals";
import { useParams } from "next/navigation";
import { getPlanningById } from "@/service/planning/planning";
const FormSchema = z.object({
date: z.date({
required_error: "Required",
}),
title: z.string({
required_error: "Required",
}),
detail: z.string({
required_error: "Required",
}),
output: z.array(z.string()).refine((value) => value.some((item) => item), {
message: "Required",
}),
unit: z.array(z.string()).refine((value) => value.some((item) => item), {
message: "Required",
}),
type: z.string({
required_error: "Required",
}),
parentId: z.string({
required_error: "Required",
}),
});
const items = [
{
id: "2",
label: "Audio Visual",
},
{
id: "1",
label: "Foto",
},
{
id: "4",
label: "Audio",
},
{
id: "3",
label: "Text",
},
];
const units = [
{
id: "1",
label: "Mabes Polri",
},
{
id: "2",
label: "Polda",
},
{
id: "3",
label: "Polres",
},
];
export default function EditDaily() {
const id = useParams()?.id;
const MySwal = withReactContent(Swal);
const [listDest, setListDest] = useState<any>([]);
const router = useRouter();
const [weeklyList, setWeeklyList] = useState<any>();
const [selected, setSelected] = useState<{ [key: string]: boolean }>({});
const [selectAll, setSelectAll] = useState<{ [key: string]: boolean }>({});
useEffect(() => {
initFetch();
}, [id]);
async function initFetch() {
if (id != undefined) {
loading();
const res = await getPlanningById(id);
close();
if (res?.data?.data != undefined) {
const data = res?.data?.data;
console.log("data");
console.log("Data :", data);
form.setValue("title", data.title);
form.setValue("detail", data.description);
form.setValue("date", new Date(data.date));
form.setValue(
"output",
data.fileTypeOutput.split(",")?.length > 1
? data.fileTypeOutput.split(",")
: [data.fileTypeOutput]
);
form.setValue("type", String(data?.assignmentTypeId));
form.setValue("parentId", String(data?.parentId));
mapTopDestination(data?.assignedToLevel);
mapDestination(data?.assignedToTopLevel);
}
}
}
const mapTopDestination = (data: string) => {
const temp: string[] = [];
data.split(",").map((list) => {
if (list.length < 2) {
temp.push(list);
}
});
form.setValue("unit", temp);
};
const mapDestination = (data: string) => {
const temp: { [key: number]: boolean } = {};
data.split(",").forEach((list) => {
temp[Number(list)] = true;
});
setSelected(temp);
};
useEffect(() => {
getWeeklyPlanning();
}, []);
async function getWeeklyPlanning() {
const res = await getWeeklyPlanList(new Date().getDate(), 1);
if (res.data !== null) {
const rawUser = res.data?.data;
const optionArr = rawUser.map((option: any) => ({
id: option.id,
label: option.title,
value: String(option.id),
}));
setWeeklyList(optionArr);
}
}
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
unit: [],
output: [],
detail: "",
},
});
const editor = useRef(null);
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
console.log("data", data);
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 getSelectedString = () => {
return Object.keys(selected)
.filter((key) => selected[key])
.join(", ");
};
console.log("data", data, selected);
loading();
const reqData = {
id: id,
planningTypeId: 1,
time: "1",
title: data.title,
assignmentTypeId: data.type, //string
description: data.detail,
assignedToLevel: unit?.join(","), //string
assignmentPurpose: getSelectedString(), //string
fileTypeOutput: data.output?.join(","), //string
status: "Open",
date: getOnlyDate(data.date),
// date:
// isPublish || isUpdate
// ? selectedDate?.length > 10
// ? data.date?.toISOString().slice(0, 10)
// : selectedDate
// : data.date?.toISOString().slice(0, 10),
parentId: Number(data.parentId), //number
assignmentMainTypeId: 1,
};
const response = await savePlanning(reqData);
if (response.error) {
error(response.message);
return false;
}
close();
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/curator/task-plan/mediahub");
}
});
};
const output = form.watch("output");
const isAllChecked = items.every((item) => output?.includes(item.id));
const unit = form.watch("unit");
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
const handleAllCheckedChange = (checked: boolean | string) => {
if (checked) {
form.setValue(
"output",
items.map((item) => item.id)
);
} else {
form.setValue("output", []);
}
};
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
form.setValue(
"output",
checked ? [...output, id] : output.filter((value) => value !== id)
);
};
const handleAllUnitCheckedChange = (checked: boolean | string) => {
if (checked) {
form.setValue(
"unit",
units.map((item) => item.id)
);
} else {
form.setValue("unit", []);
}
};
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
if (checked) {
form.setValue("unit", [...unit, id]);
} else {
if (id == "2") {
const temp = [];
for (const element of unit) {
if (element == "1") {
temp.push("1");
}
}
form.setValue("unit", temp);
} else {
form.setValue(
"unit",
unit.filter((value) => value !== id)
);
}
}
};
useEffect(() => {
async function initState() {
const response = await getUserLevelForAssignments();
setListDest(response.data.data.list);
}
initState();
}, []);
const handleParentChange = (listId: string) => {
setSelected((prev) => ({
...prev,
[listId]: !prev[listId],
}));
};
const handleSelectAllPolres = (listId: string, isChecked: boolean) => {
setSelectAll((prev) => ({
...prev,
[listId]: isChecked,
}));
setSelected((prev) => {
const updatedState = { ...prev };
listDest
.find((list: any) => list.id === listId)
?.subDestination.forEach((subDes: any) => {
updatedState[`${listId}${subDes.id}`] = isChecked;
});
return updatedState;
});
};
const handleChildChange = (childId: string) => {
setSelected((prev) => ({
...prev,
[childId]: !prev[childId],
}));
};
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-col gap-4">
<div className="flex flex-col bg-white gap-2 p-6">
<p className="text-lg">Perencanaan MediaHub</p>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Judul Perencanaan</FormLabel>
<Input
value={field.value}
placeholder="Masukkan Judul Perencanaan"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="output"
render={() => (
<FormItem>
<div className="mb-4">
<FormLabel className="text-sm">Output Tugas</FormLabel>
</div>
<div className="flex flex-row gap-3 items-center ">
<div className="flex items-center gap-3">
<Checkbox
id="all"
checked={isAllChecked}
onCheckedChange={(checked) =>
handleAllCheckedChange(checked)
}
/>
<label htmlFor="all" className="text-sm">
Semua
</label>
</div>
{items.map((item) => (
<FormField
key={item.id}
control={form.control}
name="output"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex flex-row items-start space-x-3 space-y-0"
>
<FormControl>
<Checkbox
checked={output?.includes(item.id)}
onCheckedChange={(checked) =>
handleItemCheckedChange(item.id, checked)
}
/>
</FormControl>
<FormLabel className="font-normal">
{item.label}
</FormLabel>
</FormItem>
);
}}
/>
))}
</div>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="unit"
render={() => (
<FormItem>
<div className="mb-4">
<FormLabel className="text-sm">Pelaksana Tugas</FormLabel>
</div>
<div className="flex flex-row gap-3 items-center ">
<div className="flex items-center gap-3">
<Checkbox
id="all"
checked={isAllUnitChecked}
onCheckedChange={(checked) =>
handleAllUnitCheckedChange(checked)
}
/>
<label htmlFor="all" className="text-sm">
Semua
</label>
</div>
{units.map((item) => (
<FormField
key={item.id}
control={form.control}
name="unit"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex flex-row items-start space-x-3 space-y-0"
>
<FormControl>
<Checkbox
checked={unit?.includes(item.id)}
disabled={
item.id === "3" && !unit.includes("2")
}
onCheckedChange={(checked) =>
handleUnitCheckedChange(item.id, checked)
}
/>
</FormControl>
<FormLabel className="font-normal">
{item.label}
</FormLabel>
</FormItem>
);
}}
/>
))}
<Dialog>
<DialogTrigger>
<a className="text-primary cursor-pointer text-sm">
{`[Kustom]`}
</a>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>
Daftar Wilayah Polda dan Polres
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-4 py-4 px-2 max-h-[600px] overflow-y-auto custom-scrollbar-table">
{listDest?.map((list: any) => (
<div key={list.id}>
<Accordion
type="single"
collapsible
className="h-fit border-none"
>
<AccordionItem
value={list.name}
className="border-none"
>
<div className="flex items-center space-x-2">
<Checkbox
id={list.id}
name={`all${list.id}`}
checked={
unit.includes("2")
? true
: !!selected[list.id]
}
onCheckedChange={() =>
handleParentChange(list.id)
}
disabled={unit.includes("2")}
/>
<label
htmlFor={list.name}
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{list.name}
</label>
<AccordionTrigger className="w-fit bg-transparent"></AccordionTrigger>
</div>
<AccordionContent>
<div className="flex flex-col gap-1">
<div className="flex items-center space-x-2">
<Checkbox
checked={
unit.includes("3")
? true
: !!selectAll[list.id]
}
onCheckedChange={(e) =>
handleSelectAllPolres(
list.id,
Boolean(e)
)
}
disabled={unit.includes("3")}
/>
<label
htmlFor="all-polres"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Pilih Semua Polres
</label>
</div>
{list.subDestination.map(
(subDes: any) => (
<div
key={subDes.id}
className="flex items-center space-x-2"
>
<Checkbox
id={`${list.id}${subDes.id}`}
checked={
unit.includes("3")
? true
: !!selected[
`${list.id}${subDes.id}`
]
}
onCheckedChange={() =>
handleChildChange(
`${list.id}${subDes.id}`
)
}
disabled={unit.includes("3")}
/>
<label
htmlFor={`${list.id}${subDes.id}`}
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{subDes.name}
</label>
</div>
)
)}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
))}
</div>
</DialogContent>
</Dialog>
</div>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="type"
render={({ field }) => (
<FormItem>
<FormLabel>Jenis Penugasan</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
value={field.value}
className="flex flex-row gap-3"
>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="1" />
</FormControl>
<FormLabel className="font-normal">
Publikasi
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="2" />
</FormControl>
<FormLabel className="font-normal">
Amplifikasi
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="3" />
</FormControl>
<FormLabel className="font-normal">Kontra</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="date"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Tanggal</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value ? (
format(field.value, "PPP")
) : (
<span>Masukkan Tanggal</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="parentId"
render={({ field }) => (
<FormItem>
<FormLabel>Perencanaan Mingguan</FormLabel>
<Select value={field.value} onValueChange={field.onChange}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{weeklyList?.map((list: any) => (
<SelectItem key={list.id} value={list.value}>
{list.label}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="detail"
render={({ field }) => (
<FormItem>
<FormLabel>Detail Perencanaan</FormLabel>
<JoditEditor
ref={editor}
value={field.value}
className="dark:text-black"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
<Button
onClick={() => router.back()}
variant="outline"
color="destructive"
type="button"
>
Back
</Button>
<Button type="submit" color="primary">
Submit
</Button>
</div>
</form>
</Form>
</div>
</div>
</div>
);
}

View File

@ -8,9 +8,9 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Link } from "@/i18n/routing";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useRef, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
@ -30,6 +30,35 @@ import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { getUserLevelForAssignments } from "@/service/task";
import { list } from "postcss";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { close, error, loading } from "@/config/swal";
import { id } from "date-fns/locale";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
getWeeklyPlanList,
savePlanning,
} from "@/service/agenda-setting/agenda-setting";
import { getOnlyDate } from "@/utils/globals";
const FormSchema = z.object({
date: z.date({
@ -47,46 +76,72 @@ const FormSchema = z.object({
unit: z.array(z.string()).refine((value) => value.some((item) => item), {
message: "Required",
}),
type: z.enum(["publication", "amplification", "contra"], {
type: z.string({
required_error: "Required",
}),
parentId: z.string({
required_error: "Required",
}),
});
const items = [
{
id: "video",
id: "2",
label: "Audio Visual",
},
{
id: "image",
id: "1",
label: "Foto",
},
{
id: "audio",
id: "4",
label: "Audio",
},
{
id: "text",
id: "3",
label: "Text",
},
];
const units = [
{
id: "mabes",
id: "1",
label: "Mabes Polri",
},
{
id: "polda",
id: "2",
label: "Polda",
},
{
id: "polres",
id: "3",
label: "Polres",
},
];
export default function CreateMonthly() {
export default function CreateDaily() {
const MySwal = withReactContent(Swal);
const [listDest, setListDest] = useState<any>([]);
const router = useRouter();
const [weeklyList, setWeeklyList] = useState<any>();
const [selected, setSelected] = useState<{ [key: string]: boolean }>({});
const [selectAll, setSelectAll] = useState<{ [key: string]: boolean }>({});
useEffect(() => {
getWeeklyPlanning();
}, []);
async function getWeeklyPlanning() {
const res = await getWeeklyPlanList(new Date().getDate(), 1);
if (res.data !== null) {
const rawUser = res.data?.data;
const optionArr = rawUser.map((option: any) => ({
id: option.id,
label: option.title,
value: String(option.id),
}));
setWeeklyList(optionArr);
}
}
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
@ -123,7 +178,54 @@ export default function CreateMonthly() {
};
const save = async (data: z.infer<typeof FormSchema>) => {
console.log("data", data);
const getSelectedString = () => {
return Object.keys(selected)
.filter((key) => selected[key])
.join(", ");
};
console.log("data", data, selected);
loading();
const reqData = {
planningTypeId: 1,
time: "1",
title: data.title,
assignmentTypeId: data.type, //string
description: data.detail,
assignedToLevel: unit?.join(","), //string
assignmentPurpose: getSelectedString(), //string
fileTypeOutput: data.output?.join(","), //string
status: "Open",
date: getOnlyDate(data.date),
// date:
// isPublish || isUpdate
// ? selectedDate?.length > 10
// ? data.date?.toISOString().slice(0, 10)
// : selectedDate
// : data.date?.toISOString().slice(0, 10),
parentId: Number(data.parentId), //number
assignmentMainTypeId: 1,
};
console.log("req =>", reqData);
const response = await savePlanning(reqData);
if (response.error) {
error(response.message);
return false;
}
close();
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/curator/task-plan/mediahub");
}
});
};
const output = form.watch("output");
@ -164,10 +266,64 @@ export default function CreateMonthly() {
};
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
if (checked) {
form.setValue("unit", [...unit, id]);
} else {
if (id == "2") {
const temp = [];
for (const element of unit) {
if (element == "1") {
temp.push("1");
}
}
form.setValue("unit", temp);
} else {
form.setValue(
"unit",
checked ? [...unit, id] : unit.filter((value) => value !== id)
unit.filter((value) => value !== id)
);
}
}
};
useEffect(() => {
async function initState() {
const response = await getUserLevelForAssignments();
setListDest(response.data.data.list);
}
initState();
}, []);
const handleParentChange = (listId: string) => {
setSelected((prev) => ({
...prev,
[listId]: !prev[listId],
}));
};
const handleSelectAllPolres = (listId: string, isChecked: boolean) => {
setSelectAll((prev) => ({
...prev,
[listId]: isChecked,
}));
setSelected((prev) => {
const updatedState = { ...prev };
listDest
.find((list: any) => list.id === listId)
?.subDestination.forEach((subDes: any) => {
updatedState[`${listId}${subDes.id}`] = isChecked;
});
return updatedState;
});
};
const handleChildChange = (childId: string) => {
setSelected((prev) => ({
...prev,
[childId]: !prev[childId],
}));
};
return (
@ -226,7 +382,6 @@ export default function CreateMonthly() {
<Checkbox
id="all"
checked={isAllChecked}
// indeterminate={isSomeChecked && !isAllChecked}
onCheckedChange={(checked) =>
handleAllCheckedChange(checked)
}
@ -305,8 +460,7 @@ export default function CreateMonthly() {
<Checkbox
checked={unit?.includes(item.id)}
disabled={
item.id === "polres" &&
!unit.includes("polda")
item.id === "3" && !unit.includes("2")
}
onCheckedChange={(checked) =>
handleUnitCheckedChange(item.id, checked)
@ -321,6 +475,116 @@ export default function CreateMonthly() {
}}
/>
))}
<Dialog>
<DialogTrigger asChild>
<a className="text-primary cursor-pointer text-sm">
{`[Kustom]`}
</a>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>
Daftar Wilayah Polda dan Polres
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-4 py-4 px-2 max-h-[600px] overflow-y-auto custom-scrollbar-table">
{listDest?.map((list: any) => (
<div key={list.id}>
<Accordion
type="single"
collapsible
className="h-fit border-none"
>
<AccordionItem
value={list.name}
className="border-none"
>
<div className="flex items-center space-x-2">
<Checkbox
id={list.id}
name={`all${list.id}`}
checked={
unit.includes("2")
? true
: !!selected[list.id]
}
onCheckedChange={() =>
handleParentChange(list.id)
}
disabled={unit.includes("2")}
/>
<label
htmlFor={list.name}
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{list.name}
</label>
<AccordionTrigger className="w-fit bg-transparent"></AccordionTrigger>
</div>
<AccordionContent>
<div className="flex flex-col gap-1">
<div className="flex items-center space-x-2">
<Checkbox
checked={
unit.includes("3")
? true
: !!selectAll[list.id]
}
onCheckedChange={(e) =>
handleSelectAllPolres(
list.id,
Boolean(e)
)
}
disabled={unit.includes("3")}
/>
<label
htmlFor="all-polres"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Pilih Semua Polres
</label>
</div>
{list.subDestination.map(
(subDes: any) => (
<div
key={subDes.id}
className="flex items-center space-x-2"
>
<Checkbox
id={`${list.id}${subDes.id}`}
checked={
unit.includes("3")
? true
: !!selected[
`${list.id}${subDes.id}`
]
}
onCheckedChange={() =>
handleChildChange(
`${list.id}${subDes.id}`
)
}
disabled={unit.includes("3")}
/>
<label
htmlFor={`${list.id}${subDes.id}`}
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{subDes.name}
</label>
</div>
)
)}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
))}
</div>
</DialogContent>
</Dialog>
</div>
<FormMessage />
</FormItem>
@ -340,7 +604,7 @@ export default function CreateMonthly() {
>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="publication" />
<RadioGroupItem value="1" />
</FormControl>
<FormLabel className="font-normal">
Publikasi
@ -348,7 +612,7 @@ export default function CreateMonthly() {
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="amplification" />
<RadioGroupItem value="2" />
</FormControl>
<FormLabel className="font-normal">
Amplifikasi
@ -356,7 +620,7 @@ export default function CreateMonthly() {
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="contra" />
<RadioGroupItem value="3" />
</FormControl>
<FormLabel className="font-normal">Kontra</FormLabel>
</FormItem>
@ -402,6 +666,31 @@ export default function CreateMonthly() {
</FormItem>
)}
/>
<FormField
control={form.control}
name="parentId"
render={({ field }) => (
<FormItem>
<FormLabel>Perencanaan Mingguan</FormLabel>
<Select value={field.value} onValueChange={field.onChange}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{weeklyList?.map((list: any) => (
<SelectItem key={list.id} value={list.value}>
{list.label}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="detail"

View File

@ -0,0 +1,249 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, error, loading } from "@/config/swal";
import { savePlanning } from "@/service/agenda-setting/agenda-setting";
import { getPlanningById } from "@/service/planning/planning";
import { useParams } from "next/navigation";
const FormSchema = z.object({
month: z.date({
required_error: "Required",
}),
title: z.string({
required_error: "Required",
}),
detail: z.string({
required_error: "Required",
}),
});
export default function DetailMonthly() {
const id = useParams()?.id;
const MySwal = withReactContent(Swal);
const router = useRouter();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
detail: "",
},
});
const editor = useRef(null);
useEffect(() => {
async function getPlanning() {
if (id != undefined) {
const parseDate = (dateString: string): Date => {
const [month, year] = dateString.split("/").map(Number);
return new Date(year, month - 1);
};
loading();
const res = await getPlanningById(id);
close();
if (res?.data?.data != undefined) {
const data = res?.data?.data;
console.log("Data :", data);
form.setValue("title", data?.title);
form.setValue("detail", data.description);
const date = parseDate(data.date);
console.log("date", date);
form.setValue(
"month",
new Date(date.getFullYear(), date.getMonth(), 1)
);
}
}
}
getPlanning();
}, [id]);
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 reqData = {
planningTypeId: 1,
title: data.title,
time: "3",
description: data.detail,
username: "",
date: `${new Date(data.month).getMonth() + 1}/${new Date(
data.month
).getFullYear()}`,
status: "Open",
};
console.log("req", reqData, data.month);
const response = await savePlanning(reqData);
close();
if (response.error) {
error(response.message);
return false;
}
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/curator/task-plan/mediahub");
}
});
};
const handleMonthSelect = (selectedDate: Date | undefined) => {
if (!selectedDate) return;
const newDate = new Date(
selectedDate.getFullYear(),
selectedDate.getMonth(),
1
);
console.log("newDate", newDate, selectedDate);
form.setValue("month", newDate);
};
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-col gap-4">
<div className="flex flex-col bg-white gap-2 p-6">
<p className="text-lg">Perencanaan MediaHub Bulanan</p>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Judul Perencanaan</FormLabel>
<Input
value={field.value}
placeholder="Masukkan Judul Perencanaan"
onChange={field.onChange}
readOnly
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="month"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Bulan</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!field.value && "text-muted-foreground"
)}
disabled
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value ? (
format(field.value, "MMMM yyyy")
) : (
<span>Masukkan Bulan</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="single"
selected={field.value}
onSelect={handleMonthSelect}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="detail"
render={({ field }) => (
<FormItem>
<FormLabel>Detail Perencanaan</FormLabel>
<JoditEditor
ref={editor}
config={{ readonly: true }}
value={field.value}
className="dark:text-black"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
<Button
onClick={() => router.back()}
variant="outline"
color="destructive"
type="button"
>
Back
</Button>
</div>
</form>
</Form>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,250 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, error, loading } from "@/config/swal";
import { savePlanning } from "@/service/agenda-setting/agenda-setting";
import { getPlanningById } from "@/service/planning/planning";
import { useParams } from "next/navigation";
const FormSchema = z.object({
month: z.date({
required_error: "Required",
}),
title: z.string({
required_error: "Required",
}),
detail: z.string({
required_error: "Required",
}),
});
export default function EditMonthly() {
const id = useParams()?.id;
const MySwal = withReactContent(Swal);
const router = useRouter();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
detail: "",
},
});
const editor = useRef(null);
useEffect(() => {
async function getPlanning() {
if (id != undefined) {
const parseDate = (dateString: string): Date => {
const [month, year] = dateString.split("/").map(Number);
return new Date(year, month - 1);
};
loading();
const res = await getPlanningById(id);
close();
if (res?.data?.data != undefined) {
const data = res?.data?.data;
console.log("Data :", data);
form.setValue("title", data?.title);
form.setValue("detail", data.description);
const date = parseDate(data.date);
console.log("date", date);
form.setValue(
"month",
new Date(date.getFullYear(), date.getMonth(), 1)
);
}
}
}
getPlanning();
}, [id]);
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 reqData = {
id: id,
planningTypeId: 1,
title: data.title,
time: "3",
description: data.detail,
username: "",
date: `${new Date(data.month).getMonth() + 1}/${new Date(
data.month
).getFullYear()}`,
status: "Open",
};
console.log("req", reqData, data.month);
const response = await savePlanning(reqData);
close();
if (response.error) {
error(response.message);
return false;
}
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/curator/task-plan/mediahub");
}
});
};
const handleMonthSelect = (selectedDate: Date | undefined) => {
if (!selectedDate) return;
const newDate = new Date(
selectedDate.getFullYear(),
selectedDate.getMonth(),
1
);
console.log("newDate", newDate, selectedDate);
form.setValue("month", newDate);
};
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-col gap-4">
<div className="flex flex-col bg-white gap-2 p-6">
<p className="text-lg">Perencanaan MediaHub Bulanan</p>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Judul Perencanaan</FormLabel>
<Input
value={field.value}
placeholder="Masukkan Judul Perencanaan"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="month"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Bulan</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value ? (
format(field.value, "MMMM yyyy")
) : (
<span>Masukkan Bulan</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="single"
selected={field.value}
onSelect={handleMonthSelect}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="detail"
render={({ field }) => (
<FormItem>
<FormLabel>Detail Perencanaan</FormLabel>
<JoditEditor
ref={editor}
value={field.value}
className="dark:text-black"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
<Button
onClick={() => router.back()}
variant="outline"
color="destructive"
type="button"
>
Back
</Button>
<Button type="submit" color="primary">
Submit
</Button>
</div>
</form>
</Form>
</div>
</div>
</div>
);
}

View File

@ -83,7 +83,9 @@ export default function CreateMonthly() {
time: "3",
description: data.detail,
username: "",
date: new Date(data.month).getMonth() + 1,
date: `${new Date(data.month).getMonth() + 1}/${new Date(
data.month
).getFullYear()}`,
status: "Open",
};
console.log("req", reqData, data.month);

View File

@ -0,0 +1,261 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, error, loading } from "@/config/swal";
import { getOnlyDate } from "@/utils/globals";
import {
getMonthlyPlanList,
savePlanning,
} from "@/service/agenda-setting/agenda-setting";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import dayjs from "dayjs";
import { getPlanningById } from "@/service/planning/planning";
import { useParams } from "next/navigation";
const FormSchema = z.object({
week: z.object({
from: z.date({
required_error: "Start date (from) is required",
}),
to: z.date({
required_error: "End date (to) is required",
}),
}),
title: z.string({
required_error: "Required",
}),
detail: z.string({
required_error: "Required",
}),
});
export default function DetailMonthly() {
const id = useParams()?.id;
const MySwal = withReactContent(Swal);
const router = useRouter();
const [parentId, setParentId] = useState<number | undefined>();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
detail: "",
},
});
const editor = useRef(null);
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 reqData = {
id: id,
planningTypeId: 1,
title: data.title,
time: "2",
description: data.detail,
username: "",
date: `${getOnlyDate(data.week.from)} - ${getOnlyDate(data.week.to)}`,
status: "Open",
parentId: parentId,
};
console.log("req", reqData);
const response = await savePlanning(reqData);
close();
if (response.error) {
error(response.message);
return false;
}
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/curator/task-plan/mediahub");
}
});
};
useEffect(() => {
async function getPlanningData() {
if (id != undefined) {
loading();
const res = await getPlanningById(id);
close();
if (res?.data?.data != undefined) {
const data = res?.data?.data;
console.log("Data :", data);
form.setValue("title", data?.title);
form.setValue("week", {
from: new Date(data?.startDate),
to: new Date(data?.endDate),
});
form.setValue("detail", data.description);
setParentId(data?.parentId);
}
}
}
getPlanningData();
}, []);
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-col gap-4">
<div className="flex flex-col bg-white gap-2 p-6">
<p className="text-lg">Perencanaan MediaHub Mingguan</p>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Judul Perencanaan</FormLabel>
<Input
value={field.value}
placeholder="Masukkan Judul Perencanaan"
onChange={field.onChange}
readOnly
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="week"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Tanggal</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!field.value && "text-muted-foreground"
)}
disabled
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value?.from ? (
field.value.to ? (
<>
{format(field.value.from, "LLL dd, y")} -{" "}
{format(field.value.to, "LLL dd, y")}
</>
) : (
format(field.value.from, "LLL dd, y")
)
) : (
<span>Masukkan Tanggal</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="range"
selected={field.value}
onSelect={field.onChange}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="detail"
render={({ field }) => (
<FormItem>
<FormLabel>Detail Perencanaan</FormLabel>
<JoditEditor
ref={editor}
value={field.value}
className="dark:text-black"
onChange={field.onChange}
config={{ readonly: true }}
/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
<Button
onClick={() => router.back()}
variant="outline"
color="destructive"
type="button"
>
Back
</Button>
</div>
</form>
</Form>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,261 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, error, loading } from "@/config/swal";
import { getOnlyDate } from "@/utils/globals";
import {
getMonthlyPlanList,
savePlanning,
} from "@/service/agenda-setting/agenda-setting";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import dayjs from "dayjs";
import { getPlanningById } from "@/service/planning/planning";
import { useParams } from "next/navigation";
const FormSchema = z.object({
week: z.object({
from: z.date({
required_error: "Start date (from) is required",
}),
to: z.date({
required_error: "End date (to) is required",
}),
}),
title: z.string({
required_error: "Required",
}),
detail: z.string({
required_error: "Required",
}),
});
export default function EditMonthly() {
const id = useParams()?.id;
const MySwal = withReactContent(Swal);
const router = useRouter();
const [parentId, setParentId] = useState<number | undefined>();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
detail: "",
},
});
const editor = useRef(null);
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 reqData = {
id: id,
planningTypeId: 1,
title: data.title,
time: "2",
description: data.detail,
username: "",
date: `${getOnlyDate(data.week.from)} - ${getOnlyDate(data.week.to)}`,
status: "Open",
parentId: parentId,
};
console.log("req", reqData);
const response = await savePlanning(reqData);
close();
if (response.error) {
error(response.message);
return false;
}
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/curator/task-plan/mediahub");
}
});
};
useEffect(() => {
async function getPlanningData() {
if (id != undefined) {
loading();
const res = await getPlanningById(id);
close();
if (res?.data?.data != undefined) {
const data = res?.data?.data;
console.log("Data :", data);
form.setValue("title", data?.title);
form.setValue("week", {
from: new Date(data?.startDate),
to: new Date(data?.endDate),
});
form.setValue("detail", data.description);
setParentId(data?.parentId);
}
}
}
getPlanningData();
}, []);
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-col gap-4">
<div className="flex flex-col bg-white gap-2 p-6">
<p className="text-lg">Perencanaan MediaHub Mingguan</p>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Judul Perencanaan</FormLabel>
<Input
value={field.value}
placeholder="Masukkan Judul Perencanaan"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="week"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Tanggal</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value?.from ? (
field.value.to ? (
<>
{format(field.value.from, "LLL dd, y")} -{" "}
{format(field.value.to, "LLL dd, y")}
</>
) : (
format(field.value.from, "LLL dd, y")
)
) : (
<span>Masukkan Tanggal</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="range"
selected={field.value}
onSelect={field.onChange}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="detail"
render={({ field }) => (
<FormItem>
<FormLabel>Detail Perencanaan</FormLabel>
<JoditEditor
ref={editor}
value={field.value}
className="dark:text-black"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
<Button
onClick={() => router.back()}
variant="outline"
color="destructive"
type="button"
>
Back
</Button>
<Button type="submit" color="primary">
Submit
</Button>
</div>
</form>
</Form>
</div>
</div>
</div>
);
}

View File

@ -10,7 +10,7 @@ import {
} from "@/components/ui/popover";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useRef, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
@ -30,7 +30,18 @@ import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { error } from "@/config/swal";
import { getOnlyDate } from "@/utils/globals";
import { savePlanning } from "@/service/agenda-setting/agenda-setting";
import {
getMonthlyPlanList,
savePlanning,
} from "@/service/agenda-setting/agenda-setting";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import dayjs from "dayjs";
const FormSchema = z.object({
week: z.object({
@ -47,6 +58,9 @@ const FormSchema = z.object({
detail: z.string({
required_error: "Required",
}),
parentId: z.string({
required_error: "Required",
}),
});
export default function CreateMonthly() {
const MySwal = withReactContent(Swal);
@ -57,6 +71,8 @@ export default function CreateMonthly() {
detail: "",
},
});
const [monthlyList, setMonthlyList] = useState<any>();
const editor = useRef(null);
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
@ -91,6 +107,7 @@ export default function CreateMonthly() {
username: "",
date: `${getOnlyDate(data.week.from)} - ${getOnlyDate(data.week.to)}`,
status: "Open",
parentId: Number(data.parentId),
};
console.log("req", reqData);
const response = await savePlanning(reqData);
@ -112,6 +129,23 @@ export default function CreateMonthly() {
});
};
useEffect(() => {
getMonthlyPlanning();
}, []);
async function getMonthlyPlanning() {
const res = await getMonthlyPlanList(new Date().getDate(), 1);
if (res.data !== null) {
const rawUser = res.data?.data;
const optionArr = rawUser.map((option: any) => ({
id: option.id,
label: option.title,
value: String(option.id),
}));
setMonthlyList(optionArr);
}
}
return (
<div>
<SiteBreadcrumb />
@ -198,6 +232,31 @@ export default function CreateMonthly() {
</FormItem>
)}
/>
<FormField
control={form.control}
name="parentId"
render={({ field }) => (
<FormItem>
<FormLabel>Perencanaan Bulanan</FormLabel>
<Select value={field.value} onValueChange={field.onChange}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{monthlyList?.map((list: any) => (
<SelectItem key={list.id} value={list.value}>
{list.label}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="detail"

View File

@ -0,0 +1,75 @@
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 { htmlToString } from "@/utils/globals";
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 Perencanaan",
cell: ({ row }) => <span>{row.getValue("title")}</span>,
},
{
accessorKey: "createdByName",
header: "Nama Pembuat",
cell: ({ row }) => <span>{row.getValue("createdByName")}</span>,
},
{
accessorKey: "description",
header: "Deskripsi",
cell: ({ row }) => <span>{htmlToString(row.getValue("description"))}</span>,
},
{
accessorKey: "status",
header: "Status",
cell: ({ row }) => <span>{row.getValue("status")}</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={`/curator/task-plan/mediahub/detail/${row.original.id}`}
>
Detail
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];
export default columns;

View File

@ -0,0 +1,183 @@
"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,
} from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
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 "./columns";
const TaskPlanMediahubTable = (props: {
data: any;
totalPage: number;
totalData: number;
}) => {
const { data, totalPage, totalData } = props;
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 [limit, setLimit] = React.useState(10);
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, limit, data]);
async function fetchData() {
// try {
// const res = await ticketingPagination("", limit, page - 1);
// const data = res.data?.data;
console.log("datgaa", data);
const contentData = data;
contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * limit + index + 1;
});
console.log("contentData : ", contentData);
setDataTable(contentData);
// setTotalData(data?.totalElements);
// } catch (error) {
// console.error("Error fetching tasks:", error);
// }
}
return (
<div className="w-full overflow-x-auto">
<Table className="overflow-hidden mt-3">
<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 TaskPlanMediahubTable;

View File

@ -0,0 +1,791 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
getWeeklyPlanList,
savePlanning,
} from "@/service/agenda-setting/agenda-setting";
import { id } from "date-fns/locale";
import { close, error, loading } from "@/config/swal";
import { getOnlyDate } from "@/utils/globals";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { getUserLevelForAssignments } from "@/service/task";
const FormSchema = z.object({
date: z.date({
required_error: "Required",
}),
title: z.string({
required_error: "Required",
}),
detail: z.string({
required_error: "Required",
}),
output: z.array(z.string()).refine((value) => value.some((item) => item), {
message: "Required",
}),
unit: z.array(z.string()).refine((value) => value.some((item) => item), {
message: "Required",
}),
type: z.string({
required_error: "Required",
}),
parentId: z.string({
required_error: "Required",
}),
media: z.array(z.string()).refine((value) => value.some((item) => item), {
message: "Required",
}),
});
const items = [
{
id: "2",
label: "Audio Visual",
},
{
id: "1",
label: "Foto",
},
{
id: "4",
label: "Audio",
},
{
id: "3",
label: "Text",
},
];
const medias = [
{
id: "5",
label: "X",
},
{
id: "1",
label: "Facebook",
},
{
id: "2",
label: "Instagram",
},
{
id: "3",
label: "Youtube",
},
{
id: "4",
label: "Tiktok",
},
];
const units = [
{
id: "1",
label: "Mabes Polri",
},
{
id: "2",
label: "Polda",
},
{
id: "3",
label: "Polres",
},
];
export default function CreateMonthly() {
const MySwal = withReactContent(Swal);
const [weeklyList, setWeeklyList] = useState<any>();
const router = useRouter();
const [selected, setSelected] = useState<{ [key: string]: boolean }>({});
const [selectAll, setSelectAll] = useState<{ [key: string]: boolean }>({});
const [listDest, setListDest] = useState<any>([]);
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
unit: [],
output: [],
detail: "",
media: [],
},
});
const editor = useRef(null);
useEffect(() => {
getWeeklyPlanning();
}, []);
async function getWeeklyPlanning() {
const res = await getWeeklyPlanList(new Date().getDate(), 2);
if (res.data !== null) {
const rawUser = res.data?.data;
const optionArr = rawUser.map((option: any) => ({
id: option.id,
label: option.title,
value: String(option.id),
}));
setWeeklyList(optionArr);
}
}
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 getSelectedString = () => {
return Object.keys(selected)
.filter((key) => selected[key])
.join(", ");
};
loading();
const reqData = {
planningTypeId: 2,
time: "1",
title: data.title,
assignmentTypeId: data.type, //string
description: data.detail,
assignedToLevel: unit?.join(","), //string
assignmentPurpose: getSelectedString(), //string
fileTypeOutput: data.output?.join(","), //string
status: "Open",
date: getOnlyDate(data.date),
platformType: data.media?.join(","),
parentId: Number(data.parentId), //number
assignmentMainTypeId: 2,
};
console.log("req =>", reqData);
const response = await savePlanning(reqData);
if (response.error) {
error(response.message);
return false;
}
close();
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/curator/task-plan/medsos-mediahub");
}
});
};
const output = form.watch("output");
const media = form.watch("media");
const unit = form.watch("unit");
const isAllChecked = items.every((item) => output?.includes(item.id));
const isAllMediaChecked = medias.every((item) => media?.includes(item.id));
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
useEffect(() => {
async function initState() {
const response = await getUserLevelForAssignments();
setListDest(response.data.data.list);
}
initState();
}, []);
const handleParentChange = (listId: string) => {
setSelected((prev) => ({
...prev,
[listId]: !prev[listId],
}));
};
const handleSelectAllPolres = (listId: string, isChecked: boolean) => {
setSelectAll((prev) => ({
...prev,
[listId]: isChecked,
}));
setSelected((prev) => {
const updatedState = { ...prev };
listDest
.find((list: any) => list.id === listId)
?.subDestination.forEach((subDes: any) => {
updatedState[`${listId}${subDes.id}`] = isChecked;
});
return updatedState;
});
};
const handleChildChange = (childId: string) => {
setSelected((prev) => ({
...prev,
[childId]: !prev[childId],
}));
};
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-col gap-4">
<div className="flex flex-row justify-center gap-5 mt-10">
<Link
href="/curator/task-plan/medsos-mediahub/create-monthly"
className="bg-slate-200 text-black rounded-full px-5 py-2 text-sm"
>
Bulanan
</Link>
<Link
href="/curator/task-plan/medsos-mediahub/create-weekly"
className="bg-slate-200 text-black rounded-full px-5 py-2 text-sm"
>
Mingguan
</Link>
<div className="bg-primary rounded-full px-5 py-2 text-white text-sm">
Harian
</div>
</div>
<div className="flex flex-col bg-white gap-2 p-6">
<p className="text-lg">Perencanaan MediaHub</p>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Judul Perencanaan</FormLabel>
<Input
value={field.value}
placeholder="Masukkan Judul Perencanaan"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="output"
render={() => (
<FormItem>
<div className="mb-4">
<FormLabel className="text-sm">Output Tugas</FormLabel>
</div>
<div className="flex flex-row gap-3 items-center ">
<div className="flex items-center gap-3">
<Checkbox
id="all"
checked={isAllChecked}
onCheckedChange={(checked) => {
if (checked) {
form.setValue(
"output",
items.map((item) => item.id)
);
} else {
form.setValue("output", []);
}
}}
/>
<label htmlFor="all" className="text-sm">
Semua
</label>
</div>
{items.map((item) => (
<FormField
key={item.id}
control={form.control}
name="output"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex flex-row items-start space-x-3 space-y-0"
>
<FormControl>
<Checkbox
checked={output?.includes(item.id)}
onCheckedChange={(checked) => {
form.setValue(
"output",
checked
? [...output, item.id]
: output.filter(
(value) => value !== item.id
)
);
}}
/>
</FormControl>
<FormLabel className="font-normal">
{item.label}
</FormLabel>
</FormItem>
);
}}
/>
))}
</div>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="unit"
render={() => (
<FormItem>
<div className="mb-4">
<FormLabel className="text-sm">Pelaksana Tugas</FormLabel>
</div>
<div className="flex flex-row gap-3 items-center ">
<div className="flex items-center gap-3">
<Checkbox
id="all"
checked={isAllUnitChecked}
onCheckedChange={(checked) => {
if (checked) {
form.setValue(
"unit",
units.map((item) => item.id)
);
} else {
form.setValue("unit", []);
}
}}
/>
<label htmlFor="all" className="text-sm">
Semua
</label>
</div>
{units.map((item) => (
<FormField
key={item.id}
control={form.control}
name="unit"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex flex-row items-start space-x-3 space-y-0"
>
<FormControl>
<Checkbox
checked={unit?.includes(item.id)}
disabled={
item.id === "polres" &&
!unit.includes("polda")
}
onCheckedChange={(checked) => {
form.setValue(
"unit",
checked
? [...unit, item.id]
: unit.filter(
(value) => value !== item.id
)
);
}}
/>
</FormControl>
<FormLabel className="font-normal">
{item.label}
</FormLabel>
</FormItem>
);
}}
/>
))}
<Dialog>
<DialogTrigger asChild>
<a className="text-primary cursor-pointer text-sm">
{`[Kustom]`}
</a>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>
Daftar Wilayah Polda dan Polres
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-4 py-4 px-2 max-h-[600px] overflow-y-auto custom-scrollbar-table">
{listDest?.map((list: any) => (
<div key={list.id}>
<Accordion
type="single"
collapsible
className="h-fit border-none"
>
<AccordionItem
value={list.name}
className="border-none"
>
<div className="flex items-center space-x-2">
<Checkbox
id={list.id}
name={`all${list.id}`}
checked={
unit.includes("2")
? true
: !!selected[list.id]
}
onCheckedChange={() =>
handleParentChange(list.id)
}
disabled={unit.includes("2")}
/>
<label
htmlFor={list.name}
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{list.name}
</label>
<AccordionTrigger className="w-fit bg-transparent"></AccordionTrigger>
</div>
<AccordionContent>
<div className="flex flex-col gap-1">
<div className="flex items-center space-x-2">
<Checkbox
checked={
unit.includes("3")
? true
: !!selectAll[list.id]
}
onCheckedChange={(e) =>
handleSelectAllPolres(
list.id,
Boolean(e)
)
}
disabled={unit.includes("3")}
/>
<label
htmlFor="all-polres"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Pilih Semua Polres
</label>
</div>
{list.subDestination.map(
(subDes: any) => (
<div
key={subDes.id}
className="flex items-center space-x-2"
>
<Checkbox
id={`${list.id}${subDes.id}`}
checked={
unit.includes("3")
? true
: !!selected[
`${list.id}${subDes.id}`
]
}
onCheckedChange={() =>
handleChildChange(
`${list.id}${subDes.id}`
)
}
disabled={unit.includes("3")}
/>
<label
htmlFor={`${list.id}${subDes.id}`}
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{subDes.name}
</label>
</div>
)
)}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
))}
</div>
</DialogContent>
</Dialog>
</div>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="media"
render={() => (
<FormItem>
<div className="mb-4">
<FormLabel className="text-sm">Jenis Platform</FormLabel>
</div>
<div className="flex flex-row gap-3 items-center ">
<div className="flex items-center gap-3">
<Checkbox
id="all"
checked={isAllMediaChecked}
onCheckedChange={(checked) => {
if (checked) {
form.setValue(
"media",
medias.map((item) => item.id)
);
} else {
form.setValue("media", []);
}
}}
/>
<label htmlFor="all" className="text-sm">
Semua
</label>
</div>
{medias.map((item) => (
<FormField
key={item.id}
control={form.control}
name="output"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex flex-row items-start space-x-3 space-y-0"
>
<FormControl>
<Checkbox
checked={media?.includes(item.id)}
onCheckedChange={(checked) => {
form.setValue(
"media",
checked
? [...media, item.id]
: media.filter(
(value) => value !== item.id
)
);
}}
/>
</FormControl>
<FormLabel className="font-normal">
{item.label}
</FormLabel>
</FormItem>
);
}}
/>
))}
</div>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="type"
render={({ field }) => (
<FormItem>
<FormLabel>Jenis Penugasan</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex flex-row gap-3"
>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="1" />
</FormControl>
<FormLabel className="font-normal">
Publikasi
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="2" />
</FormControl>
<FormLabel className="font-normal">
Amplifikasi
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="3" />
</FormControl>
<FormLabel className="font-normal">Kontra</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="parentId"
render={({ field }) => (
<FormItem>
<FormLabel>Perencanaan Mingguan</FormLabel>
<Select value={field.value} onValueChange={field.onChange}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{weeklyList?.map((list: any) => (
<SelectItem key={list.id} value={list.value}>
{list.label}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="date"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Tanggal</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value ? (
format(field.value, "PPP")
) : (
<span>Masukkan Tanggal</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="detail"
render={({ field }) => (
<FormItem>
<FormLabel>Detail Perencanaan</FormLabel>
<JoditEditor
ref={editor}
value={field.value}
className="dark:text-black"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
<Button type="submit" variant="outline" color="destructive">
Cancel
</Button>
<Button type="submit" color="primary">
Submit
</Button>
</div>
</form>
</Form>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,249 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, error, loading } from "@/config/swal";
import { savePlanning } from "@/service/agenda-setting/agenda-setting";
import { getPlanningById } from "@/service/planning/planning";
import { useParams } from "next/navigation";
const FormSchema = z.object({
month: z.date({
required_error: "Required",
}),
title: z.string({
required_error: "Required",
}),
detail: z.string({
required_error: "Required",
}),
});
export default function DetailMonthly() {
const id = useParams()?.id;
const MySwal = withReactContent(Swal);
const router = useRouter();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
detail: "",
},
});
const editor = useRef(null);
useEffect(() => {
async function getPlanning() {
if (id != undefined) {
const parseDate = (dateString: string): Date => {
const [month, year] = dateString.split("/").map(Number);
return new Date(year, month - 1);
};
loading();
const res = await getPlanningById(id);
close();
if (res?.data?.data != undefined) {
const data = res?.data?.data;
console.log("Data :", data);
form.setValue("title", data?.title);
form.setValue("detail", data.description);
const date = parseDate(data.date);
console.log("date", date);
form.setValue(
"month",
new Date(date.getFullYear(), date.getMonth(), 1)
);
}
}
}
getPlanning();
}, [id]);
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 reqData = {
planningTypeId: 1,
title: data.title,
time: "3",
description: data.detail,
username: "",
date: `${new Date(data.month).getMonth() + 1}/${new Date(
data.month
).getFullYear()}`,
status: "Open",
};
console.log("req", reqData, data.month);
const response = await savePlanning(reqData);
close();
if (response.error) {
error(response.message);
return false;
}
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/curator/task-plan/mediahub");
}
});
};
const handleMonthSelect = (selectedDate: Date | undefined) => {
if (!selectedDate) return;
const newDate = new Date(
selectedDate.getFullYear(),
selectedDate.getMonth(),
1
);
console.log("newDate", newDate, selectedDate);
form.setValue("month", newDate);
};
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-col gap-4">
<div className="flex flex-col bg-white gap-2 p-6">
<p className="text-lg">Perencanaan MediaHub Bulanan</p>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Judul Perencanaan</FormLabel>
<Input
value={field.value}
placeholder="Masukkan Judul Perencanaan"
onChange={field.onChange}
readOnly
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="month"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Bulan</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!field.value && "text-muted-foreground"
)}
disabled
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value ? (
format(field.value, "MMMM yyyy")
) : (
<span>Masukkan Bulan</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="single"
selected={field.value}
onSelect={handleMonthSelect}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="detail"
render={({ field }) => (
<FormItem>
<FormLabel>Detail Perencanaan</FormLabel>
<JoditEditor
ref={editor}
config={{ readonly: true }}
value={field.value}
className="dark:text-black"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
<Button
onClick={() => router.back()}
variant="outline"
color="destructive"
type="button"
>
Back
</Button>
</div>
</form>
</Form>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,250 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, error, loading } from "@/config/swal";
import { savePlanning } from "@/service/agenda-setting/agenda-setting";
import { getPlanningById } from "@/service/planning/planning";
import { useParams } from "next/navigation";
const FormSchema = z.object({
month: z.date({
required_error: "Required",
}),
title: z.string({
required_error: "Required",
}),
detail: z.string({
required_error: "Required",
}),
});
export default function EditMonthly() {
const id = useParams()?.id;
const MySwal = withReactContent(Swal);
const router = useRouter();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
detail: "",
},
});
const editor = useRef(null);
useEffect(() => {
async function getPlanning() {
if (id != undefined) {
const parseDate = (dateString: string): Date => {
const [month, year] = dateString.split("/").map(Number);
return new Date(year, month - 1);
};
loading();
const res = await getPlanningById(id);
close();
if (res?.data?.data != undefined) {
const data = res?.data?.data;
console.log("Data :", data);
form.setValue("title", data?.title);
form.setValue("detail", data.description);
const date = parseDate(data.date);
console.log("date", date);
form.setValue(
"month",
new Date(date.getFullYear(), date.getMonth(), 1)
);
}
}
}
getPlanning();
}, [id]);
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 reqData = {
id: id,
planningTypeId: 1,
title: data.title,
time: "3",
description: data.detail,
username: "",
date: `${new Date(data.month).getMonth() + 1}/${new Date(
data.month
).getFullYear()}`,
status: "Open",
};
console.log("req", reqData, data.month);
const response = await savePlanning(reqData);
close();
if (response.error) {
error(response.message);
return false;
}
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/curator/task-plan/mediahub");
}
});
};
const handleMonthSelect = (selectedDate: Date | undefined) => {
if (!selectedDate) return;
const newDate = new Date(
selectedDate.getFullYear(),
selectedDate.getMonth(),
1
);
console.log("newDate", newDate, selectedDate);
form.setValue("month", newDate);
};
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-col gap-4">
<div className="flex flex-col bg-white gap-2 p-6">
<p className="text-lg">Perencanaan MediaHub Bulanan</p>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Judul Perencanaan</FormLabel>
<Input
value={field.value}
placeholder="Masukkan Judul Perencanaan"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="month"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Bulan</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value ? (
format(field.value, "MMMM yyyy")
) : (
<span>Masukkan Bulan</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="single"
selected={field.value}
onSelect={handleMonthSelect}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="detail"
render={({ field }) => (
<FormItem>
<FormLabel>Detail Perencanaan</FormLabel>
<JoditEditor
ref={editor}
value={field.value}
className="dark:text-black"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
<Button
onClick={() => router.back()}
variant="outline"
color="destructive"
type="button"
>
Back
</Button>
<Button type="submit" color="primary">
Submit
</Button>
</div>
</form>
</Form>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,229 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { error } from "@/config/swal";
import { savePlanning } from "@/service/agenda-setting/agenda-setting";
const FormSchema = z.object({
month: z.date({
required_error: "Required",
}),
title: z.string({
required_error: "Required",
}),
detail: z.string({
required_error: "Required",
}),
});
export default function CreateMonthly() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
detail: "",
},
});
const editor = useRef(null);
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 reqData = {
planningTypeId: 2,
title: data.title,
time: "3",
description: data.detail,
username: "",
date: `${new Date(data.month).getMonth() + 1}/${new Date(
data.month
).getFullYear()}`,
status: "Open",
};
console.log("req", reqData, data.month);
const response = await savePlanning(reqData);
close();
if (response.error) {
error(response.message);
return false;
}
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/curator/task-plan/medsos-mediahub");
}
});
};
const handleMonthSelect = (selectedDate: Date | undefined) => {
if (!selectedDate) return;
const newDate = new Date(
selectedDate.getFullYear(),
selectedDate.getMonth(),
1
);
form.setValue("month", newDate);
};
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-col gap-4">
<div className="flex flex-row justify-center gap-5 mt-10">
<div className="bg-primary rounded-full px-5 py-2 text-white text-sm">
Bulanan
</div>
<Link
href="/curator/task-plan/medsos-mediahub/create-weekly"
className="bg-slate-200 text-black rounded-full px-5 py-2 text-sm"
>
Mingguan
</Link>
<Link
href="/curator/task-plan/medsos-mediahub/create-daily"
className="bg-slate-200 text-black rounded-full px-5 py-2 text-sm"
>
Harian
</Link>
</div>
<div className="flex flex-col bg-white gap-2 p-6">
<p className="text-lg">Perencanaan MediaHub Bulanan</p>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Judul Perencanaan</FormLabel>
<Input
value={field.value}
placeholder="Masukkan Judul Perencanaan"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="month"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Bulan</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value ? (
format(field.value, "MMMM yyyy")
) : (
<span>Masukkan Bulan</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="single"
selected={field.value}
onSelect={handleMonthSelect}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="detail"
render={({ field }) => (
<FormItem>
<FormLabel>Detail Perencanaan</FormLabel>
<JoditEditor
ref={editor}
value={field.value}
className="dark:text-black"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
<Button type="submit" variant="outline" color="destructive">
Cancel
</Button>
<Button type="submit" color="primary">
Submit
</Button>
</div>
</form>
</Form>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,261 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, error, loading } from "@/config/swal";
import { getOnlyDate } from "@/utils/globals";
import {
getMonthlyPlanList,
savePlanning,
} from "@/service/agenda-setting/agenda-setting";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import dayjs from "dayjs";
import { getPlanningById } from "@/service/planning/planning";
import { useParams } from "next/navigation";
const FormSchema = z.object({
week: z.object({
from: z.date({
required_error: "Start date (from) is required",
}),
to: z.date({
required_error: "End date (to) is required",
}),
}),
title: z.string({
required_error: "Required",
}),
detail: z.string({
required_error: "Required",
}),
});
export default function DetailMonthly() {
const id = useParams()?.id;
const MySwal = withReactContent(Swal);
const router = useRouter();
const [parentId, setParentId] = useState<number | undefined>();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
detail: "",
},
});
const editor = useRef(null);
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 reqData = {
id: id,
planningTypeId: 1,
title: data.title,
time: "2",
description: data.detail,
username: "",
date: `${getOnlyDate(data.week.from)} - ${getOnlyDate(data.week.to)}`,
status: "Open",
parentId: parentId,
};
console.log("req", reqData);
const response = await savePlanning(reqData);
close();
if (response.error) {
error(response.message);
return false;
}
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/curator/task-plan/mediahub");
}
});
};
useEffect(() => {
async function getPlanningData() {
if (id != undefined) {
loading();
const res = await getPlanningById(id);
close();
if (res?.data?.data != undefined) {
const data = res?.data?.data;
console.log("Data :", data);
form.setValue("title", data?.title);
form.setValue("week", {
from: new Date(data?.startDate),
to: new Date(data?.endDate),
});
form.setValue("detail", data.description);
setParentId(data?.parentId);
}
}
}
getPlanningData();
}, []);
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-col gap-4">
<div className="flex flex-col bg-white gap-2 p-6">
<p className="text-lg">Perencanaan MediaHub Mingguan</p>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Judul Perencanaan</FormLabel>
<Input
value={field.value}
placeholder="Masukkan Judul Perencanaan"
onChange={field.onChange}
readOnly
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="week"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Tanggal</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!field.value && "text-muted-foreground"
)}
disabled
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value?.from ? (
field.value.to ? (
<>
{format(field.value.from, "LLL dd, y")} -{" "}
{format(field.value.to, "LLL dd, y")}
</>
) : (
format(field.value.from, "LLL dd, y")
)
) : (
<span>Masukkan Tanggal</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="range"
selected={field.value}
onSelect={field.onChange}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="detail"
render={({ field }) => (
<FormItem>
<FormLabel>Detail Perencanaan</FormLabel>
<JoditEditor
ref={editor}
value={field.value}
className="dark:text-black"
onChange={field.onChange}
config={{ readonly: true }}
/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
<Button
onClick={() => router.back()}
variant="outline"
color="destructive"
type="button"
>
Back
</Button>
</div>
</form>
</Form>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,261 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, error, loading } from "@/config/swal";
import { getOnlyDate } from "@/utils/globals";
import {
getMonthlyPlanList,
savePlanning,
} from "@/service/agenda-setting/agenda-setting";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import dayjs from "dayjs";
import { getPlanningById } from "@/service/planning/planning";
import { useParams } from "next/navigation";
const FormSchema = z.object({
week: z.object({
from: z.date({
required_error: "Start date (from) is required",
}),
to: z.date({
required_error: "End date (to) is required",
}),
}),
title: z.string({
required_error: "Required",
}),
detail: z.string({
required_error: "Required",
}),
});
export default function EditMonthly() {
const id = useParams()?.id;
const MySwal = withReactContent(Swal);
const router = useRouter();
const [parentId, setParentId] = useState<number | undefined>();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
detail: "",
},
});
const editor = useRef(null);
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 reqData = {
id: id,
planningTypeId: 1,
title: data.title,
time: "2",
description: data.detail,
username: "",
date: `${getOnlyDate(data.week.from)} - ${getOnlyDate(data.week.to)}`,
status: "Open",
parentId: parentId,
};
console.log("req", reqData);
const response = await savePlanning(reqData);
close();
if (response.error) {
error(response.message);
return false;
}
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/curator/task-plan/mediahub");
}
});
};
useEffect(() => {
async function getPlanningData() {
if (id != undefined) {
loading();
const res = await getPlanningById(id);
close();
if (res?.data?.data != undefined) {
const data = res?.data?.data;
console.log("Data :", data);
form.setValue("title", data?.title);
form.setValue("week", {
from: new Date(data?.startDate),
to: new Date(data?.endDate),
});
form.setValue("detail", data.description);
setParentId(data?.parentId);
}
}
}
getPlanningData();
}, []);
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-col gap-4">
<div className="flex flex-col bg-white gap-2 p-6">
<p className="text-lg">Perencanaan MediaHub Mingguan</p>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Judul Perencanaan</FormLabel>
<Input
value={field.value}
placeholder="Masukkan Judul Perencanaan"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="week"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Tanggal</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value?.from ? (
field.value.to ? (
<>
{format(field.value.from, "LLL dd, y")} -{" "}
{format(field.value.to, "LLL dd, y")}
</>
) : (
format(field.value.from, "LLL dd, y")
)
) : (
<span>Masukkan Tanggal</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="range"
selected={field.value}
onSelect={field.onChange}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="detail"
render={({ field }) => (
<FormItem>
<FormLabel>Detail Perencanaan</FormLabel>
<JoditEditor
ref={editor}
value={field.value}
className="dark:text-black"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
<Button
onClick={() => router.back()}
variant="outline"
color="destructive"
type="button"
>
Back
</Button>
<Button type="submit" color="primary">
Submit
</Button>
</div>
</form>
</Form>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,292 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Link, useRouter } from "@/i18n/routing";
import { CalendarIcon } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import JoditEditor from "jodit-react";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { error } from "@/config/swal";
import { getOnlyDate } from "@/utils/globals";
import {
getMonthlyPlanList,
savePlanning,
} from "@/service/agenda-setting/agenda-setting";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
const FormSchema = z.object({
week: z.object({
from: z.date({
required_error: "Start date (from) is required",
}),
to: z.date({
required_error: "End date (to) is required",
}),
}),
title: z.string({
required_error: "Required",
}),
detail: z.string({
required_error: "Required",
}),
parentId: z.string({
required_error: "Required",
}),
});
export default function CreateMonthly() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
detail: "",
},
});
const [monthlyList, setMonthlyList] = useState<any>();
const editor = useRef(null);
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 reqData = {
planningTypeId: 2,
title: data.title,
time: "2",
description: data.detail,
username: "",
date: `${getOnlyDate(data.week.from)} - ${getOnlyDate(data.week.to)}`,
status: "Open",
parentId: Number(data.parentId),
};
console.log("req", reqData);
const response = await savePlanning(reqData);
close();
if (response.error) {
error(response.message);
return false;
}
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/curator/task-plan/medsos-mediahub");
}
});
};
useEffect(() => {
getMonthlyPlanning();
}, []);
async function getMonthlyPlanning() {
const res = await getMonthlyPlanList(new Date().getDate(), 2);
if (res.data !== null) {
const rawUser = res.data?.data;
const optionArr = rawUser.map((option: any) => ({
id: option.id,
label: option.title,
value: String(option.id),
}));
console.log("ssss", optionArr);
setMonthlyList(optionArr);
}
}
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-col gap-4">
<div className="flex flex-row justify-center gap-5 mt-10">
<Link
href="/curator/task-plan/medsos-mediahub/create-monthly"
className="bg-slate-200 text-black rounded-full px-5 py-2 text-sm"
>
Bulanan
</Link>
<div className="bg-primary rounded-full px-5 py-2 text-white text-sm">
Mingguan
</div>
<Link
href="/curator/task-plan/medsos-mediahub/create-daily"
className="bg-slate-200 text-black rounded-full px-5 py-2 text-sm"
>
Harian
</Link>
</div>
<div className="flex flex-col bg-white gap-2 p-6">
<p className="text-lg">Perencanaan MediaHub Mingguan</p>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Judul Perencanaan</FormLabel>
<Input
value={field.value}
placeholder="Masukkan Judul Perencanaan"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="week"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Tanggal</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value?.from ? (
field.value.to ? (
<>
{format(field.value.from, "LLL dd, y")} -{" "}
{format(field.value.to, "LLL dd, y")}
</>
) : (
format(field.value.from, "LLL dd, y")
)
) : (
<span>Masukkan Tanggal</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="range"
selected={field.value}
onSelect={field.onChange}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="parentId"
render={({ field }) => (
<FormItem>
<FormLabel>Perencanaan Bulanan</FormLabel>
<Select value={field.value} onValueChange={field.onChange}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{monthlyList?.map((list: any) => (
<SelectItem key={list.id} value={list.value}>
{list.label}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="detail"
render={({ field }) => (
<FormItem>
<FormLabel>Detail Perencanaan</FormLabel>
<JoditEditor
ref={editor}
value={field.value}
className="dark:text-black"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-row gap-2 justify-end mt-4 pt-4">
<Button type="submit" variant="outline" color="destructive">
Cancel
</Button>
<Button type="submit" color="primary">
Submit
</Button>
</div>
</form>
</Form>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,270 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { close, loading } from "@/config/swal";
import { getWeeklyPlanList } from "@/service/agenda-setting/agenda-setting";
import { getPlanningById } from "@/service/planning/planning";
import { useParams } from "next/navigation";
import dayjs from "dayjs";
import { useEffect, useRef, useState } from "react";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import JoditEditor from "jodit-react";
export default function DetailTaskPlanMediahub() {
const params = useParams();
const id = params?.id;
const editor = useRef(null);
const [planningData, setPlanningData] = useState<any>();
const [type, setType] = useState("");
const [weeklyList, setWeeklyList] = useState<any>([]);
const [weeklySelected, setWeeklySelected] = useState();
const [taskOutput, setTaskOutput] = useState<any>([]);
const [destination, setDestination] = useState<any>([]);
const [listDest, setListDest] = useState([]);
const [topDestination, setTopDestination] = useState<any>([]);
useEffect(() => {
initFetch();
}, [id]);
async function initFetch() {
if (id != undefined) {
loading();
const res = await getPlanningById(id);
close();
if (res?.data?.data != undefined) {
const data = res?.data?.data;
console.log("Data :", data);
setPlanningData(data);
setAssignedTopLevel(data?.assignedToTopLevel);
setArrayDestination(data?.assignedToLevel);
setArrayTaskOutput(data?.fileTypeOutput);
setType(String(data?.assignmentTypeId));
}
}
}
function setArrayDestination(assignedToLevel: any) {
if (assignedToLevel?.length > 0) {
const arrayDestination = [];
const arrayDest = assignedToLevel.split(",");
for (const element of arrayDest) {
arrayDestination.push(element);
}
setDestination(arrayDestination);
}
}
function setAssignedTopLevel(assignedToTopLevel: any) {
if (assignedToTopLevel?.length > 0) {
const arrayTopLevel = [];
const arrayTop = assignedToTopLevel.split(",");
for (const element of arrayTop) {
arrayTopLevel.push(Number(element));
}
setTopDestination(arrayTopLevel);
}
}
function setArrayTaskOutput(output: any) {
if (output?.length > 0) {
const arrayOutput = [];
const arrOutput = output.split(",");
for (const element of arrOutput) {
arrayOutput.push(Number(element));
}
setTaskOutput(arrayOutput);
}
}
useEffect(() => {
getWeeklyPlanning();
}, [planningData]);
async function getWeeklyPlanning() {
const TODAY = dayjs().format("YYYY-MM-DD");
const res = await getWeeklyPlanList(planningData?.date || TODAY, 1);
if (res.data !== null) {
const rawUser = res.data?.data;
const optionArr = rawUser.map((option: any) => ({
id: option.id,
label: option.title,
value: option.id,
}));
console.log("res", optionArr);
setWeeklyList(optionArr);
}
}
return (
<>
<SiteBreadcrumb />
<div className="text-black flex flex-col gap-4">
<p className="text-lg">Perencanaan Mediahub</p>
<p className="text-sm">Judul Perencanaan</p>
<Input value={planningData?.title} readOnly />
<p className="text-sm">Output Tugas</p>
<div className="flex flex-row gap-3">
<div className="flex items-center space-x-2">
<Checkbox
id="all"
checked={taskOutput ? taskOutput.length == 4 : false}
/>
<label
htmlFor="all"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Semua
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="all"
checked={taskOutput ? taskOutput.includes(2) : false}
/>
<label
htmlFor="all"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Audio Visual
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="all"
checked={taskOutput ? taskOutput.includes(4) : false}
/>
<label
htmlFor="all"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Audio
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="all"
checked={taskOutput ? taskOutput.includes(1) : false}
/>
<label
htmlFor="all"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Foto
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="all"
checked={taskOutput ? taskOutput.includes(3) : false}
/>
<label
htmlFor="all"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Text
</label>
</div>
</div>
<p className="text-sm">Pelaksana Tugas</p>
<div className="flex flex-row gap-3">
<div className="flex items-center space-x-2">
<Checkbox
id="all"
checked={topDestination ? topDestination.includes(0) : false}
/>
<label
htmlFor="all"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Semua Unit
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="all"
checked={topDestination ? topDestination.includes(1) : false}
/>
<label
htmlFor="all"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Mabes Polri
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="all"
checked={topDestination ? topDestination.includes(2) : false}
/>
<label
htmlFor="all"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Polda
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="all"
checked={topDestination ? topDestination.includes(3) : false}
/>
<label
htmlFor="all"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Polres
</label>
</div>
</div>
<p className="text-sm">Jenis Penugasan</p>
<RadioGroup className="flex flex-row" disabled>
<div className="flex items-center space-x-2">
<RadioGroupItem value="1" id="1" checked={type === "1"} />
<Label htmlFor="1">Publikasi {type}</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="2" id="2" checked={type === "2"} />
<Label htmlFor="2">Amplifikasi</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="3" id="3" checked={type === "3"} />
<Label htmlFor="3">Kontra</Label>
</div>
</RadioGroup>
<p className="text-sm">Tanggal</p>
<Input value={planningData?.date} className="w-fit" readOnly />
<p className="text-sm">Penugasan Mingguan</p>
<Input value={weeklyList[0]?.label} readOnly />
<JoditEditor
ref={editor}
value={planningData?.description}
className="dark:text-black"
config={{ readonly: true, toolbar: false }}
/>
</div>
</>
);
}

View File

@ -1,9 +1,41 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import ListViewSocialMediaTable from "@/components/table/task-plan/list-view-social-media-table";
import SingleViewSocialMediaTable from "@/components/table/task-plan/single-view-social-media-table";
import { Button } from "@/components/ui/button";
import { useRouter } from "@/i18n/routing";
import { useState } from "react";
export default function TaskPlanMedsosMediaHub() {
export default function TaskPlanSocialMediaMediaHub() {
const router = useRouter();
const [view, setView] = useState("single");
return (
<div>
<>
<SiteBreadcrumb />
<div className="flex flex-col gap-4">
<div className="flex justify-between">
<Button
onClick={() =>
view === "single" ? setView("list") : setView("single")
}
>
{view == "single" ? "List" : "Single"} View
</Button>
<Button
color="primary"
onClick={() =>
router.push("/curator/task-plan/medsos-mediahub/create-monthly")
}
>
Buat Perencaan
</Button>
</div>
{view == "single" ? (
<SingleViewSocialMediaTable />
) : (
<ListViewSocialMediaTable />
)}
</div>
</>
);
}

View File

@ -49,10 +49,25 @@ const DetailAudio = () => {
<BarWave color="#ffc831" width="60px" height="25px" duration="2s" />
<div className="absolute top-4 left-4"></div>
</div>
{/* Footer Informasi */}
<div className="p-4 text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<p className="flex flex-row items-center">
oleh&nbsp;<span className="font-semibold text-black">{detailDataAudio?.uploadedBy?.userLevel?.name}</span>&nbsp;|&nbsp;Diupdate pada {detailDataAudio?.updatedAt} WIB&nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp;
{detailDataAudio?.clickCount}
</p>
<p>Kreator: {detailDataAudio?.creatorName}</p>
</div>
{/* Keterangan */}
<div className="md:w-3/4">
<h1 className="flex flex-row font-bold text-2xl mx-5 my-8">{detailDataAudio?.title}</h1>
<div className="font-light text-justify" dangerouslySetInnerHTML={{ __html: detailDataAudio?.htmlDescription }} />
</div>
</div>
{/* Bagian Kanan */}
<div className="md:w-1/4 p-4 bg-gray-300 rounded-lg mx-4">
<div className="md:w-1/4 p-4 h-fit bg-gray-300 rounded-lg mx-4">
<div className="flex flex-col mb-3 items-center justify-center cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" width="2.5em" height="2.5em" viewBox="0 0 24 24">
<path fill="black" d="m17 18l-5-2.18L7 18V5h10m0-2H7a2 2 0 0 0-2 2v16l7-3l7 3V5a2 2 0 0 0-2-2" />
@ -106,24 +121,8 @@ const DetailAudio = () => {
</button>
</div>
</div>
{/* Footer Informasi */}
<div className="p-4 text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<p className="flex flex-row items-center">
oleh&nbsp;<span className="font-semibold text-black">{detailDataAudio?.uploadedBy?.userLevel?.name}</span>&nbsp;|&nbsp;Diupdate pada {detailDataAudio?.updatedAt} WIB&nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp;
{detailDataAudio?.clickCount}
</p>
<p>Kreator: {detailDataAudio?.creatorName}</p>
</div>
{/* Keterangan */}
<div className="md:w-3/4">
<h1 className="flex flex-row font-bold text-2xl mx-5 my-8">{detailDataAudio?.title}</h1>
<div dangerouslySetInnerHTML={{ __html: detailDataAudio?.htmlDescription }} />
</div>
</div>
<div className="w-full mb-8">
{/* Comment */}
<div className="flex flex-col my-16 gap-5 p-10 bg-gray-300">

View File

@ -4,11 +4,9 @@ import { useParams, usePathname, useRouter } from "next/navigation";
import React, { useEffect, useState } from "react";
import { Icon } from "@iconify/react/dist/iconify.js";
import { getDetail } from "@/service/landing/landing";
import VideoPlayer from "@/utils/video-player";
import NewContent from "@/components/landing-page/new-content";
import { Link } from "@/i18n/routing";
import { Textarea } from "@/components/ui/textarea";
import { BarWave } from "react-cssfx-loading";
const DetailDocument = () => {
const [selectedSize, setSelectedSize] = useState<string>("L");
@ -49,19 +47,19 @@ const DetailDocument = () => {
<div className="absolute top-4 left-4"></div>
</div>
{/* Footer Informasi */}
<div className="p-4 text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<p className="flex flex-row items-center">
<div className="text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<p className="flex flex-row items-center mt-3">
oleh&nbsp;<span className="font-semibold text-black">{detailDataDocument?.uploadedBy?.userLevel?.name}</span>&nbsp;|&nbsp;Diupdate pada {detailDataDocument?.updatedAt} WIB&nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp;
{detailDataDocument?.clickCount}
</p>
<p>Kreator: {detailDataDocument?.creatorName}</p>
<p className="mt-3">Kreator: {detailDataDocument?.creatorName}</p>
</div>
{/* Keterangan */}
<div className="md:w-3/4">
<h1 className="flex flex-row font-bold text-2xl mx-5 my-8">{detailDataDocument?.title}</h1>
<h1 className="flex flex-row font-bold text-2xl my-8">{detailDataDocument?.title}</h1>
<div dangerouslySetInnerHTML={{ __html: detailDataDocument?.htmlDescription }} />
</div>
</div>
@ -123,15 +121,15 @@ const DetailDocument = () => {
<div className="w-full mb-8">
{/* Comment */}
<div className="flex flex-col my-16 p-10 bg-[#f7f7f7]">
<div className="gap-5 flex flex-col px-4 lg:px-20">
<div className="gap-5 flex flex-col px-4 lg:px-16">
<p className="flex items-start text-lg">Berikan Komentar</p>
<Textarea placeholder="Type your comments here." className="flex w-full" />
<button className="flex items-start bg-[#bb3523] rounded-lg w-fit px-4 py-1">Kirim</button>
<button className="flex items-start bg-[#bb3523] rounded-lg text-white w-fit px-4 py-1">Kirim</button>
</div>
</div>
{/* Konten Serupa */}
<div className="px-4">
<div className="">
<NewContent type={"similar"} />
</div>
</div>

View File

@ -1,15 +1,19 @@
"use client";
import React, { useEffect, useState } from "react";
import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "@/components/ui/pagination";
import { Card, CardContent } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Icon } from "@iconify/react/dist/iconify.js";
import { getListContent } from "@/service/landing/landing";
import { formatDateToIndonesian } from "@/utils/globals";
import { useParams, usePathname, useRouter, useSearchParams } from "next/navigation";
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
import { formatDateToIndonesian, getOnlyDate, getOnlyMonthAndYear } from "@/utils/globals";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import { getListContent, getUserLevelListByParent, listCategory, listData, listDataRegional } from "@/service/landing/landing";
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
import LandingPagination from "@/components/landing-page/pagination";
import { Reveal } from "@/components/landing-page/Reveal";
import { Link } from "@/i18n/routing";
import { Link, useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import ReactDatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { close, loading } from "@/config/swal";
const columns: ColumnDef<any>[] = [
{
@ -19,35 +23,13 @@ const columns: ColumnDef<any>[] = [
},
];
const categories = [
{ id: 1, title: "HUT HUMAS KE - 73" },
{ id: 2, title: "OPERASI ZEBRA 2024" },
{ id: 3, title: "PON XXI" },
{ id: 4, title: "OPS LILIN NATARU 2024" },
{ id: 5, title: "HUT HUMAS KE - 72" },
{ id: 6, title: "OPS MANTAP PRAJA & PILKADA 2024" },
{ id: 6, title: "OPS KETUPAT 2024" },
{ id: 6, title: "OPS PATUH 2024" },
{ id: 6, title: "HARI JUANG POLRI" },
{ id: 6, title: "HUT RI KE-79" },
{ id: 6, title: "HARI BHAYANGKARA KE-78" },
];
const formatAudio = [
{ id: 1, title: "DOC" },
{ id: 2, title: "DOCX" },
{ id: 3, title: "PDF" },
{ id: 4, title: "PPT" },
{ id: 5, title: "PPTX" },
];
const DocumentPage = () => {
const FilterPage = () => {
const router = useRouter();
const pathname = usePathname();
const asPath = usePathname();
const params = useParams();
const searchParams = useSearchParams();
const locale = params?.locale;
const [imageData, setImageData] = useState<any>();
const [documentData, setDocumentData] = useState<any>();
const [totalData, setTotalData] = React.useState<number>(1);
const [totalPage, setTotalPage] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
@ -56,9 +38,36 @@ const DocumentPage = () => {
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 6,
pageSize: 10,
});
const [page, setPage] = useState(1);
const [totalContent, setTotalContent] = useState();
const [change, setChange] = useState(false);
const sortBy = searchParams?.get("sortBy");
const title = searchParams?.get("title");
const categorie = searchParams?.get("category");
const group = searchParams?.get("group");
const [, setGetTotalPage] = useState();
let typingTimer: any;
const doneTypingInterval = 1500;
const [contentDocument, setContentDocument] = useState([]);
const [categoryFilter, setCategoryFilter] = useState<any>([]);
const [monthYearFilter, setMonthYearFilter] = useState<any>();
const [searchTitle, setSearchTitle] = useState<string>("");
const [sortByOpt, setSortByOpt] = useState<any>(sortBy === "popular" ? "clickCount" : "createdAt");
const isRegional = asPath?.includes("regional");
const isSatker = asPath?.includes("satker");
const [formatFilter, setFormatFilter] = useState<any>([]);
const pages = page ? page - 1 : 0;
const [startDateString, setStartDateString] = useState<any>();
const [endDateString, setEndDateString] = useState<any>();
const [dateRange, setDateRange] = useState<any>([null, null]);
const [calenderState, setCalenderState] = useState(false);
const [handleClose, setHandleClose] = useState(false);
const [categories, setCategories] = useState([]);
const [userLevels, setUserLevels] = useState([]);
// const [startDate, endDate] = dateRange;
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
@ -67,8 +76,191 @@ const DocumentPage = () => {
}
}, [searchParams]);
useEffect(() => {
async function initState() {
getCategories();
// getSelectedCategory();
if (isSatker) {
getUserLevels();
}
}
initState();
}, []);
useEffect(() => {
if (categorie) {
setCategoryFilter(categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
console.log("Kategori", categorie, categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
}
}, [categorie]);
// useEffect(() => {
// fetchData();
// }, [page, sortBy, sortByOpt, title]);
useEffect(() => {
async function initState() {
if (isRegional) {
getDataRegional();
} else {
getDataAll();
}
}
console.log(monthYearFilter, "monthFilter");
initState();
}, [change, asPath, monthYearFilter, page, sortBy, sortByOpt, title, startDateString, endDateString, categorie, formatFilter]);
async function getCategories() {
const category = await listCategory("3");
const resCategory = category?.data?.data?.content;
setCategories(resCategory);
}
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
setStartDateString(getOnlyDate(dateRange[0]));
setEndDateString(getOnlyDate(dateRange[1]));
setHandleClose(true);
console.log("date range", dateRange, getOnlyDate(dateRange[0]));
}
}
initState();
}, [calenderState]);
async function getDataAll() {
if (asPath?.includes("/polda/") == true) {
if (asPath?.split("/")[2] !== "[polda_name]") {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
const filterGroup = group == undefined ? asPath.split("/")[2] : group;
loading();
const response = await listData(
"3",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
filterGroup,
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
// setGetTotalPage(response.data?.data?.totalPages);
// setContentImage(response.data?.data?.content);
// setTotalContent(response.data?.data?.totalElements);
const data = response.data?.data;
const contentData = data?.content;
setDocumentData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response.data?.data?.totalElements);
}
} else {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listData(
"3",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
// setGetTotalPage(response.data?.data?.totalPages);
// setContentImage(response.data?.data?.content);
// setTotalContent(response.data?.data?.totalElements);
const data = response.data?.data;
const contentData = data?.content;
setDocumentData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response.data?.data?.totalElements);
}
}
const handleCategoryFilter = (e: boolean, id: string) => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
};
const handleFormatFilter = (e: boolean, id: string) => {
let filter = [...formatFilter];
if (e) {
filter = [...formatFilter, id];
} else {
filter.splice(formatFilter.indexOf(id), 1);
}
console.log("Format filter", filter);
setFormatFilter(filter);
};
const cleanCheckbox = () => {
setCategoryFilter([]);
setFormatFilter([]);
router.push(`?category=&title=`);
setDateRange([null, null]);
setMonthYearFilter(null);
setChange(!change);
};
async function getDataRegional() {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listDataRegional(
"3",
name,
filter,
format,
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
12,
pages,
sortByOpt
);
close();
setGetTotalPage(response.data?.data?.totalPages);
setContentDocument(response.data?.data?.content);
setTotalContent(response.data?.data?.totalElements);
}
const table = useReactTable({
data: imageData,
data: documentData,
columns: columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
@ -88,10 +280,9 @@ const DocumentPage = () => {
},
});
const [documentData, setDocumentData] = useState<any>();
useEffect(() => {
initFetch();
}, []);
}, [page]);
const initFetch = async () => {
const response = await getListContent({ page: page - 1, size: 6, sortBy: "createdAt", contentTypeId: "3" });
console.log(response);
@ -102,81 +293,199 @@ const DocumentPage = () => {
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
};
function getSelectedCategory() {
const filter = [];
if (categorie) {
const categoryArr = categorie.split(",");
for (const element of categoryArr) {
filter.push(Number(element));
}
setCategoryFilter(filter);
}
}
const handleDeleteDate = () => {
setDateRange([null, null]);
setStartDateString("");
setEndDateString("");
setHandleClose(false);
};
const handleSorting = (e: any) => {
console.log(e.target.value);
if (e.target.value == "terbaru") {
setSortByOpt("createdAt");
} else {
setSortByOpt("clickCount");
}
setChange(!change);
};
async function getUserLevels() {
const res = await getUserLevelListByParent(761);
const userLevelList = res?.data?.data;
if (userLevelList !== null) {
let optionArr: any = [];
userLevelList?.map((option: any) => {
let optionData = {
id: option.id,
label: option.name,
value: option.id,
};
optionArr.push(optionData);
});
setUserLevels(optionArr);
}
}
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {
if (searchTitle == "" || searchTitle == undefined) {
router.push("?title=");
} else {
router.push(`?title=${searchTitle}`);
}
}
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
return (
<div className="flex flex-col">
{/* Header */}
<div className="flex flex-col md:flex-row items-start gap-5 p-10 bg-[#f7f7f7] dark:bg-black">
<p>
{" "}
Teks {">"} <span className="font-bold">Semua Teks</span>
</p>
<p className="font-bold">|</p>
<p>Terdapat 32499 artikel berisi Teks yang dapat diunduh </p>
<p>{`Terdapat ${totalContent} artikel berisi Teks yang dapat diunduh`}</p>
</div>
{/* Left */}
<div className="flex flex-col lg:flex-row gap-6 p-4">
{/* Sidebar Kiri */}
<div className="lg:w-1/4 w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
<h2 className="text-xl font-semibold mb-4 flex items-center gap-1">
<div className="lg:w-[40%] w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
<Icon icon="stash:filter-light" fontSize={30} />
Filter
</h2>
<div className="border-t border-black dark:border-white my-4"></div>
<div className="border-t border-black my-4 dark:border-white"></div>
<div className="space-y-6">
{/* Pencarian */}
<div>
<label htmlFor="search" className="block text-sm font-medium text-gray-700 dark:text-white">
Pencarian
</label>
<input type="text" id="search" placeholder="Cari judul..." className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500" />
<Input
value={searchTitle}
onChange={(e) => setSearchTitle(e.target.value)}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
type="text"
id="search"
placeholder="Cari judul..."
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
/>
</div>
{/* Tahun & Bulan */}
<div>
<label htmlFor="month" className="block text-sm font-medium text-gray-700 dark:text-white">
Pilih Tahun & Bulan
</label>
<input type="month" id="month" className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500" />
<label className="block text-sm font-medium text-gray-700 dark:text-white">Tahun & Bulan</label>
<ReactDatePicker
selected={monthYearFilter}
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
onChange={(date) => setMonthYearFilter(date)}
dateFormat="MM | yyyy"
placeholderText="Pilih Tahun dan Bulan"
showMonthYearPicker
/>
</div>
{/* Tanggal */}
<div>
<label htmlFor="date" className="block text-sm font-medium text-gray-700 dark:text-white">
Pilih Tanggal
</label>
<input type="date" id="date" className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500" />
<label className="block text-sm font-medium text-gray-700 dark:text-white">Tanggal</label>
<div className="flex flex-row justify justify-between gap-2">
<ReactDatePicker
selectsRange
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
startDate={dateRange[0]}
endDate={dateRange[1]}
onChange={(update) => {
setDateRange(update);
}}
placeholderText="Pilih Tanggal"
onCalendarClose={() => setCalenderState(!calenderState)}
/>
<div className="flex items-center">{handleClose ? <Icon icon="carbon:close-filled" onClick={handleDeleteDate} width="20" inline color="#216ba5" /> : ""}</div>
</div>
{/* Kategori */}
</div>
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Kategori</h3>
<ul className="mt-2 space-y-2">
{categories.map((category) => (
{categories.map((category: any) => (
<li key={category?.id}>
<label className="inline-flex items-center">
<Checkbox id="terms" />
<span className="ml-2 text-gray-700 dark:text-white">{category.title}</span>
<label className="inline-flex items-center" htmlFor={`${category.id}`}>
<Checkbox id={`${category.id}`} value={category.id} checked={categoryFilter.includes(String(category.id))} onCheckedChange={(e) => handleCategoryFilter(Boolean(e), category.id)} />
<span className="ml-2 text-gray-700 dark:text-white">{category?.name}</span>
</label>
</li>
))}
</ul>
</div>
{/* Garis */}
<div className="border-t border-black dark:border-white my-4"></div>
<div className="border-t border-black my-4 dark:border-white"></div>
{/* Garis */}
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Format Foto</h3>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Format Teks</h3>
<ul className="mt-2 space-y-2">
{formatAudio.map((format) => (
<li key={format?.id}>
<li>
<label className="inline-flex items-center">
<Checkbox id="terms" />
<span className="ml-2 text-gray-700 dark:text-white">{format.title}</span>
<Checkbox id="doc" value="doc" checked={formatFilter.includes("doc")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "doc")} />
<span className="ml-2 text-gray-700 dark:text-white">DOC</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="docx" value="docx" checked={formatFilter.includes("docx")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "docx")} />
<span className="ml-2 text-gray-700 dark:text-white">DOCX</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="pdf" value="pdf" checked={formatFilter.includes("pdf")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "pdf")} />
<span className="ml-2 text-gray-700 dark:text-white">PDF</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="ppt" value="ppt" checked={formatFilter.includes("ppt")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "ppt")} />
<span className="ml-2 text-gray-700 dark:text-white">PPT</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="pptx" value="pptx" checked={formatFilter.includes("pptx")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "pptx")} />
<span className="ml-2 text-gray-700 dark:text-white">PPTX</span>
</label>
</li>
))}
</ul>
</div>
<div className="border-t border-black dark:border-white my-4"></div>
<div className="text-center">
<a href="#" className="text-[#bb3523]">
<a onClick={cleanCheckbox} className="text-[#bb3523] cursor-pointer">
<b>Reset Filter</b>
</a>
</div>
@ -184,20 +493,16 @@ const DocumentPage = () => {
</div>
{/* Konten Kanan */}
<div className="flex-1">
<Reveal>
<div className="flex-1">
<div className="flex flex-col items-end mb-4">
<h2 className="text-lg font-semibold tetx-red-300">Urutkan berdasarkan</h2>
<select className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
<h2 className="text-lg font-semibold">Urutkan berdasarkan</h2>
<select defaultValue={sortBy == "popular" ? "terpopuler" : "terbaru"} onChange={(e) => handleSorting(e)} className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
<option value="terbaru">Terbaru</option>
<option value="terlama">Terpopuler</option>
<option value="terpopuler">Terpopuler</option>
</select>
</div>
</Reveal>
{/* Card */}
<Reveal>
<div className=" grid grid-cols-1 md:grid-cols-2 gap-6">
{documentData?.map((document: any) => (
<Link href={`/document/detail/${document?.slug}`} key={document?.id} className="flex flex-col bg-yellow-500 sm:flex-row items-center dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full">
@ -226,11 +531,11 @@ const DocumentPage = () => {
))}
</div>
<LandingPagination table={table} totalData={totalData} totalPage={totalPage} />
</Reveal>
</div>
</Reveal>
</div>
</div>
);
};
export default DocumentPage;
export default FilterPage;

View File

@ -49,17 +49,17 @@ const DetailInfo = () => {
</div>
{/* Gambar bawah Kecil */}
<div className="p-4 grid grid-cols-4 gap-2">
<div className="py-4 flex flex-row gap-3">
{detailDataImage?.files?.map((file: any, index: number) => (
<a onClick={() => setSelectedImage(index)} key={file?.id}>
<img src={file?.url} className="w-full h-fit object-cover rounded-md cursor-pointer hover:ring-2 hover:ring-red-600" />
<img src={file?.url} className="w-[120px] h-[90px] object-cover rounded-md cursor-pointer hover:ring-2 hover:ring-red-600" />
</a>
))}
</div>
{/* Footer Informasi */}
<div className="p-4 text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<div className="flex flex-row items-center">
<div className="text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<div className="flex flex-row items-center mt-3 justify-between">
oleh&nbsp;<span className="font-semibold text-black">{detailDataImage?.uploadedBy?.userLevel?.name}</span>&nbsp; | &nbsp;Diupdate pada {detailDataImage?.updatedAt} WIB &nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp; {detailDataImage?.clickCount} &nbsp;
@ -69,8 +69,8 @@ const DetailInfo = () => {
{/* Keterangan */}
<div className="w-full">
<h1 className="flex flex-row font-bold text-2xl mx-5 my-8">{detailDataImage?.title}</h1>
<div dangerouslySetInnerHTML={{ __html: detailDataImage?.htmlDescription }} />
<h1 className="flex flex-row font-bold text-2xl my-8">{detailDataImage?.title}</h1>
<div className="font-light text-justify" dangerouslySetInnerHTML={{ __html: detailDataImage?.htmlDescription }} />
</div>
</div>
@ -134,15 +134,15 @@ const DetailInfo = () => {
<div className="w-full mb-8">
{/* Comment */}
<div className="flex flex-col my-16 p-10 bg-[#f7f7f7]">
<div className="gap-5 flex flex-col px-4 lg:px-16">
<div className="gap-5 flex flex-col px-4 lg:px-14">
<p className="flex items-start text-lg">Berikan Komentar</p>
<Textarea placeholder="Type your comments here." className="flex w-full" />
<button className="flex items-start bg-[#bb3523] rounded-lg w-fit px-4 py-1">Kirim</button>
<button className="flex items-start bg-[#bb3523] rounded-lg w-fit text-white px-4 py-1">Kirim</button>
</div>
</div>
{/* Konten Serupa */}
<div className="px-4">
<div className="">
<NewContent type={"similar"} />
</div>
</div>

View File

@ -3,14 +3,17 @@ import React, { useEffect, useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Icon } from "@iconify/react/dist/iconify.js";
import { formatDateToIndonesian } from "@/utils/globals";
import { useParams, usePathname, useRouter, useSearchParams } from "next/navigation";
import { getListContent, listCategory } from "@/service/landing/landing";
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
import { formatDateToIndonesian, getOnlyDate, getOnlyMonthAndYear } from "@/utils/globals";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import { getUserLevelListByParent, listCategory, listData, listDataRegional } from "@/service/landing/landing";
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
import LandingPagination from "@/components/landing-page/pagination";
import { Reveal } from "@/components/landing-page/Reveal";
import { Link } from "@/i18n/routing";
import { Link, useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import ReactDatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { close, loading } from "@/config/swal";
const columns: ColumnDef<any>[] = [
{
@ -20,25 +23,6 @@ const columns: ColumnDef<any>[] = [
},
];
const categories = [
{ id: 1, title: "HUT HUMAS KE - 73" },
{ id: 2, title: "OPERASI ZEBRA 2024" },
{ id: 3, title: "PON XXI" },
{ id: 4, title: "OPS LILIN NATARU 2024" },
{ id: 5, title: "HUT HUMAS KE - 72" },
{ id: 6, title: "OPS MANTAP PRAJA & PILKADA 2024" },
{ id: 7, title: "OPS KETUPAT 2024" },
{ id: 8, title: "OPS PATUH 2024" },
{ id: 9, title: "HARI JUANG POLRI" },
{ id: 10, title: "HUT RI KE-79" },
{ id: 11, title: "HARI BHAYANGKARA KE-78" },
];
const formatPicture = [
{ id: 1, title: "PNG" },
{ id: 2, title: "JPEG" },
{ id: 3, title: "JPG" },
];
const FilterPage = () => {
const router = useRouter();
const asPath = usePathname();
@ -58,12 +42,33 @@ const FilterPage = () => {
});
const [page, setPage] = useState(1);
const [totalContent, setTotalContent] = useState();
const [change, setChange] = useState(false);
const sortBy = searchParams?.get("sortBy");
const title = searchParams?.get("title");
const categorie = searchParams?.get("category");
const group = searchParams?.get("group");
const [contentImage, setContentImage] = useState([]);
const [, setGetTotalPage] = useState();
const [categories, setCategories] = useState<any>([]);
let typingTimer: any;
const doneTypingInterval = 1500;
const [search, setSearch] = useState();
const [categoryFilter, setCategoryFilter] = useState<any>([]);
const [monthYearFilter, setMonthYearFilter] = useState<any>();
const [searchTitle, setSearchTitle] = useState<string>("");
const [sortByOpt, setSortByOpt] = useState<any>(sortBy === "popular" ? "clickCount" : "createdAt");
const isRegional = asPath?.includes("regional");
const isSatker = asPath?.includes("satker");
const [formatFilter, setFormatFilter] = useState<any>([]);
const pages = page ? page - 1 : 0;
const [startDateString, setStartDateString] = useState<any>();
const [endDateString, setEndDateString] = useState<any>();
const [dateRange, setDateRange] = useState<any>([null, null]);
const [calenderState, setCalenderState] = useState(false);
const [handleClose, setHandleClose] = useState(false);
const [categories, setCategories] = useState([]);
const [userLevels, setUserLevels] = useState([]);
// const [startDate, endDate] = dateRange;
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
@ -73,20 +78,188 @@ const FilterPage = () => {
}, [searchParams]);
useEffect(() => {
fetchData();
}, [page]);
async function initState() {
getCategories();
// getSelectedCategory();
if (isSatker) {
getUserLevels();
}
}
const fetchData = async () => {
const response = await getListContent({ page: page - 1, size: 6, sortBy: "createdAt", contentTypeId: "1" });
console.log(response);
initState();
}, []);
useEffect(() => {
if (categorie) {
setCategoryFilter(categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
console.log("Kategori", categorie, categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
}
}, [categorie]);
// useEffect(() => {
// fetchData();
// }, [page, sortBy, sortByOpt, title]);
useEffect(() => {
async function initState() {
if (isRegional) {
getDataRegional();
} else {
getDataAll();
}
}
console.log(monthYearFilter, "monthFilter");
initState();
}, [change, asPath, monthYearFilter, page, sortBy, sortByOpt, title, startDateString, endDateString, categorie, formatFilter]);
async function getCategories() {
const category = await listCategory("1");
const resCategory = category?.data?.data?.content;
setCategories(resCategory);
}
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
setStartDateString(getOnlyDate(dateRange[0]));
setEndDateString(getOnlyDate(dateRange[1]));
setHandleClose(true);
console.log("date range", dateRange, getOnlyDate(dateRange[0]));
}
}
initState();
}, [calenderState]);
async function getDataAll() {
if (asPath?.includes("/polda/") == true) {
if (asPath?.split("/")[2] !== "[polda_name]") {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
const filterGroup = group == undefined ? asPath.split("/")[2] : group;
loading();
const response = await listData(
"1",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
filterGroup,
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
// setGetTotalPage(response.data?.data?.totalPages);
// setContentImage(response.data?.data?.content);
// setTotalContent(response.data?.data?.totalElements);
const data = response.data?.data;
const contentData = data?.content;
setImageData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response.data?.data?.totalElements);
}
} else {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listData(
"1",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
// setGetTotalPage(response.data?.data?.totalPages);
// setContentImage(response.data?.data?.content);
// setTotalContent(response.data?.data?.totalElements);
const data = response.data?.data;
const contentData = data?.content;
setImageData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response.data?.data?.totalElements);
}
}
const handleCategoryFilter = (e: boolean, id: string) => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
};
const handleFormatFilter = (e: boolean, id: string) => {
let filter = [...formatFilter];
if (e) {
filter = [...formatFilter, id];
} else {
filter.splice(formatFilter.indexOf(id), 1);
}
console.log("Format filter", filter);
setFormatFilter(filter);
};
const cleanCheckbox = () => {
setCategoryFilter([]);
setFormatFilter([]);
router.push(`?category=&title=`);
setDateRange([null, null]);
setMonthYearFilter(null);
setChange(!change);
};
async function getDataRegional() {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listDataRegional(
"1",
name,
filter,
format,
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
12,
pages,
sortByOpt
);
close();
setGetTotalPage(response.data?.data?.totalPages);
setContentImage(response.data?.data?.content);
setTotalContent(response.data?.data?.totalElements);
}
const table = useReactTable({
data: imageData,
columns: columns,
@ -108,25 +281,58 @@ const FilterPage = () => {
},
});
// async function getCategories() {
// const category = await listCategory("1");
// const resCategory = category?.data?.data?.content;
// setCategories(resCategory);
// }
function getSelectedCategory() {
const filter = [];
// function getSelectedCategory() {
// const filter = [];
if (categorie) {
const categoryArr = categorie.split(",");
// if (category) {
// const categoryArr = category.split(",");
for (const element of categoryArr) {
filter.push(Number(element));
}
// for (const element of categoryArr) {
// filter.push(Number(element));
// }
setCategoryFilter(filter);
}
}
// setCategoryFilter(filter);
// }
// }
const handleDeleteDate = () => {
setDateRange([null, null]);
setStartDateString("");
setEndDateString("");
setHandleClose(false);
};
const handleSorting = (e: any) => {
console.log(e.target.value);
if (e.target.value == "terbaru") {
setSortByOpt("createdAt");
} else {
setSortByOpt("clickCount");
}
setChange(!change);
};
async function getUserLevels() {
const res = await getUserLevelListByParent(761);
const userLevelList = res?.data?.data;
if (userLevelList !== null) {
let optionArr: any = [];
userLevelList?.map((option: any) => {
let optionData = {
id: option.id,
label: option.name,
value: option.id,
};
optionArr.push(optionData);
});
setUserLevels(optionArr);
}
}
const handleKeyUp = () => {
clearTimeout(typingTimer);
@ -134,10 +340,10 @@ const FilterPage = () => {
};
async function doneTyping() {
if (search == "" || search == undefined) {
router.push("");
if (searchTitle == "" || searchTitle == undefined) {
router.push("?title=");
} else {
router.push(`?title=${search}`);
router.push(`?title=${searchTitle}`);
}
}
@ -155,12 +361,12 @@ const FilterPage = () => {
Foto {">"} <span className="font-bold">Semua Foto</span>
</p>
<p className="font-bold">|</p>
<p>Terdapat 324911 artikel berisi Foto yang dapat diunduh </p>
<p>{`Terdapat ${totalContent} artikel berisi Foto yang dapat diunduh`}</p>
</div>
{/* Left */}
<div className="flex flex-col lg:flex-row gap-6 p-4">
<div className="lg:w-1/4 w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
<div className="lg:w-[40%] w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
<Icon icon="stash:filter-light" fontSize={30} />
Filter
@ -171,21 +377,46 @@ const FilterPage = () => {
<label htmlFor="search" className="block text-sm font-medium text-gray-700 dark:text-white">
Pencarian
</label>
<Input onKeyUp={handleKeyUp} onKeyDown={handleKeyDown} type="text" id="search" placeholder="Cari judul..." className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500" />
<Input
value={searchTitle}
onChange={(e) => setSearchTitle(e.target.value)}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
type="text"
id="search"
placeholder="Cari judul..."
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
/>
</div>
<div>
<label htmlFor="month" className="block text-sm font-medium text-gray-700 dark:text-white">
Tahun & Bulan
</label>
<input type="month" id="month" className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500" />
<label className="block text-sm font-medium text-gray-700 dark:text-white">Tahun & Bulan</label>
<ReactDatePicker
selected={monthYearFilter}
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
onChange={(date) => setMonthYearFilter(date)}
dateFormat="MM | yyyy"
placeholderText="Pilih Tahun dan Bulan"
showMonthYearPicker
/>
</div>
<div>
<label htmlFor="date" className="block text-sm font-medium text-gray-700 dark:text-white">
Tanggal
</label>
<input type="date" id="date" className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500" />
<label className="block text-sm font-medium text-gray-700 dark:text-white">Tanggal</label>
<div className="flex flex-row justify justify-between gap-2">
<ReactDatePicker
selectsRange
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
startDate={dateRange[0]}
endDate={dateRange[1]}
onChange={(update) => {
setDateRange(update);
}}
placeholderText="Pilih Tanggal"
onCalendarClose={() => setCalenderState(!calenderState)}
/>
<div className="flex items-center">{handleClose ? <Icon icon="carbon:close-filled" onClick={handleDeleteDate} width="20" inline color="#216ba5" /> : ""}</div>
</div>
</div>
<div>
@ -193,9 +424,9 @@ const FilterPage = () => {
<ul className="mt-2 space-y-2">
{categories.map((category: any) => (
<li key={category?.id}>
<label className="inline-flex items-center">
<Checkbox id="terms" />
<span className="ml-2 text-gray-700 dark:text-white">{category?.title}</span>
<label className="inline-flex items-center" htmlFor={`${category.id}`}>
<Checkbox id={`${category.id}`} value={category.id} checked={categoryFilter.includes(String(category.id))} onCheckedChange={(e) => handleCategoryFilter(Boolean(e), category.id)} />
<span className="ml-2 text-gray-700 dark:text-white">{category?.name}</span>
</label>
</li>
))}
@ -207,19 +438,29 @@ const FilterPage = () => {
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Format Foto</h3>
<ul className="mt-2 space-y-2">
{formatPicture.map((format) => (
<li key={format?.id}>
<li>
<label className="inline-flex items-center">
<Checkbox id="terms" />
<span className="ml-2 text-gray-700 dark:text-white">{format.title}</span>
<Checkbox id="png" value="png" checked={formatFilter.includes("png")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "png")} />
<span className="ml-2 text-gray-700 dark:text-white">PNG</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="jpeg" value="jpeg" checked={formatFilter.includes("jpeg")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "jpeg")} />
<span className="ml-2 text-gray-700 dark:text-white">JPEG</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="jpg" value="jpg" checked={formatFilter.includes("jpg")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "jpg")} />
<span className="ml-2 text-gray-700 dark:text-white">JPG</span>
</label>
</li>
))}
</ul>
</div>
<div className="border-t border-black dark:border-white my-4"></div>
<div className="text-center">
<a href="#" className="text-[#bb3523]">
<a onClick={cleanCheckbox} className="text-[#bb3523] cursor-pointer">
<b>Reset Filter</b>
</a>
</div>
@ -231,9 +472,9 @@ const FilterPage = () => {
<div className="flex-1">
<div className="flex flex-col items-end mb-4">
<h2 className="text-lg font-semibold">Urutkan berdasarkan</h2>
<select className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
<select defaultValue={sortBy == "popular" ? "terpopuler" : "terbaru"} onChange={(e) => handleSorting(e)} className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
<option value="terbaru">Terbaru</option>
<option value="terlama">Terlama</option>
<option value="terpopuler">Terpopuler</option>
</select>
</div>

View File

@ -2,69 +2,91 @@
import { Textarea } from "@/components/ui/textarea";
import { useParams, usePathname, useRouter } from "next/navigation";
import React, { useEffect, useState } from "react";
import NewContent from "@/components/landing-page/new-content";
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel";
import { Link } from "@/i18n/routing";
import { getDetailIndeks } from "@/service/landing/landing";
import { getDetailIndeks, publicDetailBlog } from "@/service/landing/landing";
import { formatDateToIndonesian } from "@/utils/globals";
import { Icon } from "@iconify/react/dist/iconify.js";
const IndeksDetail = () => {
const [indeksData, setIndeksData] = useState<any>();
const params = useParams();
const slug = params?.slug;
const [relatedPost, setRelatedPost] = useState<any>();
useEffect(() => {
initFetch();
detailFetch();
}, []);
const initFetch = async () => {
const response = await getDetailIndeks();
console.log(response);
setIndeksData(response?.data?.data?.content);
setRelatedPost(response?.data?.data?.content);
};
const detailFetch = async () => {
const response = await publicDetailBlog(slug);
console.log(response);
setIndeksData(response?.data?.data);
};
return (
<>
<div className="p-4 lg:px-60 lg:p-12">
{/* Judul */}
<div className="flex justify-center mb-5">
<div className="flex flex-col mb-5">
<h1 className="text-lg mb-2">Indeks / Detail</h1>
<h1 className="flex flex-row font-bold text-center text-2xl">{indeksData?.title}</h1>
</div>
{/* Gambar Utama */}
<div className="flex items-center justify-center">
<img src={indeksData?.thumbnailLink} alt="Main" className="h-[500px] w-full rounded-lg" />
<img src={indeksData?.thumbnailLink} alt="Main" className="h-[550px] w-full rounded-lg" />
</div>
{/* Footer Informasi */}
<div className="p-4 text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<p className="items-end">
{formatDateToIndonesian(new Date(indeksData?.createdAt))} {indeksData?.timezone ? indeksData?.timezone : "WIB"}|{" "}
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
/>
</svg>{" "}
{indeksData?.clickCount}
</p>
<div className="text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<div className="flex flex-row items-center mt-3 justify-between">
oleh&nbsp;<span className="font-semibold text-black">{indeksData?.uploaderName}</span>&nbsp; | &nbsp;Diupdate pada {indeksData?.createdAt} WIB &nbsp;
</div>
</div>
{/* Keterangan */}
<div className="w-auto">
<p dangerouslySetInnerHTML={{ __html: indeksData?.description }} />
<p className="font-light text-justify" dangerouslySetInnerHTML={{ __html: indeksData?.description }} />
</div>
</div>
<div className="w-full">
{/* Comment */}
<div className="flex flex-col my-16 p-10 bg-[#f7f7f7]">
<div className="w-full">
<div className="flex flex-col py-5 p-10 bg-[#f7f7f7]">
<div className="gap-5 flex flex-col px-4 lg:px-16">
<p className="flex items-start text-lg">Berikan Komentar</p>
<Textarea placeholder="Type your comments here." className="flex w-full" />
<button className="flex items-start bg-[#bb3523] rounded-lg w-fit px-4 py-1">Kirim</button>
<button className="flex items-start bg-[#bb3523] text-white rounded-lg w-fit px-4 py-1">Kirim</button>
</div>
</div>
{/* Konten Serupa */}
<div className="space-x-5 flex flex-col p-4">
<NewContent type={"similar"} />
<div className="space-x-5 flex flex-col px-4 lg:px-16 py-16 gap-5">
<h1 className="font-bold text-base lg:text-xl px-4 lg:px-8">Post Terkait</h1>
<Carousel>
<CarouselContent className="w-full max-w-7xl">
{relatedPost?.map((relate: any) => (
<CarouselItem key={relate?.id} className="md:basis-1/2 lg:basis-1/3">
<Link href={`/indeks/detail/${relate?.slug}`} className="relative group overflow-hidden shadow-md hover:shadow-lg">
<img src={relate?.thumbnailLink} className="w-full rounded-lg h-40 lg:h-60 object-cover group-hover:scale-100 transition-transform duration-300" />
<div className="absolute bottom-0 left-0 right-0 bg-gray-600 border-l-4 border-[#bb3523] rounded-lg backdrop-blur-sm text-white p-2">
<span className="text-white bg-[#bb3523] rounded-md w-full h-full font-semibold uppercase text-sm px-4 py-1">{relate?.categoryName}</span>
<h1 className="text-sm lg:text-lg mb-2 font-semibold h-5 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">{relate?.title}</h1>
<p className="flex flex-row items-center text-[10px] gap-2">
{formatDateToIndonesian(new Date(relate?.createdAt))} {relate?.timezone ? relate?.timezone : "WIB"} | <Icon icon="formkit:eye" width="15" height="15" /> {relate.clickCount}{" "}
</p>
</div>
</Link>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
</div>
</div>
</>

View File

@ -9,11 +9,11 @@ import { format } from "date-fns";
import { cn } from "@/lib/utils";
import { Checkbox } from "@/components/ui/checkbox";
import { Icon } from "@iconify/react/dist/iconify.js";
import { listSchedule, listScheduleNextPublic, listSchedulePrevPublic, listScheduleTodayPublic } from "@/service/schedule/schedule";
import { detailSchedule, listSchedule, listScheduleNextPublic, listSchedulePrevPublic, listScheduleTodayPublic } from "@/service/schedule/schedule";
import { useRouter } from "@/i18n/routing";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
import { close, loading } from "@/config/swal";
const timeList = [
{
@ -114,7 +114,7 @@ const timeList = [
},
];
const Schedule = () => {
const Schedule = (props: any) => {
const router = useRouter();
const [startDate, setStartDate] = useState<Date | undefined>(new Date());
const [dateAWeek, setDateAWeek] = useState<string[]>([]);
@ -125,6 +125,19 @@ const Schedule = () => {
const [isOpen, setIsOpen] = React.useState(false);
const [schedules, setSchedules] = useState([]);
const [openDialog, setOpenDialog] = useState(false);
const [detail, setDetail] = useState<any>();
const [content, setContent] = useState();
const { id } = props;
useEffect(() => {
async function getDataSchedule() {
const response = await detailSchedule(id);
setDetail(response.data?.data);
setContent(response.data?.data?.files);
}
getDataSchedule();
}, [id]);
useEffect(() => {
async function initState() {
@ -256,7 +269,13 @@ const Schedule = () => {
}
};
const getItem = (itemFound: any) => {
const getItem = async (itemFound: any) => {
loading();
const response = await detailSchedule(itemFound?.id);
setDetail(response.data?.data);
setContent(response.data?.data?.files);
console.log("item Found", itemFound);
close();
setOpenDialog(true);
};
@ -687,29 +706,31 @@ const Schedule = () => {
<AlertDialog open={openDialog} onOpenChange={setOpenDialog}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Test Event</AlertDialogTitle>
<AlertDialogTitle>
<h1 className="my-4 font-light">JADWAL / {detail?.isYoutube == true ? "LIVE STREAMING" : "DETAIL"}</h1>
<p className="font-bold">{detail?.title}</p>
</AlertDialogTitle>
<AlertDialogDescription>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
08.00 - 12.00 WIB
{detail?.date} {detail?.startTime} - {detail?.endTime} {detail?.timezone ? detail.timezone : "WIB"}
</p>
</AlertDialogDescription>
<AlertDialogDescription>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={30} />
Jl. Trunojoyo No.3 2, RT.2/RW.1, Selong, Kec. Kby. Baru, Kota Jakarta Selatan, Daerah Khusus Ibukota Jakarta 12110, Indonesia.
{detail?.address}
</p>
</AlertDialogDescription>
<AlertDialogDescription>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
Hanif Salafi
{detail?.speakerTitle || ""} {detail?.speakerName || ""}{" "}
</p>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Continue</AlertDialogAction>
<AlertDialogAction>Close</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>

View File

@ -49,20 +49,20 @@ const DetailVideo = () => {
</div>
{/* Footer Informasi */}
<div className="p-4 text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<p className="flex flex-row items-center">
<div className="text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<p className="flex flex-row items-center mt-3">
oleh&nbsp;<span className="font-semibold text-black">{detailDataVideo?.uploadedBy?.userLevel?.name}</span>&nbsp;|&nbsp;Diupdate pada {detailDataVideo?.updatedAt} WIB&nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp;
{detailDataVideo?.clickCount}
</p>
<p>Kreator: {detailDataVideo?.creatorName}</p>
<p className="mt-3">Kreator: {detailDataVideo?.creatorName}</p>
</div>
{/* Keterangan */}
<div className="w-full">
<h1 className="flex flex-row font-bold text-2xl mx-5 my-8">{detailDataVideo?.title}</h1>
<div dangerouslySetInnerHTML={{ __html: detailDataVideo?.htmlDescription }} />
<h1 className="flex flex-row font-bold text-2xl my-8">{detailDataVideo?.title}</h1>
<div className="font-light text-justify" dangerouslySetInnerHTML={{ __html: detailDataVideo?.htmlDescription }} />
</div>
</div>
@ -125,15 +125,15 @@ const DetailVideo = () => {
<div className="w-full mb-8">
{/* Comment */}
<div className="flex flex-col my-16 p-10 bg-[#f7f7f7]">
<div className="gap-5 flex flex-col px-4 lg:px-16">
<div className="gap-5 flex flex-col px-4 lg:px-14">
<p className="flex items-start text-lg">Berikan Komentar</p>
<Textarea placeholder="Type your comments here." className="flex w-full" />
<button className="flex items-start bg-[#bb3523] rounded-lg w-fit px-4 py-1">Kirim</button>
<button className="flex items-start bg-[#bb3523] text-white rounded-lg w-fit px-4 py-1">Kirim</button>
</div>
</div>
{/* Konten Serupa */}
<div className="px-4">
<div className="">
<NewContent type={"similar"} />
</div>
</div>

View File

@ -3,13 +3,17 @@ import React, { useEffect, useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Icon } from "@iconify/react/dist/iconify.js";
import { getListContent } from "@/service/landing/landing";
import { formatDateToIndonesian } from "@/utils/globals";
import { useParams, usePathname, useRouter, useSearchParams } from "next/navigation";
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
import { formatDateToIndonesian, getOnlyDate, getOnlyMonthAndYear } from "@/utils/globals";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import { getListContent, getUserLevelListByParent, listCategory, listData, listDataRegional } from "@/service/landing/landing";
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
import LandingPagination from "@/components/landing-page/pagination";
import { Reveal } from "@/components/landing-page/Reveal";
import { Link } from "@/i18n/routing";
import { Link, useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import ReactDatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { close, loading } from "@/config/swal";
const columns: ColumnDef<any>[] = [
{
@ -19,34 +23,13 @@ const columns: ColumnDef<any>[] = [
},
];
const categories = [
{ id: 1, title: "HUT HUMAS KE - 73" },
{ id: 2, title: "OPERASI ZEBRA 2024" },
{ id: 3, title: "PON XXI" },
{ id: 4, title: "OPS LILIN NATARU 2024" },
{ id: 5, title: "HUT HUMAS KE - 72" },
{ id: 6, title: "OPS MANTAP PRAJA & PILKADA 2024" },
{ id: 6, title: "OPS KETUPAT 2024" },
{ id: 6, title: "OPS PATUH 2024" },
{ id: 6, title: "HARI JUANG POLRI" },
{ id: 6, title: "HUT RI KE-79" },
{ id: 6, title: "HARI BHAYANGKARA KE-78" },
];
const formatPicture = [
{ id: 1, title: "MK4" },
{ id: 2, title: "MOV" },
{ id: 3, title: "MP4" },
{ id: 3, title: "AVI" },
{ id: 3, title: "WMV" },
];
const FilterPage = () => {
const router = useRouter();
const pathname = usePathname();
const asPath = usePathname();
const params = useParams();
const searchParams = useSearchParams();
const locale = params?.locale;
const [imageData, setImageData] = useState<any>();
const [videoData, setVideoData] = useState<any>();
const [totalData, setTotalData] = React.useState<number>(1);
const [totalPage, setTotalPage] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
@ -55,9 +38,36 @@ const FilterPage = () => {
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 6,
pageSize: 10,
});
const [page, setPage] = useState(1);
const [totalContent, setTotalContent] = useState();
const [change, setChange] = useState(false);
const sortBy = searchParams?.get("sortBy");
const title = searchParams?.get("title");
const categorie = searchParams?.get("category");
const group = searchParams?.get("group");
const [, setGetTotalPage] = useState();
let typingTimer: any;
const doneTypingInterval = 1500;
const [contentVideo, setContentVideo] = useState([]);
const [categoryFilter, setCategoryFilter] = useState<any>([]);
const [monthYearFilter, setMonthYearFilter] = useState<any>();
const [searchTitle, setSearchTitle] = useState<string>("");
const [sortByOpt, setSortByOpt] = useState<any>(sortBy === "popular" ? "clickCount" : "createdAt");
const isRegional = asPath?.includes("regional");
const isSatker = asPath?.includes("satker");
const [formatFilter, setFormatFilter] = useState<any>([]);
const pages = page ? page - 1 : 0;
const [startDateString, setStartDateString] = useState<any>();
const [endDateString, setEndDateString] = useState<any>();
const [dateRange, setDateRange] = useState<any>([null, null]);
const [calenderState, setCalenderState] = useState(false);
const [handleClose, setHandleClose] = useState(false);
const [categories, setCategories] = useState([]);
const [userLevels, setUserLevels] = useState([]);
// const [startDate, endDate] = dateRange;
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
@ -66,8 +76,191 @@ const FilterPage = () => {
}
}, [searchParams]);
useEffect(() => {
async function initState() {
getCategories();
// getSelectedCategory();
if (isSatker) {
getUserLevels();
}
}
initState();
}, []);
useEffect(() => {
if (categorie) {
setCategoryFilter(categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
console.log("Kategori", categorie, categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
}
}, [categorie]);
// useEffect(() => {
// fetchData();
// }, [page, sortBy, sortByOpt, title]);
useEffect(() => {
async function initState() {
if (isRegional) {
getDataRegional();
} else {
getDataAll();
}
}
console.log(monthYearFilter, "monthFilter");
initState();
}, [change, asPath, monthYearFilter, page, sortBy, sortByOpt, title, startDateString, endDateString, categorie, formatFilter]);
async function getCategories() {
const category = await listCategory("2");
const resCategory = category?.data?.data?.content;
setCategories(resCategory);
}
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
setStartDateString(getOnlyDate(dateRange[0]));
setEndDateString(getOnlyDate(dateRange[1]));
setHandleClose(true);
console.log("date range", dateRange, getOnlyDate(dateRange[0]));
}
}
initState();
}, [calenderState]);
async function getDataAll() {
if (asPath?.includes("/polda/") == true) {
if (asPath?.split("/")[2] !== "[polda_name]") {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
const filterGroup = group == undefined ? asPath.split("/")[2] : group;
loading();
const response = await listData(
"2",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
filterGroup,
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
// setGetTotalPage(response.data?.data?.totalPages);
// setContentImage(response.data?.data?.content);
// setTotalContent(response.data?.data?.totalElements);
const data = response.data?.data;
const contentData = data?.content;
setVideoData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response.data?.data?.totalElements);
}
} else {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listData(
"2",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
// setGetTotalPage(response.data?.data?.totalPages);
// setContentImage(response.data?.data?.content);
// setTotalContent(response.data?.data?.totalElements);
const data = response.data?.data;
const contentData = data?.content;
setVideoData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response.data?.data?.totalElements);
}
}
const handleCategoryFilter = (e: boolean, id: string) => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
};
const handleFormatFilter = (e: boolean, id: string) => {
let filter = [...formatFilter];
if (e) {
filter = [...formatFilter, id];
} else {
filter.splice(formatFilter.indexOf(id), 1);
}
console.log("Format filter", filter);
setFormatFilter(filter);
};
const cleanCheckbox = () => {
setCategoryFilter([]);
setFormatFilter([]);
router.push(`?category=&title=`);
setDateRange([null, null]);
setMonthYearFilter(null);
setChange(!change);
};
async function getDataRegional() {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listDataRegional(
"2",
name,
filter,
format,
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
12,
pages,
sortByOpt
);
close();
setGetTotalPage(response.data?.data?.totalPages);
setContentVideo(response.data?.data?.content);
setTotalContent(response.data?.data?.totalElements);
}
const table = useReactTable({
data: imageData,
data: videoData,
columns: columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
@ -87,7 +280,6 @@ const FilterPage = () => {
},
});
const [videoData, setVideoData] = useState<any>();
useEffect(() => {
initFetch();
}, [page]);
@ -102,97 +294,212 @@ const FilterPage = () => {
setTotalPage(data?.totalPages);
};
function getSelectedCategory() {
const filter = [];
if (categorie) {
const categoryArr = categorie.split(",");
for (const element of categoryArr) {
filter.push(Number(element));
}
setCategoryFilter(filter);
}
}
const handleDeleteDate = () => {
setDateRange([null, null]);
setStartDateString("");
setEndDateString("");
setHandleClose(false);
};
const handleSorting = (e: any) => {
console.log(e.target.value);
if (e.target.value == "terbaru") {
setSortByOpt("createdAt");
} else {
setSortByOpt("clickCount");
}
setChange(!change);
};
async function getUserLevels() {
const res = await getUserLevelListByParent(761);
const userLevelList = res?.data?.data;
if (userLevelList !== null) {
let optionArr: any = [];
userLevelList?.map((option: any) => {
let optionData = {
id: option.id,
label: option.name,
value: option.id,
};
optionArr.push(optionData);
});
setUserLevels(optionArr);
}
}
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {
if (searchTitle == "" || searchTitle == undefined) {
router.push("?title=");
} else {
router.push(`?title=${searchTitle}`);
}
}
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
return (
<div className="flex flex-col">
{/* Header */}
<div className="flex flex-col md:flex-row items-start gap-5 p-10 bg-[#f7f7f7] dark:bg-black">
<p>
{" "}
Audio Visual {">"} <span className="font-bold">Semua Audio Visual</span>
</p>
<p className="font-bold">|</p>
<p>Terdapat 324911 artikel berisi Audio Visual yang dapat diunduh </p>
<p>{`Terdapat ${totalContent} artikel berisi Audio Visual yang dapat diunduh`}</p>
</div>
{/* Left */}
<div className="flex flex-col lg:flex-row gap-6 p-4">
<div className="lg:w-1/4 w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
<Reveal>
<div className="lg:w-[40%] w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
<Icon icon="stash:filter-light" fontSize={30} />
Filter
</h2>
<div className="border-t border-black dark:border-white my-4"></div>
<div className="border-t border-black my-4 dark:border-white"></div>
<div className="space-y-6">
<div>
<label htmlFor="search" className="block text-sm font-medium text-gray-700 dark:text-white">
Pencarian
</label>
<input type="text" id="search" placeholder="Cari judul..." className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500" />
<Input
value={searchTitle}
onChange={(e) => setSearchTitle(e.target.value)}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
type="text"
id="search"
placeholder="Cari judul..."
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
/>
</div>
<div>
<label htmlFor="month" className="block text-sm font-medium text-gray-700 dark:text-white">
Tahun & Bulan
</label>
<input type="month" id="month" className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500" />
<label className="block text-sm font-medium text-gray-700 dark:text-white">Tahun & Bulan</label>
<ReactDatePicker
selected={monthYearFilter}
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
onChange={(date) => setMonthYearFilter(date)}
dateFormat="MM | yyyy"
placeholderText="Pilih Tahun dan Bulan"
showMonthYearPicker
/>
</div>
<div>
<label htmlFor="date" className="block text-sm font-medium text-gray-700 dark:text-white">
Tanggal
</label>
<input type="date" id="date" className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500" />
<label className="block text-sm font-medium text-gray-700 dark:text-white">Tanggal</label>
<div className="flex flex-row justify justify-between gap-2">
<ReactDatePicker
selectsRange
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
startDate={dateRange[0]}
endDate={dateRange[1]}
onChange={(update) => {
setDateRange(update);
}}
placeholderText="Pilih Tanggal"
onCalendarClose={() => setCalenderState(!calenderState)}
/>
<div className="flex items-center">{handleClose ? <Icon icon="carbon:close-filled" onClick={handleDeleteDate} width="20" inline color="#216ba5" /> : ""}</div>
</div>
</div>
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Kategori</h3>
<ul className="mt-2 space-y-2">
{categories.map((category) => (
{categories.map((category: any) => (
<li key={category?.id}>
<label className="inline-flex items-center">
<Checkbox id="terms" />
<span className="ml-2 text-gray-700 dark:text-white">{category.title}</span>
<label className="inline-flex items-center" htmlFor={`${category.id}`}>
<Checkbox id={`${category.id}`} value={category.id} checked={categoryFilter.includes(String(category.id))} onCheckedChange={(e) => handleCategoryFilter(Boolean(e), category.id)} />
<span className="ml-2 text-gray-700 dark:text-white">{category?.name}</span>
</label>
</li>
))}
</ul>
</div>
{/* Garis */}
<div className="border-t border-black dark:border-white my-4"></div>
<div className="border-t border-black my-4 dark:border-white"></div>
{/* Garis */}
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Format Foto</h3>
<ul className="mt-2 space-y-2">
{formatPicture.map((format) => (
<li key={format?.id}>
<li>
<label className="inline-flex items-center">
<Checkbox id="terms" />
<span className="ml-2 text-gray-700 dark:text-white">{format.title}</span>
<Checkbox id="mk4" value="mk4" checked={formatFilter.includes("mk4")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "mk4")} />
<span className="ml-2 text-gray-700 dark:text-white">MK4</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="mov" value="mov" checked={formatFilter.includes("mov")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "mov")} />
<span className="ml-2 text-gray-700 dark:text-white">MOV</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="mp4" value="mp4" checked={formatFilter.includes("mp4")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "mp4")} />
<span className="ml-2 text-gray-700 dark:text-white">MP4</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="avi" value="avi" checked={formatFilter.includes("avi")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "avi")} />
<span className="ml-2 text-gray-700 dark:text-white">AVI</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="wmv" value="wmv" checked={formatFilter.includes("wmv")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "wmv")} />
<span className="ml-2 text-gray-700 dark:text-white">WMV</span>
</label>
</li>
))}
</ul>
</div>
<div className="border-t border-black dark:border-white my-4"></div>
<div className="text-center">
<a href="#" className="text-[#bb3523]">
<a onClick={cleanCheckbox} className="text-[#bb3523] cursor-pointer">
<b>Reset Filter</b>
</a>
</div>
</div>
</Reveal>
</div>
{/* Konten Kanan */}
<div className="flex-1">
<Reveal>
<div className="flex-1">
<div className="flex flex-col items-end mb-4">
<h2 className="text-lg font-semibold">Urutkan berdasarkan</h2>
<select className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
<select defaultValue={sortBy == "popular" ? "terpopuler" : "terbaru"} onChange={(e) => handleSorting(e)} className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
<option value="terbaru">Terbaru</option>
<option value="terlama">Terlama</option>
<option value="terpopuler">Terpopuler</option>
</select>
</div>
@ -201,7 +508,7 @@ const FilterPage = () => {
<Card key={video?.id} className="hover:scale-110 transition-transform duration-300">
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
<Link href={`/video/detail/${video?.slug}`}>
<img src={video?.thumbnailLink} className=" h-60 object-cover items-center w-full justify-center rounded-lg" />
<img src={video?.thumbnailLink} className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
<div className="flex flex-row items-center gap-2 text-[10px] mx-2">
{formatDateToIndonesian(new Date(video?.createdAt))} {video?.timezone ? video?.timezone : "WIB"}| <Icon icon="formkit:eye" width="15" height="15" />
{video?.clickCount}{" "}
@ -212,15 +519,15 @@ const FilterPage = () => {
/>
</svg>{" "}
</div>
<div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate text-base hover:whitespace-normal hover:overflow-visible w-full">{video?.title}</div>
<div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible w-full">{video?.title}</div>
</Link>
</CardContent>
</Card>
))}
</div>
<LandingPagination table={table} totalData={totalData} totalPage={totalPage} />
</Reveal>
</div>
</Reveal>
</div>
</div>
);

130
components/icon.tsx Normal file
View File

@ -0,0 +1,130 @@
import { SVGProps } from "react";
type IconSvgProps = SVGProps<SVGSVGElement> & {
size?: number;
};
export const FacebookIcon = ({
size,
height = 24,
width = 24,
fill = "currentColor",
...props
}: IconSvgProps) => (
<svg
height={size || height}
width={size || width}
viewBox="0 0 24 24"
fill={fill}
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<g fill="none">
<g clip-path="url(#akarIconsFacebookFill0)">
<path
fill="currentColor"
fillRule="evenodd"
d="M0 12.067C0 18.034 4.333 22.994 10 24v-8.667H7V12h3V9.333c0-3 1.933-4.666 4.667-4.666c.866 0 1.8.133 2.666.266V8H15.8c-1.467 0-1.8.733-1.8 1.667V12h3.2l-.533 3.333H14V24c5.667-1.006 10-5.966 10-11.933C24 5.43 18.6 0 12 0S0 5.43 0 12.067"
clipRule="evenodd"
/>
</g>
<defs>
<clipPath id="akarIconsFacebookFill0">
<path fill="#fff" d="M0 0h24v24H0z" />
</clipPath>
</defs>
</g>
</svg>
);
export const InstagramIcon = ({
size,
height = 24,
width = 24,
fill = "currentColor",
...props
}: IconSvgProps) => (
<svg
height={size || height}
width={size || width}
viewBox="0 0 24 24"
fill={fill}
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
fill="currentColor"
d="M7.8 2h8.4C19.4 2 22 4.6 22 7.8v8.4a5.8 5.8 0 0 1-5.8 5.8H7.8C4.6 22 2 19.4 2 16.2V7.8A5.8 5.8 0 0 1 7.8 2m-.2 2A3.6 3.6 0 0 0 4 7.6v8.8C4 18.39 5.61 20 7.6 20h8.8a3.6 3.6 0 0 0 3.6-3.6V7.6C20 5.61 18.39 4 16.4 4zm9.65 1.5a1.25 1.25 0 0 1 1.25 1.25A1.25 1.25 0 0 1 17.25 8A1.25 1.25 0 0 1 16 6.75a1.25 1.25 0 0 1 1.25-1.25M12 7a5 5 0 0 1 5 5a5 5 0 0 1-5 5a5 5 0 0 1-5-5a5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3a3 3 0 0 0 3 3a3 3 0 0 0 3-3a3 3 0 0 0-3-3"
/>
</svg>
);
export const YoutubeIcon = ({
size,
height = 24,
width = 24,
fill = "currentColor",
...props
}: IconSvgProps) => (
<svg
height={size || height}
width={size || width}
viewBox="0 0 24 24"
fill={fill}
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<g fill="none">
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
<path
fill="currentColor"
d="M12 4c.855 0 1.732.022 2.582.058l1.004.048l.961.057l.9.061l.822.064a3.8 3.8 0 0 1 3.494 3.423l.04.425l.075.91c.07.943.122 1.971.122 2.954s-.052 2.011-.122 2.954l-.075.91l-.04.425a3.8 3.8 0 0 1-3.495 3.423l-.82.063l-.9.062l-.962.057l-1.004.048A62 62 0 0 1 12 20a62 62 0 0 1-2.582-.058l-1.004-.048l-.961-.057l-.9-.062l-.822-.063a3.8 3.8 0 0 1-3.494-3.423l-.04-.425l-.075-.91A41 41 0 0 1 2 12c0-.983.052-2.011.122-2.954l.075-.91l.04-.425A3.8 3.8 0 0 1 5.73 4.288l.821-.064l.9-.061l.962-.057l1.004-.048A62 62 0 0 1 12 4m0 2c-.825 0-1.674.022-2.5.056l-.978.047l-.939.055l-.882.06l-.808.063a1.8 1.8 0 0 0-1.666 1.623C4.11 9.113 4 10.618 4 12s.11 2.887.227 4.096c.085.872.777 1.55 1.666 1.623l.808.062l.882.06l.939.056l.978.047c.826.034 1.675.056 2.5.056s1.674-.022 2.5-.056l.978-.047l.939-.055l.882-.06l.808-.063a1.8 1.8 0 0 0 1.666-1.623C19.89 14.887 20 13.382 20 12s-.11-2.887-.227-4.096a1.8 1.8 0 0 0-1.666-1.623l-.808-.062l-.882-.06l-.939-.056l-.978-.047A61 61 0 0 0 12 6m-2 3.575a.6.6 0 0 1 .819-.559l.081.04l4.2 2.424a.6.6 0 0 1 .085.98l-.085.06l-4.2 2.425a.6.6 0 0 1-.894-.43l-.006-.09z"
/>
</g>
</svg>
);
export const TiktokIcon = ({
size,
height = 24,
width = 24,
fill = "currentColor",
...props
}: IconSvgProps) => (
<svg
height={size || height}
width={size || width}
viewBox="0 0 24 24"
fill={fill}
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
fill="currentColor"
d="M16.6 5.82s.51.5 0 0A4.28 4.28 0 0 1 15.54 3h-3.09v12.4a2.59 2.59 0 0 1-2.59 2.5c-1.42 0-2.6-1.16-2.6-2.6c0-1.72 1.66-3.01 3.37-2.48V9.66c-3.45-.46-6.47 2.22-6.47 5.64c0 3.33 2.76 5.7 5.69 5.7c3.14 0 5.69-2.55 5.69-5.7V9.01a7.35 7.35 0 0 0 4.3 1.38V7.3s-1.88.09-3.24-1.48"
/>
</svg>
);
export const XIcon = ({
size,
height = 24,
width = 24,
fill = "currentColor",
...props
}: IconSvgProps) => (
<svg
height={size || height}
width={size || width}
viewBox="0 0 24 24"
fill={fill}
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="m13.081 10.712l-4.786-6.71a.6.6 0 0 0-.489-.252H5.28a.6.6 0 0 0-.488.948l6.127 8.59m2.162-2.576l6.127 8.59a.6.6 0 0 1-.488.948h-2.526a.6.6 0 0 1-.489-.252l-4.786-6.71m2.162-2.576l5.842-6.962m-8.004 9.538L5.077 20.25"
/>
</svg>
);

View File

@ -534,7 +534,7 @@ const Navbar = () => {
<NavigationMenuTrigger>
<a className="dark:text-white text-black flex flex-row justify-center items-center cursor-pointer">
<svg
className="mx-2 dark:"
className="mx-2"
width="25"
height="24"
viewBox="0 0 25 24"

View File

@ -1,10 +1,11 @@
"use-client";
import React, { useEffect, useState } from "react";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel";
import { useParams, usePathname, useRouter } from "next/navigation";
import { Icon } from "@iconify/react/dist/iconify.js";
import { formatDateToIndonesian, textEllipsis } from "@/utils/globals";
import { generateLocalizedPath } from "@/utils/globals";
import { formatDateToIndonesian } from "@/utils/globals";
import { getListContent } from "@/service/landing/landing";
import { Link } from "@/i18n/routing";
import { Reveal } from "./Reveal";
@ -33,11 +34,11 @@ const NewContent = (props: { type: string }) => {
};
return (
<div className="px-4 lg:px-16">
<div className="px-4 lg:px-16 py-4">
<Reveal>
<div className="flex flex-col p-4">
<div className="mx-auto w-full max-w-7xl justify-start flex px-5 flex-col lg:flex-row gap-5 mb-4">
<h2 className="flex items-center text-sm lg:text-xl w-fit font-bold bg-[#bb3523] px-4 py-1 rounded-lg text-white">
<h2 className="flex items-center text-xl lg:text-2xl w-fit font-bold bg-[#bb3523] px-4 py-1 rounded-lg text-white">
<span className="text-black ">Konten&nbsp;</span>
{props.type == "popular" ? "Populer" : props.type == "latest" ? "Terbaru" : "Serupa"}
</h2>
@ -73,7 +74,7 @@ const NewContent = (props: { type: string }) => {
</TabsList>
</Tabs>
</div>
<div className="px-10">
<div className="px-0 lg:px-10">
{selectedTab == "video" ? (
newContent?.length > 0 ? (
<Carousel className="w-full max-w-7xl mx-auto">
@ -81,7 +82,7 @@ const NewContent = (props: { type: string }) => {
{newContent?.map((video: any) => (
<CarouselItem key={video?.id} className="md:basis-1/2 lg:basis-1/3">
<Link href={`/video/detail/${video?.slug}`} className="relative group overflow-hidden shadow-md hover:shadow-lg">
<img src={video?.thumbnailLink} className="w-full rounded-lg h-40 lg:h-60 object-cover group-hover:scale-100 transition-transform duration-300" />
<img src={video?.thumbnailLink} className="w-full rounded-lg h-48 lg:h-60 object-cover group-hover:scale-100 transition-transform duration-300" />
<div className="absolute bottom-0 left-0 right-0 bg-gray-600 border-l-4 border-[#bb3523] rounded-lg backdrop-blur-sm text-white p-2">
<h1 className="text-sm lg:text-lg mb-2 font-semibold h-5 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">{video?.title}</h1>
<p className="flex flex-row items-center text-[10px] gap-2">
@ -200,7 +201,7 @@ const NewContent = (props: { type: string }) => {
</div>
</div>
<div className="flex items-center flex-row justify-center">
<Link href="/video/filter" className="border text-[#bb3523] rounded-lg text-sm lg:text-md px-4 py-1 border-[#bb3523]">
<Link href={`/${selectedTab}/filter?sortBy=${props.type}`} className="border text-[#bb3523] rounded-lg text-sm lg:text-md px-4 py-1 border-[#bb3523]">
LIHAT SEMUA
</Link>
</div>

View File

@ -11,7 +11,7 @@ const SearchSection = () => {
<h1 className="text-2xl md:text-3xl font-bold text-gray-800 dark:text-white">
<span className="text-[#bb3523] dark:text-white">Eksplorasi</span> dan <span className="text-[#bb3523] dark:text-white">Download</span> Liputan Resmi Kami
</h1>
<div className="w-auto h-1 bg-[#bb3523] mx-auto mt-2"></div>
<div className="w-[80%] h-1 bg-[#bb3523] mx-auto mt-2"></div>
<p className="text-sm md:text-base text-gray-500 dark:text-gray-100 mt-4">Liputan resmi yang bersumber dari kegiatan Polri di Mabes dan Polda seluruh Indonesia</p>
{/* Search Form */}
@ -76,7 +76,7 @@ const SearchSection = () => {
{/* Search Input */}
{/* Button */}
<button className="px-6 lg:w-[20%] py-2 bg-[#bb3523] flex justify-center items-center gap-2 text-white rounded-lg hover:bg-red-700">
<button className="flex justify-center items-center px-6 w-full lg:w-[20%] py-2 bg-[#bb3523] gap-2 text-white rounded-lg hover:bg-red-700">
Cari Liputan <Icon icon="ri:arrow-right-s-line" fontSize={20} />
</button>
</div>

File diff suppressed because it is too large Load Diff

View File

@ -78,7 +78,21 @@ const columns: ColumnDef<any>[] = [
</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={`/curator/task-plan/mediahub/create-weekly/detail/${data.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={`/curator/task-plan/mediahub/create-weekly/edit/${data.id}`}
>
Edit
</Link>
</DropdownMenuItem>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<a className="text-red-600">Delete</a>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@ -121,7 +135,21 @@ const columns: ColumnDef<any>[] = [
</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={`/curator/task-plan/mediahub/create-monthly/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={`/curator/task-plan/mediahub/create-monthly/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 className="text-red-600">Delete</a>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

View File

@ -0,0 +1,161 @@
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 { 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";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
// {
// accessorKey: "title",
// header: "Judul Perencanaan",
// cell: ({ row }) => <span>{row.getValue("title")}</span>,
// },
{
accessorKey: "title",
header: "Judul Perencanaan",
cell: ({ row }) => {
const datas = row.original.subPlanningList;
return (
<Dialog>
<DialogTrigger asChild>
<a className="cursor-pointer">{row.getValue("title")}</a>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>Rencanaan Mingguan</DialogTitle>
</DialogHeader>
<table>
<tr>
<th className="text-left">Judul Perencanaan</th>
<th>Batas Waktu</th>
<th>Status</th>
<th>Aksi</th>
</tr>
{datas?.map((data: any) => (
<tr key={data.id}>
<th className="text-left font-normal text-sm">
{data.title}
</th>
<th className="font-normal text-sm">{data.date}</th>
<th className="font-normal text-sm">{data.status}</th>
<th className="flex justify-center text-sm">
<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={`/curator/task-plan/medsos-mediahub/create-weekly/detail/${data.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={`/curator/task-plan/medsos-mediahub/create-weekly/edit/${data.id}`}
>
Edit
</Link>
</DropdownMenuItem>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<a className="text-red-600">Hapus</a>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</th>
</tr>
))}
</table>
</DialogContent>
</Dialog>
);
},
},
{
accessorKey: "date",
header: "Batas Waktu",
cell: ({ row }) => <span>{row.getValue("date")}</span>,
},
{
accessorKey: "status",
header: "Status",
cell: ({ row }) => <span>{row.getValue("status")}</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={`/curator/task-plan/medsos-mediahub/create-monthly/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={`/curator/task-plan/medsos-mediahub/create-monthly/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 className="text-red-600">Hapus</a>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];
export default columns;

View File

@ -0,0 +1,179 @@
"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,
} from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
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 "./list-view-social-media-column";
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
const ListViewSocialMediaTable = () => {
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 [limit, setLimit] = React.useState(10);
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, limit]);
async function fetchData() {
try {
const res = await getPlanningPagination(page - 1, "", 10, 2, 3);
const data = res.data?.data;
const contentData = data?.content;
contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * limit + index + 1;
});
console.log("contentData : ", data);
setDataTable(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
} catch (error) {
console.error("Error fetching tasks:", error);
}
}
return (
<div className="w-full overflow-x-auto">
<Table className="overflow-hidden mt-3">
<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 ListViewSocialMediaTable;

View File

@ -0,0 +1,470 @@
"use client";
import dayjs from "dayjs";
import { useEffect, useRef, useState } from "react";
import utc from "dayjs/plugin/utc";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { close, loading } from "@/config/swal";
import { useSearchParams } from "next/navigation";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
import "swiper/css/free-mode";
import "swiper/css/navigation";
import "swiper/css/thumbs";
import Image from "next/image";
import { FreeMode, Navigation, Thumbs } from "swiper/modules";
import { Swiper as SwiperType } from "swiper/types";
import {
getMonthlyPlanList,
getPlanningDailyByTypeId,
getPlanningDailyMedsosByPlatform,
getPlanningMonthlyPerSocmed,
getWeeklyPlanList,
getWeeklyPlanListByParentId,
} from "@/service/agenda-setting/agenda-setting";
import TaskPlanMediahubTable from "@/app/[locale]/(protected)/curator/task-plan/mediahub/components/table";
import weekday from "dayjs/plugin/weekday";
import { Icon } from "@iconify/react/dist/iconify.js";
import {
FacebookIcon,
InstagramIcon,
TiktokIcon,
XIcon,
YoutubeIcon,
} from "@/components/icon";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import TaskPlanningSocialMediaTable from "./social-media-modal/table";
const WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const TODAY = dayjs().format("YYYY-MM-DD");
export default function SingleViewSocialMediaTable() {
const params = useSearchParams();
const [selectedMonthItem, setSelectedMonthItem] = useState<
string | undefined
>(undefined);
const [selectedWeekly, setSelectedWeekly] = useState<string | undefined>(
undefined
);
const [selectedDate, setSelectedDate] = useState<number>(
new Date(TODAY).getDate()
);
const [nowDate, setNowDate] = useState<string>(TODAY);
const INITIAL_YEAR = dayjs().format("YYYY");
const INITIAL_MONTH = dayjs().format("M");
const size = 20;
const page: string | undefined | null = params?.get("page");
const id: string | undefined | null = params?.get("id");
const pages = page ? Number(page) - 1 : 0;
const no = (size || 10) * pages;
const [selectedMonth, setSelectedMonth] = useState<any>(
dayjs(new Date(parseInt(INITIAL_YEAR), parseInt(INITIAL_MONTH) - 1, 1))
);
const [selectedMonthTitle, setSelectedMonthTitle] = useState<string>("");
const [days, setDays] = useState<any>([]);
const weekday = require("dayjs/plugin/weekday");
const weekOfYear = require("dayjs/plugin/weekOfYear");
const [planningData, setPlanningData] = useState<any>([]);
dayjs.extend(utc);
dayjs.extend(weekday);
dayjs.extend(weekOfYear);
let currentMonthDays: any;
let previousMonthDays: any;
let nextMonthDays: any;
const [monthlyList, setMonthlyList] = useState([]);
const [weeklyList, setWeeklyList] = useState([]);
const [getData, setGetData] = useState<any>([]);
const [isOpen, setIsOpen] = useState(false);
const [selectedDateNow, setSelectedDateNow] = useState("");
const [selectedPlatform, setSelectedPlatform] = useState("");
useEffect(() => {
createCalendar("START");
}, []);
function createDaysForCurrentMonth(
year: string,
month: string,
daysInMonth: number
) {
const days: any = [];
for (let day = 1; day <= daysInMonth; day++) {
const date = dayjs(
new Date(parseInt(year), parseInt(month) - 1, day)
).format("YYYY-MM-DD");
days.push({
date,
isCurrentMonth: true,
isToday: date === TODAY,
});
}
return days;
}
async function getMonthlyPlanning(dates: number) {
const res = await getMonthlyPlanList(dates, 2);
setMonthlyList(res.data?.data);
}
async function getWeeklyPlanning(
id: string | undefined,
date: number | undefined
) {
if (id) {
const res = await getWeeklyPlanListByParentId(id, 2);
setWeeklyList(res.data?.data);
} else {
const res = await getWeeklyPlanList(date, 2, true);
setWeeklyList(res.data?.data);
}
}
function createCalendar(
year: string = INITIAL_YEAR,
month: string = INITIAL_MONTH
) {
year = year === "START" ? INITIAL_YEAR : year;
fetchData(month, year);
setSelectedMonthTitle(
dayjs(new Date(parseInt(year), parseInt(month) - 1))
.utc()
.local()
.format("MMMM YYYY")
);
currentMonthDays = createDaysForCurrentMonth(
year,
month,
dayjs(`${year}-${month}-01`).daysInMonth()
);
getMonthlyPlanning(year ? currentMonthDays[0]?.date : TODAY);
getWeeklyPlanning(undefined, year ? currentMonthDays[0]?.date : TODAY);
previousMonthDays = createDaysForPreviousMonth(year, month);
nextMonthDays = createDaysForNextMonth(year, month);
const listDay = [
...previousMonthDays,
...currentMonthDays,
...nextMonthDays,
];
setDays(listDay);
}
function getWeekday(date: string | undefined) {
return dayjs(date).weekday();
}
function createDaysForPreviousMonth(year: string, month: string) {
const firstDayOfTheMonthWeekday = getWeekday(currentMonthDays[0]?.date);
const previousMonth = dayjs(`${year}-${month}-01`).subtract(1, "month");
// Cover first day of the month being sunday (firstDayOfTheMonthWeekday == 0)
const visibleNumberOfDaysFromPreviousMonth = firstDayOfTheMonthWeekday
? firstDayOfTheMonthWeekday - 1
: 6;
const previousMonthLastMondayDayOfMonth = dayjs(currentMonthDays[0].date)
.subtract(visibleNumberOfDaysFromPreviousMonth, "day")
.date();
return [...new Array(visibleNumberOfDaysFromPreviousMonth)].map(
(day, index) => ({
date: dayjs(
`${previousMonth.year()}-${previousMonth.month() + 1}-${
previousMonthLastMondayDayOfMonth + index
}`
).format("YYYY-MM-DD"),
dayOfMonth: previousMonthLastMondayDayOfMonth + index,
isCurrentMonth: false,
})
);
}
function createDaysForNextMonth(year: string, month: string) {
const lastDayOfTheMonthWeekday = getWeekday(
`${year}-${month}-${currentMonthDays.length}`
);
const nextMonth = dayjs(`${year}-${month}-01`).add(1, "month");
const visibleNumberOfDaysFromNextMonth = lastDayOfTheMonthWeekday
? 7 - lastDayOfTheMonthWeekday
: lastDayOfTheMonthWeekday;
return [...new Array(visibleNumberOfDaysFromNextMonth)].map(
(day, index) => ({
date: dayjs(
`${nextMonth.year()}-${nextMonth.month() + 1}-${index + 1}`
).format("YYYY-MM-DD"),
dayOfMonth: index + 1,
isCurrentMonth: false,
})
);
}
async function fetchData(
month: string,
year: string,
parentId?: string | undefined
) {
loading();
const res = await getPlanningMonthlyPerSocmed(month, year, 2, parentId);
close();
setPlanningData(res.data?.data);
}
function getPrevMonth() {
const selectedMonthNew = dayjs(selectedMonth).subtract(1, "month");
createCalendar(
selectedMonthNew.format("YYYY"),
selectedMonthNew.format("M")
);
fetchData("", selectedMonthNew?.format("YYYY-MM-DD"));
setSelectedMonth(selectedMonthNew);
setSelectedDate(1);
}
function getPresentMonth() {
const selectedMonthNew = dayjs(
new Date(parseInt(INITIAL_YEAR), parseInt(INITIAL_MONTH) - 1, 1)
);
createCalendar(
selectedMonthNew.format("YYYY"),
selectedMonthNew.format("M")
);
fetchData("", TODAY);
setSelectedDate(Number(dayjs().format("D")));
setSelectedMonth(selectedMonthNew);
}
function getNextMonth() {
const selectedMonthNew = dayjs(selectedMonth).add(1, "month");
createCalendar(
selectedMonthNew.format("YYYY"),
selectedMonthNew.format("M")
);
fetchData("", selectedMonthNew?.format("YYYY-MM-DD"));
setSelectedMonth(selectedMonthNew);
setSelectedDate(1);
}
const onSelectedMonthItem = (id: string | undefined) => {
// fetchData(date)
setSelectedMonthItem(id);
getWeeklyPlanning(id, undefined);
};
const onSelectedWeekly = (id: string | undefined) => {
setSelectedWeekly(id);
fetchData("", String(id));
};
const removeSelection = () => {
setSelectedMonthItem(undefined);
setSelectedWeekly(undefined);
};
const onSelectedSocmed = async (date: string, platform: string) => {
setSelectedDateNow(date);
setSelectedPlatform(platform);
setIsOpen(true);
};
const getDataForThisDate = (day: number) => {
const todayData = planningData?.find((a: any) => a?.day == day);
if (todayData) {
return (
<div className="flex flex-col gap-1">
<a
onClick={() =>
onSelectedSocmed(todayData.planningList[0].date, "5")
}
className="flex flex-row gap-5 rounded-sm bg-[#0C0705] text-white px-3 py-1 justify-center cursor-pointer"
>
Total: {todayData.totalTwitter} <XIcon />
</a>
<a
onClick={() =>
onSelectedSocmed(todayData.planningList[0].date, "1")
}
className="flex flex-row gap-5 rounded-sm bg-[#1877F2] text-white px-3 py-1 justify-center cursor-pointer"
>
Total: {todayData.totalFacebook} <FacebookIcon />
</a>
<a
onClick={() =>
onSelectedSocmed(todayData.planningList[0].date, "2")
}
className="flex flex-row gap-5 rounded-sm bg-[#d62976] text-white px-3 py-1 justify-center cursor-pointer"
>
Total: {todayData.totalInstagram} <InstagramIcon />
</a>
<a
onClick={() =>
onSelectedSocmed(todayData.planningList[0].date, "3")
}
className="flex flex-row gap-5 rounded-sm bg-[#FF0000] text-white px-3 py-1 justify-center cursor-pointer"
>
Total: {todayData.totalYoutube} <YoutubeIcon />
</a>
<a
onClick={() =>
onSelectedSocmed(todayData.planningList[0].date, "4")
}
className="flex flex-row gap-5 rounded-sm bg-[#000000] text-white px-3 py-1 justify-center cursor-pointer"
>
Total: {todayData.totalTiktok} <TiktokIcon />
</a>
</div>
);
}
};
return (
<div className="border-2 rounded-sm p-4 flex gap-3 flex-col">
<div className="flex justify-between">
<div className="flex flex-row gap-2">
<a onClick={getPrevMonth} className="cursor-pointer">
<ChevronLeft />
</a>
<a onClick={getPresentMonth} className="cursor-pointer">
Today
</a>
<a onClick={getNextMonth} className="cursor-pointer">
<ChevronRight />
</a>
</div>
<p className="text-xl font-semibold">{selectedMonthTitle}</p>
</div>
<div className="flex justify-end">
<a
className="border-2 border-primary px-2 py-1 rounded-full text-[10px] text-primary cursor-pointer"
onClick={() => removeSelection()}
>
Hapus Pilihan
</a>
</div>
<div className="px-8 flex flex-col gap-2 text-sm">
<p className="font-semibold">Rencana Bulanan</p>
{monthlyList?.length > 0 ? (
<div className="flex flex-nowrap gap-3 flex-row">
{monthlyList?.map((item: any) => (
<div
key={item.id}
className={`rounded-full px-8 py-3 cursor-pointer ${
selectedMonthItem === item.id
? "bg-sky-600 text-white "
: "bg-sky-300"
}`}
onClick={() => onSelectedMonthItem(item.id)}
>
<p>{item.title}</p>
</div>
))}
</div>
) : (
<div className="flex justify-center bg-slate-200 rounded-full py-1">
Rencana Bulanan Belum Tersedia
</div>
)}
<p className="font-semibold">Rencana Mingguan</p>
{weeklyList?.length > 0 ? (
<div className="flex flex-nowrap gap-3 flex-row">
{weeklyList?.map((item: any) => (
<a
key={item.id}
className={`bg-sky-300 rounded-full px-8 py-3 cursor-pointer ${
selectedWeekly === item.id
? "bg-sky-600 text-white "
: "bg-sky-300"
}`}
onClick={() => onSelectedWeekly(item.id)}
>
<p>{item.title}</p>
</a>
))}
</div>
) : (
<div className="flex justify-center bg-slate-200 rounded-full py-1">
Rencana Mingguan Belum Tersedia
</div>
)}
</div>
<div>
<div className="w-full mb-2">
<div className="overflow-auto">
<ol
id="days-of-week"
className="grid grid-cols-7 gap-1 border-y-2 py-3"
>
{WEEKDAYS.map((weekday, index) => (
<li
key={`weekday-${index}`}
className="mr-1 text-sm font-medium text-end"
>
{weekday}
</li>
))}
</ol>
<ol id="calendar-days" className="grid grid-cols-7 gap-1">
{days?.map((day: any, index: number) => (
<li
key={`day-${index}`}
className={`p-2 text-end min-h-[100px] ${
day.isCurrentMonth ? "" : "bg-slate-200 text-slate-400"
}`}
>
<span
className={`block mb-1 ${
day.date == TODAY ? "font-semibold text-xl" : ""
}`}
>
{parseInt(day.date.split("-")[2])}
</span>
{day.isCurrentMonth &&
getDataForThisDate(parseInt(day.date.split("-")[2]))}
</li>
))}
</ol>
</div>
</div>
</div>
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent size="lg" className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Perencanaan Medsos Harian</DialogTitle>
</DialogHeader>
<div className="flex items-center space-x-2">
<TaskPlanningSocialMediaTable
date={selectedDateNow}
platform={selectedPlatform}
/>
</div>
</DialogContent>
</Dialog>
</div>
);
}

View File

@ -0,0 +1,104 @@
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 { 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";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
// {
// accessorKey: "title",
// header: "Judul Perencanaan",
// cell: ({ row }) => <span>{row.getValue("title")}</span>,
// },
{
accessorKey: "title",
header: "Judul Perencanaan",
cell: ({ row }) => <span>{row.getValue("title")}</span>,
},
{
accessorKey: "createdByName",
header: "Nama Pembuat",
cell: ({ row }) => <span>{row.getValue("createdByName")}</span>,
},
{
accessorKey: "createdAt",
header: "Dibuat",
cell: ({ row }) => <span>{row.getValue("createdAt")}</span>,
},
{
accessorKey: "status",
header: "Dibuat",
cell: ({ row }) => <span>{row.getValue("status")}</span>,
},
{
accessorKey: "platformTypeId",
header: "Jenis Platform",
cell: ({ row }) => <span>{row.getValue("platformTypeId")}</span>,
},
{
accessorKey: "status",
header: "Status",
cell: ({ row }) => <span>{row.getValue("status")}</span>,
},
{
accessorKey: "description",
header: "Deskripsi",
cell: ({ row }) => <span>{htmlToString(row.getValue("status"))}</span>,
},
{
id: "actions",
accessorKey: "action",
header: "Aksi",
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">
Detail
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];
export default columns;

View File

@ -0,0 +1,200 @@
"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,
} from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
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 {
getPlanningDailyMedsosByPlatform,
getPlanningPagination,
} from "@/service/agenda-setting/agenda-setting";
const TaskPlanningSocialMediaTable = (props: {
date: string;
platform: string;
}) => {
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 [limit, setLimit] = React.useState(10);
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, limit, props]);
async function fetchData() {
try {
const response = await getPlanningDailyMedsosByPlatform(
"0",
20,
props.date,
props.platform
);
const data = response.data?.data;
const contentData = data?.content;
contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * limit + index + 1;
item.platformTypeId =
props.platform === "1"
? "Facebook"
: props.platform === "2"
? "Instagram"
: props.platform === "3"
? "Youtube"
: props.platform === "4"
? "Tiktok"
: props.platform === "3"
? "X"
: "";
});
setDataTable(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
} catch (error) {
console.error("Error fetching tasks:", error);
}
}
return (
<div className="w-full overflow-x-auto">
<Table className="overflow-hidden mt-3">
<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 TaskPlanningSocialMediaTable;

View File

@ -31,7 +31,6 @@ export type Group = {
};
export function getMenuList(pathname: string, t: any): Group[] {
const roleId = getCookiesDecrypt("urie");
const levelNumber = getCookiesDecrypt("ulne");
const userLevelId = getCookiesDecrypt("ulie");
@ -1517,8 +1516,8 @@ export function getMenuList(pathname: string, t: any): Group[] {
],
},
],
}
]
},
];
} else if (Number(roleId) == 11) {
menusSelected = [
{
@ -1569,7 +1568,7 @@ export function getMenuList(pathname: string, t: any): Group[] {
menus: [
{
id: "agenda-setting",
href: "/curator/agenda-setting",
href: "/contributor/agenda-setting",
label: t("agenda-setting"),
active: pathname.includes("/agenda-setting"),
icon: "iconoir:journal-page",
@ -1675,8 +1674,8 @@ export function getMenuList(pathname: string, t: any): Group[] {
submenus: [],
},
],
}
]
},
];
}
return menusSelected;

131
package-lock.json generated
View File

@ -57,6 +57,7 @@
"@types/cleave.js": "^1.4.12",
"@types/crypto-js": "^4.2.2",
"@types/js-cookie": "^3.0.6",
"@types/next": "^9.0.0",
"@types/qs": "^6.9.17",
"@types/react-google-recaptcha": "^2.1.9",
"@types/react-html-parser": "^2.0.6",
@ -88,7 +89,7 @@
"leaflet": "^1.9.4",
"lucide-react": "^0.390.0",
"moment": "^2.30.1",
"next": "14.2.3",
"next": "^14.2.3",
"next-intl": "^3.19.1",
"next-themes": "^0.3.0",
"nextra": "^2.13.4",
@ -155,7 +156,6 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
"dev": true,
"engines": {
"node": ">=10"
},
@ -841,7 +841,6 @@
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
"dev": true,
"dependencies": {
"string-width": "^5.1.2",
"string-width-cjs": "npm:string-width@^4.2.0",
@ -858,7 +857,6 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
"dev": true,
"engines": {
"node": ">=12"
},
@ -870,7 +868,6 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"dependencies": {
"ansi-regex": "^6.0.1"
},
@ -1206,7 +1203,6 @@
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
@ -1219,7 +1215,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
"engines": {
"node": ">= 8"
}
@ -1228,7 +1223,6 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
@ -1250,7 +1244,6 @@
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
"dev": true,
"optional": true,
"engines": {
"node": ">=14"
@ -3119,6 +3112,15 @@
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
},
"node_modules/@types/next": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@types/next/-/next-9.0.0.tgz",
"integrity": "sha512-gnBXM8rP1mnCgT1uE2z8SnpFTKRWReJlhbZLZkOLq/CH1ifvTNwjIVtXvsywTy1dwVklf+y/MB0Eh6FOa94yrg==",
"deprecated": "This is a stub types definition. next provides its own type definitions, so you do not need this installed.",
"dependencies": {
"next": "*"
}
},
"node_modules/@types/node": {
"version": "20.17.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.9.tgz",
@ -3440,7 +3442,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -3454,7 +3455,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@ -3468,14 +3468,12 @@
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
"dev": true
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
@ -3520,8 +3518,7 @@
"node_modules/arg": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
"dev": true
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
},
"node_modules/argparse": {
"version": "2.0.1",
@ -3812,14 +3809,12 @@
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"dev": true,
"engines": {
"node": ">=8"
},
@ -3841,7 +3836,6 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"dependencies": {
"fill-range": "^7.1.1"
},
@ -3901,7 +3895,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
"dev": true,
"engines": {
"node": ">= 6"
}
@ -4001,7 +3994,6 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@ -4025,7 +4017,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
@ -4210,7 +4201,6 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@ -4234,7 +4224,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true,
"bin": {
"cssesc": "bin/cssesc"
},
@ -4910,8 +4899,7 @@
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
"dev": true
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
},
"node_modules/diff": {
"version": "5.2.0",
@ -4936,8 +4924,7 @@
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
"dev": true
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
},
"node_modules/doctrine": {
"version": "3.0.0",
@ -5038,8 +5025,7 @@
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"dev": true
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
},
"node_modules/elkjs": {
"version": "0.9.3",
@ -5087,8 +5073,7 @@
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
},
"node_modules/enhanced-resolve": {
"version": "5.17.1",
@ -5928,7 +5913,6 @@
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
"dev": true,
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
@ -5944,7 +5928,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
@ -5968,7 +5951,6 @@
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
"dev": true,
"dependencies": {
"reusify": "^1.0.4"
}
@ -6012,7 +5994,6 @@
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
@ -6103,7 +6084,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
"integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
"dev": true,
"dependencies": {
"cross-spawn": "^7.0.0",
"signal-exit": "^4.0.1"
@ -6314,7 +6294,6 @@
"version": "10.3.10",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
"integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
"dev": true,
"dependencies": {
"foreground-child": "^3.1.0",
"jackspeak": "^2.3.5",
@ -6336,7 +6315,6 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.3"
},
@ -6348,7 +6326,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
@ -6357,7 +6334,6 @@
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
@ -7325,7 +7301,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
@ -7457,7 +7432,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -7481,7 +7455,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -7505,7 +7478,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
@ -7550,7 +7522,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"engines": {
"node": ">=0.12.0"
}
@ -7799,7 +7770,6 @@
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
"integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
"dev": true,
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
@ -7817,7 +7787,6 @@
"version": "1.21.6",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz",
"integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==",
"dev": true,
"bin": {
"jiti": "bin/jiti.js"
}
@ -8073,7 +8042,6 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"dev": true,
"engines": {
"node": ">=14"
},
@ -8182,8 +8150,7 @@
"node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
},
"node_modules/lucide-react": {
"version": "0.390.0",
@ -8897,7 +8864,6 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"dev": true,
"engines": {
"node": ">= 8"
}
@ -9685,7 +9651,6 @@
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
@ -9746,7 +9711,6 @@
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"dev": true,
"engines": {
"node": ">=16 || 14 >=14.17"
}
@ -9795,7 +9759,6 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
"dev": true,
"dependencies": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
@ -10072,7 +10035,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -10119,7 +10081,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"dev": true,
"engines": {
"node": ">= 6"
}
@ -10418,7 +10379,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -10432,7 +10392,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"dev": true,
"dependencies": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
@ -10471,7 +10430,6 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"engines": {
"node": ">=8.6"
},
@ -10483,7 +10441,6 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -10492,7 +10449,6 @@
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
"dev": true,
"engines": {
"node": ">= 6"
}
@ -10510,7 +10466,6 @@
"version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"dev": true,
"funding": [
{
"type": "opencollective",
@ -10538,7 +10493,6 @@
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
"dev": true,
"dependencies": {
"postcss-value-parser": "^4.0.0",
"read-cache": "^1.0.0",
@ -10555,7 +10509,6 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
"dev": true,
"dependencies": {
"camelcase-css": "^2.0.1"
},
@ -10574,7 +10527,6 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
@ -10609,7 +10561,6 @@
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz",
"integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==",
"dev": true,
"bin": {
"yaml": "bin.mjs"
},
@ -10621,7 +10572,6 @@
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
@ -10646,7 +10596,6 @@
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"dev": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@ -10658,8 +10607,7 @@
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"node_modules/preact": {
"version": "10.12.1",
@ -10753,7 +10701,6 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true,
"funding": [
{
"type": "github",
@ -11304,7 +11251,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
"dev": true,
"dependencies": {
"pify": "^2.3.0"
}
@ -11313,7 +11259,6 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"dependencies": {
"picomatch": "^2.2.1"
},
@ -11736,7 +11681,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
"dev": true,
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
@ -11809,7 +11753,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"dev": true,
"funding": [
{
"type": "github",
@ -12001,7 +11944,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"dependencies": {
"shebang-regex": "^3.0.0"
},
@ -12013,7 +11955,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -12050,7 +11991,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true,
"engines": {
"node": ">=14"
},
@ -12180,7 +12120,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"dev": true,
"dependencies": {
"eastasianwidth": "^0.2.0",
"emoji-regex": "^9.2.2",
@ -12198,7 +12137,6 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@ -12211,14 +12149,12 @@
"node_modules/string-width-cjs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/string-width/node_modules/ansi-regex": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
"dev": true,
"engines": {
"node": ">=12"
},
@ -12230,7 +12166,6 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"dependencies": {
"ansi-regex": "^6.0.1"
},
@ -12366,7 +12301,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@ -12379,7 +12313,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@ -12484,7 +12417,6 @@
"version": "3.35.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
"integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
"dev": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.2",
"commander": "^4.0.0",
@ -12506,7 +12438,6 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
"dev": true,
"engines": {
"node": ">= 6"
}
@ -12680,7 +12611,6 @@
"version": "3.4.16",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.16.tgz",
"integrity": "sha512-TI4Cyx7gDiZ6r44ewaJmt0o6BrMCT5aK5e0rmJ/G9Xq3w7CX/5VXl/zIPEJZFUK5VEqwByyhqNPycPlvcK4ZNw==",
"dev": true,
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
@ -12740,7 +12670,6 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
"dev": true,
"dependencies": {
"any-promise": "^1.0.0"
}
@ -12749,7 +12678,6 @@
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
"dev": true,
"dependencies": {
"thenify": ">= 3.1.0 < 4"
},
@ -12862,7 +12790,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"dependencies": {
"is-number": "^7.0.0"
},
@ -12925,8 +12852,7 @@
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
"dev": true
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
},
"node_modules/tsconfig-paths": {
"version": "3.15.0",
@ -13404,8 +13330,7 @@
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/uuid": {
"version": "9.0.1",
@ -13644,7 +13569,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"dependencies": {
"isexe": "^2.0.0"
},
@ -13751,7 +13675,6 @@
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
"dev": true,
"dependencies": {
"ansi-styles": "^6.1.0",
"string-width": "^5.0.1",
@ -13769,7 +13692,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@ -13785,14 +13707,12 @@
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@ -13806,7 +13726,6 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
"dev": true,
"engines": {
"node": ">=12"
},
@ -13818,7 +13737,6 @@
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
"engines": {
"node": ">=12"
},
@ -13830,7 +13748,6 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"dependencies": {
"ansi-regex": "^6.0.1"
},

View File

@ -58,6 +58,7 @@
"@types/cleave.js": "^1.4.12",
"@types/crypto-js": "^4.2.2",
"@types/js-cookie": "^3.0.6",
"@types/next": "^9.0.0",
"@types/qs": "^6.9.17",
"@types/react-google-recaptcha": "^2.1.9",
"@types/react-html-parser": "^2.0.6",
@ -89,7 +90,7 @@
"leaflet": "^1.9.4",
"lucide-react": "^0.390.0",
"moment": "^2.30.1",
"next": "14.2.3",
"next": "^14.2.3",
"next-intl": "^3.19.1",
"next-themes": "^0.3.0",
"nextra": "^2.13.4",

View File

@ -28,7 +28,7 @@ export async function getPlanningDailyByTypeId(
return getAPIInterceptor(url);
}
export async function getMonthlyPlanList(dates: number, typeId: number) {
export async function getMonthlyPlanList(dates: any, typeId: number) {
const url = `planning/monthly/list?date=${dates}&typeId=${typeId}`;
return getAPIInterceptor(url);
}
@ -63,3 +63,23 @@ export async function savePlanning(data: any) {
const url = "planning";
return postAPIInterceptor(url, data);
}
export async function getPlanningMonthlyPerSocmed(
month = "",
year = "",
typeId: number,
parentId = ""
) {
const url = `planning/socmed/monthly?month=${month}&year=${year}&typeId=${typeId}&parentId=${parentId}`;
return getAPIInterceptor(url);
}
export async function getPlanningDailyMedsosByPlatform(
page: string,
size = 10,
date: string,
platformTypeId: string
) {
const url = `planning/pagination/daily?enablePage=1&size=${size}&page=${page}&date=${date}&typeId=2&platformTypeId=${platformTypeId}`;
return getAPIInterceptor(url);
}

View File

@ -10,7 +10,9 @@ export async function getCategoryData() {
export async function getListContent(props: any) {
return await httpGetInterceptor(
`media/public/list?enablePage=1&sort=desc&sortBy=${props.sortBy}&size=${props.size}&page=${props.page}&typeId=${props.contentTypeId}&title=&categoryId=&fileFormats=&tags=&group=&startDate=&endDate=&month=&year=`
`media/public/list?enablePage=1&sort=desc&sortBy=${props.sortBy}&size=${props.size}&page=${props.page}&typeId=${props.contentTypeId}&title=${
props.title ? props.title : ""
}&categoryId=&fileFormats=&tags=&group=&startDate=&endDate=&month=&year=`
);
}
@ -42,4 +44,22 @@ export async function listCategory(type = "") {
return await httpGetInterceptor(`media/categories/list/enable?enablePage=1&sort=desc&sortBy=updatedAt&size=12&type=${type}`);
}
export async function publicDetailBlog(slug: any) {
return await httpGetInterceptor(`blog/public/read/${slug}`);
}
export async function listData(type: string, search: string, category: string, size = 10, page = 0, sortBy = "createdAt", format = "", tag = "", group = "", startDate = "", endDate = "", month = "", year = "") {
return await httpGetInterceptor(
`media/public/list?enablePage=1&sort=desc&sortBy=${sortBy}&size=${size}&page=${page}&typeId=${type}&title=${search}&categoryId=${category}&fileFormats=${format}&tags=${tag}&group=${group}&startDate=${startDate}&endDate=${endDate}&month=${month}&year=${year}`
);
}
export async function listDataRegional(type: string, search: string, category: string, format = "", tag = "", startDate = "", endDate = "", month = "", year = "", size = 10, page = 0, sortBy = "createdAt") {
return await httpGetInterceptor(
`media/public/regional-list?enablePage=1&size=${size}&page=${page}&sort=desc&sortBy=${sortBy}&typeId=${type}&title=${search}&categoryId=${category}&fileFormats=${format}&tags=${tag}&startDate=${startDate}&endDate=${endDate}&month=${month}&year=${year}`
);
}
export async function getUserLevelListByParent(id: any) {
return await httpGetInterceptor(`users/user-levels/list?userLevelId=${id}`);
}

View File

@ -0,0 +1,6 @@
import { getAPIInterceptor } from "@/config/api";
export async function getMediaTrackingMonitoring(page: number, size: number) {
const url = `cekmedsos/monitoring/pagination?page=${page}&size=${size}`;
return getAPIInterceptor(url);
}

View File

@ -63,3 +63,9 @@ export function getOnlyDate(date: Date) {
return `${year}-${month}-${day}`;
}
export function getOnlyMonthAndYear(d: Date) {
const pad = (n: any, s = 2) => `${new Array(s).fill(0)}${n}`.slice(-s);
return `${pad(d.getMonth() + 1)}/${pad(d.getFullYear(), 4)}`;
}