pull
This commit is contained in:
commit
f072a636c3
28
Dockerfile
28
Dockerfile
|
|
@ -1,26 +1,32 @@
|
|||
# Menggunakan image Node.js yang lebih ringan
|
||||
FROM node:23.5.0-alpine
|
||||
|
||||
# Mengatur port
|
||||
ENV PORT 3000
|
||||
|
||||
# Install pnpm globally
|
||||
# Install pnpm secara global
|
||||
RUN npm install -g pnpm
|
||||
|
||||
# Create app directory
|
||||
RUN mkdir -p /usr/src/app
|
||||
# Membuat direktori aplikasi dan mengatur sebagai working directory
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Installing dependencies
|
||||
COPY package*.json pnpm-lock.yaml* /usr/src/app/
|
||||
# Menyalin file penting terlebih dahulu untuk caching
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
|
||||
# Install dependencies using pnpm
|
||||
# Menyalin direktori ckeditor5 jika diperlukan
|
||||
COPY vendor/ckeditor5 ./vendor/ckeditor5
|
||||
|
||||
# Install dependencies
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# Copying source files
|
||||
COPY . /usr/src/app
|
||||
# Menyalin source code aplikasi
|
||||
COPY . .
|
||||
|
||||
# Building app
|
||||
# Build aplikasi
|
||||
RUN pnpm run build
|
||||
|
||||
# Expose port untuk server
|
||||
EXPOSE 3000
|
||||
|
||||
# Running the app
|
||||
CMD ["pnpm", "run", "start"]
|
||||
# Perintah untuk menjalankan aplikasi
|
||||
CMD ["pnpm", "run", "start"]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
import * as React from "react";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
|
||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuItem,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import {
|
||||
formatDateToIndonesian,
|
||||
getOnlyDate,
|
||||
htmlToString,
|
||||
} from "@/utils/globals";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible";
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
accessorKey: "no",
|
||||
header: "No",
|
||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: "Nama",
|
||||
cell: ({ row }) => <span>{row.getValue("name")}</span>,
|
||||
},
|
||||
{
|
||||
accessorKey: "region",
|
||||
header: "Wilayah",
|
||||
cell: ({ row }) => <span>{row.getValue("region")}</span>,
|
||||
},
|
||||
{
|
||||
accessorKey: "skills",
|
||||
header: "Bidang Keahlian",
|
||||
cell: ({ row }) => <span>{row.getValue("skills")}</span>,
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "experience",
|
||||
header: "Pengalaman",
|
||||
cell: ({ row }) => <span>{row.getValue("experience")}</span>,
|
||||
},
|
||||
|
||||
{
|
||||
id: "actions",
|
||||
accessorKey: "action",
|
||||
header: "Actions",
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
size="icon"
|
||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||
>
|
||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<Link href="#">Detail</Link>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default columns;
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
PaginationState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { UserIcon } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||
import { paginationBlog } from "@/service/blog/blog";
|
||||
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import columns from "./column";
|
||||
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { listDataMedia } from "@/service/broadcast/broadcast";
|
||||
import { listEnableCategory } from "@/service/content/content";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { close, loading } from "@/config/swal";
|
||||
import { Link } from "@/i18n/routing";
|
||||
|
||||
const dummyData = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Prof. Dr. Ravi",
|
||||
region: "Nasional",
|
||||
skills: "Komunikasi",
|
||||
experience: "Akademisi",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Prof. Dr. Novan",
|
||||
region: "DKI Jakarta",
|
||||
skills: "Hukum",
|
||||
experience: "Akademisi + Praktisi",
|
||||
},
|
||||
];
|
||||
|
||||
const AddExpertTable = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [showData, setShowData] = React.useState("10");
|
||||
const [categories, setCategories] = React.useState<any>();
|
||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[]
|
||||
);
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({});
|
||||
const [rowSelection, setRowSelection] = React.useState({});
|
||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: Number(showData),
|
||||
});
|
||||
const [categoryFilter, setCategoryFilter] = React.useState<number[]>([]);
|
||||
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||
const [page, setPage] = React.useState(1);
|
||||
const [totalPage, setTotalPage] = React.useState(1);
|
||||
const table = useReactTable({
|
||||
data: dataTable,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
onPaginationChange: setPagination,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
pagination,
|
||||
},
|
||||
});
|
||||
|
||||
let typingTimer: any;
|
||||
const doneTypingInterval = 1500;
|
||||
|
||||
const handleKeyUp = () => {
|
||||
clearTimeout(typingTimer);
|
||||
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||
};
|
||||
|
||||
const handleKeyDown = () => {
|
||||
clearTimeout(typingTimer);
|
||||
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||
};
|
||||
|
||||
async function doneTyping() {
|
||||
fetchData();
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
const pageFromUrl = searchParams?.get("page");
|
||||
if (pageFromUrl) {
|
||||
setPage(Number(pageFromUrl));
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchData();
|
||||
setPagination({
|
||||
pageIndex: 0,
|
||||
pageSize: Number(showData),
|
||||
});
|
||||
}, [page, showData]);
|
||||
|
||||
async function fetchData() {
|
||||
try {
|
||||
loading();
|
||||
|
||||
const contentData = dummyData;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * Number(showData) + index + 1;
|
||||
});
|
||||
|
||||
setDataTable(contentData);
|
||||
setTotalData(contentData?.length);
|
||||
setTotalPage(1);
|
||||
close();
|
||||
} catch (error) {
|
||||
console.error("Error fetching tasks:", error);
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
getCategories();
|
||||
}, []);
|
||||
|
||||
async function getCategories() {
|
||||
const category = await listEnableCategory("");
|
||||
const resCategory = category?.data?.data?.content;
|
||||
setCategories(resCategory);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||
<div className="flex justify-between mb-10 items-center">
|
||||
<p className="text-xl font-medium text-default-900">Tenaga Ahli</p>
|
||||
<Link href="/admin/add-experts/create">
|
||||
<Button color="primary" size="md" className="text-sm">
|
||||
<UserIcon />
|
||||
Tambah Tenaga Ahli
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between ">
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
onKeyUp={handleKeyUp}
|
||||
onKeyDown={handleKeyDown}
|
||||
className="max-w-[300px]"
|
||||
/>
|
||||
<div className="flex flex-row gap-2">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button size="md" variant="outline">
|
||||
1 - {showData} Data
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56">
|
||||
<DropdownMenuRadioGroup
|
||||
value={showData}
|
||||
onValueChange={setShowData}
|
||||
>
|
||||
<DropdownMenuRadioItem value="10">
|
||||
1 - 10 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="20">
|
||||
1 - 20 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="25">
|
||||
1 - 25 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="50">
|
||||
1 - 50 Data
|
||||
</DropdownMenuRadioItem>
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
<Table className="overflow-hidden">
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
className="h-[75px]"
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<TablePagination
|
||||
table={table}
|
||||
totalData={totalData}
|
||||
totalPage={totalPage}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddExpertTable;
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
"use client";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { z } from "zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
|
||||
const FormSchema = z.object({
|
||||
name: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
phoneNumber: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
email: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
region: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
skills: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
experience: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
company: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
|
||||
export default function AddExpertForm() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||
console.log("data", data);
|
||||
|
||||
// successSubmit();
|
||||
};
|
||||
|
||||
function successSubmit() {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
router.push("/admin/add-experts");
|
||||
}
|
||||
});
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-3 bg-white rounded-sm p-4"
|
||||
>
|
||||
<p className="fonnt-semibold">Campaign</p>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama Lengkap</FormLabel>
|
||||
<Input
|
||||
value={field.value}
|
||||
placeholder="Masukkan Nama Lengkap"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="phoneNumber"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>No. HP</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
value={field.value}
|
||||
placeholder="Masukkan Nama Lengkap"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<Input
|
||||
type="email"
|
||||
value={field.value}
|
||||
placeholder="Masukkan Nama Lengkap"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="region"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Wilayah</FormLabel>
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Pilih Region" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="nasional">Nasional</SelectItem>
|
||||
<SelectItem value="jakarta">DKI Jakarta</SelectItem>
|
||||
<SelectItem value="jabar">Jawa Barat</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="skills"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Bidang Keahlian</FormLabel>
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Pilih Bidang Keahlian" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="komunikasi">Komunikasi</SelectItem>
|
||||
<SelectItem value="hukum">Hukum</SelectItem>
|
||||
<SelectItem value="bahasa">Bahasa</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="experience"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Pengalaman</FormLabel>
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Pilih Pengalaman" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="akademisi">Akademisi</SelectItem>
|
||||
<SelectItem value="praktisi">Praktisi</SelectItem>
|
||||
<SelectItem value="akademisi+praktisi">
|
||||
Akademisi + Praktisi
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="company"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama Institusi/Perusahaan</FormLabel>
|
||||
<Input
|
||||
type="text"
|
||||
value={field.value}
|
||||
placeholder="Nama Institusi/Perusahaan"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex flex-row justify-end gap-2 mt-4 pt-4">
|
||||
<Button
|
||||
size="md"
|
||||
type="button"
|
||||
variant="outline"
|
||||
color="destructive"
|
||||
className="text-xs"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="md" type="submit" color="primary" className="text-xs">
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import AddExpertTable from "./component/table";
|
||||
|
||||
export default function AddExpert() {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<AddExpertTable />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
import * as React from "react";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
|
||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuItem,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { close, error, loading } from "@/config/swal";
|
||||
import { deleteMediaBlastAccount } from "@/service/broadcast/broadcast";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
accessorKey: "no",
|
||||
header: "No",
|
||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "accountName",
|
||||
header: "Nama",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("accountName")}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "accountType",
|
||||
header: "Tipe Akun",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("accountType")}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "accountCategory",
|
||||
header: "Kategory",
|
||||
cell: ({ row }) => (
|
||||
<span className="uppercase">{row.getValue("accountCategory")}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "emailAddress",
|
||||
header: "Email",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("emailAddress")}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "whatsappNumber",
|
||||
header: "Whatsapp",
|
||||
cell: ({ row }) => <span>{row.getValue("whatsappNumber")}</span>,
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
accessorKey: "action",
|
||||
header: "Actions",
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const { toast } = useToast();
|
||||
const router = useRouter();
|
||||
const handleDelete = (id: string) => {
|
||||
MySwal.fire({
|
||||
title: "Apakah anda ingin menghapus data?",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#dc3545",
|
||||
confirmButtonText: "Iya",
|
||||
cancelButtonText: "Tidak",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
doDeleteAccount(id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async function succes() {
|
||||
toast({
|
||||
title: "Sukses",
|
||||
description: "Berhasil Delete",
|
||||
});
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
async function doDeleteAccount(id: string) {
|
||||
loading();
|
||||
const response = await deleteMediaBlastAccount(id);
|
||||
close();
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return false;
|
||||
}
|
||||
succes();
|
||||
}
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
size="icon"
|
||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||
>
|
||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<Link
|
||||
href={`/admin/broadcast/campaign-list/account-list/edit/${row.original.id}`}
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<a onClick={() => handleDelete(row.original.id)}>Delete</a>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default columns;
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
PaginationState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { UserIcon } from "lucide-react";
|
||||
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import columns from "./column";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { getMediaBlastAccountPage } from "@/service/broadcast/broadcast";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { close, loading } from "@/config/swal";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
|
||||
const AccountListTable = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[]
|
||||
);
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({});
|
||||
const [rowSelection, setRowSelection] = React.useState({});
|
||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
const [page, setPage] = React.useState(1);
|
||||
const [totalPage, setTotalPage] = React.useState(1);
|
||||
const [filtered, setFiltered] = React.useState<string[]>([]);
|
||||
const table = useReactTable({
|
||||
data: dataTable,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
onPaginationChange: setPagination,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
pagination,
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
const pageFromUrl = searchParams?.get("page");
|
||||
if (pageFromUrl) {
|
||||
setPage(Number(pageFromUrl));
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchData();
|
||||
}, [page]);
|
||||
|
||||
async function fetchData() {
|
||||
try {
|
||||
loading();
|
||||
const res = await getMediaBlastAccountPage(
|
||||
page - 1,
|
||||
filtered ? filtered.join(",") : ""
|
||||
);
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * 10 + index + 1;
|
||||
});
|
||||
|
||||
setDataTable(contentData);
|
||||
setTotalData(data?.totalElements);
|
||||
setTotalPage(data?.totalPages);
|
||||
close();
|
||||
} catch (error) {
|
||||
console.error("Error fetching tasks:", error);
|
||||
}
|
||||
}
|
||||
|
||||
const handleFilter = (id: string, checked: boolean) => {
|
||||
let temp = [...filtered];
|
||||
if (checked) {
|
||||
temp = [...temp, id];
|
||||
} else {
|
||||
temp = temp.filter((a) => a !== id);
|
||||
}
|
||||
setFiltered(temp);
|
||||
console.log("sss", temp);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||
<div className="flex justify-between mb-10 items-center">
|
||||
<p className="text-xl font-medium text-default-900">Daftar Akun</p>
|
||||
<div className="flex flex-row gap-3">
|
||||
<Link href="/admin/broadcast/campaign-list/account-list/create">
|
||||
<Button color="primary" size="md" className="text-sm">
|
||||
<Icon icon="tdesign:user-add-filled" />
|
||||
Tambah Akun
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/admin/broadcast/campaign-list/import">
|
||||
<Button color="success" size="md" className="text-sm">
|
||||
<UserIcon />
|
||||
Import Akun
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button size="md" variant="outline">
|
||||
Filter
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-80 ">
|
||||
<div className="flex flex-col gap-2 px-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<p>Filter</p>
|
||||
<a
|
||||
onClick={() => fetchData()}
|
||||
className="cursor-pointer text-primary"
|
||||
>
|
||||
Simpan
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 overflow-auto max-h-[300px] text-xs custom-scrollbar-table">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="accepted"
|
||||
checked={filtered.includes("polri")}
|
||||
onCheckedChange={(e) => handleFilter("polri", Boolean(e))}
|
||||
/>
|
||||
<label
|
||||
htmlFor="accepted"
|
||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
POLRI
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="accepted"
|
||||
checked={filtered.includes("jurnalis")}
|
||||
onCheckedChange={(e) =>
|
||||
handleFilter("jurnalis", Boolean(e))
|
||||
}
|
||||
/>
|
||||
<label
|
||||
htmlFor="accepted"
|
||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
JURNALIS
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="accepted"
|
||||
checked={filtered.includes("umum")}
|
||||
onCheckedChange={(e) => handleFilter("umum", Boolean(e))}
|
||||
/>
|
||||
<label
|
||||
htmlFor="accepted"
|
||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
UMUM
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="accepted"
|
||||
checked={filtered.includes("ksp")}
|
||||
onCheckedChange={(e) => handleFilter("ksp", Boolean(e))}
|
||||
/>
|
||||
<label
|
||||
htmlFor="accepted"
|
||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
KSP
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<Table className="overflow-hidden">
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
className="h-[75px]"
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<TablePagination
|
||||
table={table}
|
||||
totalData={totalData}
|
||||
totalPage={totalPage}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccountListTable;
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
"use client";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import { z } from "zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { format } from "date-fns";
|
||||
import { CalendarIcon } from "lucide-react";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { getOnlyDate } from "@/utils/globals";
|
||||
import {
|
||||
saveMediaBlastAccount,
|
||||
saveMediaBlastCampaign,
|
||||
} from "@/service/broadcast/broadcast";
|
||||
import { error } from "@/config/swal";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
|
||||
const FormSchema = z.object({
|
||||
name: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
accountType: z
|
||||
.array(z.string())
|
||||
.refine((value) => value.some((item) => item), {
|
||||
message: "Required",
|
||||
}),
|
||||
accountCategory: z.enum(["polri", "jurnalis", "umumu", "ksp"], {
|
||||
required_error: "Required",
|
||||
}),
|
||||
email: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
whatsapp: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
|
||||
export default function CreateAccountForBroadcast() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: { accountType: [] },
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function successSubmit() {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
router.push("/admin/broadcast/campaign-list/account-list");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||
const reqData = {
|
||||
accountName: data.name,
|
||||
accountType: data.accountType.join(","),
|
||||
accountCategory: data.accountCategory,
|
||||
emailAddress: data.email,
|
||||
whatsappNumber: data.whatsapp,
|
||||
};
|
||||
console.log("data", data);
|
||||
|
||||
const response = await saveMediaBlastAccount(reqData);
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
successSubmit();
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-3 bg-white rounded-sm p-4"
|
||||
>
|
||||
<p className="fonnt-semibold">Account</p>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama</FormLabel>
|
||||
<Input
|
||||
value={field.value}
|
||||
placeholder="Masukkan nama"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="accountType"
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<FormLabel>Tipe Akun</FormLabel>
|
||||
<div className="flex flex-row gap-2">
|
||||
{" "}
|
||||
<FormField
|
||||
key="wa"
|
||||
control={form.control}
|
||||
name="accountType"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key="wa"
|
||||
className="flex flex-row items-start space-x-3 space-y-0"
|
||||
>
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value?.includes("wa")}
|
||||
onCheckedChange={(checked) => {
|
||||
return checked
|
||||
? field.onChange([...field.value, "wa"])
|
||||
: field.onChange(
|
||||
field.value?.filter(
|
||||
(value) => value !== "wa"
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
Whatsapp
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
key="email"
|
||||
control={form.control}
|
||||
name="accountType"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key="email"
|
||||
className="flex flex-row items-start space-x-3 space-y-0"
|
||||
>
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value?.includes("email")}
|
||||
onCheckedChange={(checked) => {
|
||||
return checked
|
||||
? field.onChange([...field.value, "email"])
|
||||
: field.onChange(
|
||||
field.value?.filter(
|
||||
(value) => value !== "email"
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">Email</FormLabel>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="accountCategory"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-3">
|
||||
<FormLabel>Kategori</FormLabel>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
className="flex flex-row gap-2"
|
||||
>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="polri" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">POLRI</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="jurnalis" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">JURNALIS</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="umum" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">UMUM</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="ksp" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">KSP</FormLabel>
|
||||
</FormItem>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama</FormLabel>
|
||||
<Input
|
||||
type="email"
|
||||
value={field.value}
|
||||
placeholder="Masukkan email"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="whatsapp"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
value={field.value}
|
||||
placeholder="Masukkan whatsapp"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex flex-row gap-2 mt-4 pt-4">
|
||||
<Button
|
||||
size="md"
|
||||
type="button"
|
||||
variant="outline"
|
||||
color="destructive"
|
||||
className="text-xs"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="md" type="submit" color="primary" className="text-xs">
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,325 @@
|
|||
"use client";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import { z } from "zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { format } from "date-fns";
|
||||
import { CalendarIcon } from "lucide-react";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { getOnlyDate } from "@/utils/globals";
|
||||
import {
|
||||
getMediaBlastAccount,
|
||||
saveMediaBlastAccount,
|
||||
saveMediaBlastCampaign,
|
||||
} from "@/service/broadcast/broadcast";
|
||||
import { error } from "@/config/swal";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const FormSchema = z.object({
|
||||
name: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
accountType: z
|
||||
.array(z.string())
|
||||
.refine((value) => value.some((item) => item), {
|
||||
message: "Required",
|
||||
}),
|
||||
accountCategory: z.enum(["polri", "jurnalis", "umumu", "ksp"], {
|
||||
required_error: "Required",
|
||||
}),
|
||||
email: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
whatsapp: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
|
||||
export default function EditAccountForBroadcast() {
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: { accountType: [] },
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
async function getDetailData() {
|
||||
const response = await getMediaBlastAccount(String(id));
|
||||
const details = response?.data?.data;
|
||||
console.log("new", details);
|
||||
form.setValue("name", details.accountName);
|
||||
form.setValue("email", details?.emailAddress);
|
||||
form.setValue("whatsapp", details?.whatsappNumber);
|
||||
form.setValue("accountCategory", details?.accountCategory);
|
||||
form.setValue("accountType", details?.accountType.split(","));
|
||||
}
|
||||
|
||||
getDetailData();
|
||||
}, [id]);
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function successSubmit() {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
router.push("/admin/broadcast/campaign-list/account-list");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||
const reqData = {
|
||||
id: String(id),
|
||||
accountName: data.name,
|
||||
accountType: data.accountType.join(","),
|
||||
accountCategory: data.accountCategory,
|
||||
emailAddress: data.email,
|
||||
whatsappNumber: data.whatsapp,
|
||||
};
|
||||
console.log("data", data);
|
||||
|
||||
const response = await saveMediaBlastAccount(reqData);
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
successSubmit();
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-3 bg-white rounded-sm p-4"
|
||||
>
|
||||
<p className="fonnt-semibold">Account</p>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama</FormLabel>
|
||||
<Input
|
||||
value={field.value}
|
||||
placeholder="Masukkan nama"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="accountType"
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<FormLabel>Tipe Akun</FormLabel>
|
||||
<div className="flex flex-row gap-2">
|
||||
<FormField
|
||||
key="wa"
|
||||
control={form.control}
|
||||
name="accountType"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key="wa"
|
||||
className="flex flex-row items-start space-x-3 space-y-0"
|
||||
>
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value?.includes("wa")}
|
||||
onCheckedChange={(checked) => {
|
||||
return checked
|
||||
? field.onChange([...field.value, "wa"])
|
||||
: field.onChange(
|
||||
field.value?.filter(
|
||||
(value) => value !== "wa"
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
Whatsapp
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
key="email"
|
||||
control={form.control}
|
||||
name="accountType"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key="email"
|
||||
className="flex flex-row items-start space-x-3 space-y-0"
|
||||
>
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value?.includes("email")}
|
||||
onCheckedChange={(checked) => {
|
||||
return checked
|
||||
? field.onChange([...field.value, "email"])
|
||||
: field.onChange(
|
||||
field.value?.filter(
|
||||
(value) => value !== "email"
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">Email</FormLabel>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="accountCategory"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-3">
|
||||
<FormLabel>Kategori</FormLabel>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
className="flex flex-row gap-2"
|
||||
>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="polri" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">POLRI</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="jurnalis" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">JURNALIS</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="umum" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">UMUM</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="ksp" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">KSP</FormLabel>
|
||||
</FormItem>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama</FormLabel>
|
||||
<Input
|
||||
type="email"
|
||||
value={field.value}
|
||||
placeholder="Masukkan email"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="whatsapp"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
value={field.value}
|
||||
placeholder="Masukkan whatsapp"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex flex-row gap-2 mt-4 pt-4">
|
||||
<Button
|
||||
size="md"
|
||||
type="button"
|
||||
variant="outline"
|
||||
color="destructive"
|
||||
className="text-xs"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="md" type="submit" color="primary" className="text-xs">
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import AccountListTable from "./component/table";
|
||||
|
||||
export default function AdminCampaignList() {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<AccountListTable />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
import * as React from "react";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
|
||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuItem,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
accessorKey: "no",
|
||||
header: "No",
|
||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "title",
|
||||
header: "Judul",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("title")}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "sendTime",
|
||||
header: "Tanggal & Waktu",
|
||||
cell: ({ row }) => <span>{row.getValue("sendTime")}</span>,
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: "Status",
|
||||
cell: ({ row }) => (
|
||||
<Badge
|
||||
className={`text-center items-center text-white ${
|
||||
row.getValue("status") == "done"
|
||||
? "bg-blue-500"
|
||||
: row.getValue("status") == "waiting"
|
||||
? "bg-yellow-400"
|
||||
: "bg-red-600"
|
||||
}`}
|
||||
>
|
||||
{row.getValue("status") == "done"
|
||||
? "Selesai"
|
||||
: row.getValue("status") == "waiting"
|
||||
? "Proses"
|
||||
: "Gagal"}
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
id: "actions",
|
||||
accessorKey: "action",
|
||||
header: "Actions",
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
size="icon"
|
||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||
>
|
||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<Link
|
||||
href={`/admin/broadcast/campaign-list/detail/${row.original.id}`}
|
||||
>
|
||||
Detail
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<Link
|
||||
href={`//admin/broadcast/campaign-list/edit/${row.original.id}`}
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<a>Delete</a>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default columns;
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
PaginationState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import {
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Eye,
|
||||
MoreVertical,
|
||||
Search,
|
||||
SquarePen,
|
||||
Trash2,
|
||||
TrendingDown,
|
||||
TrendingUp,
|
||||
UserIcon,
|
||||
} from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||
import { paginationBlog } from "@/service/blog/blog";
|
||||
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import columns from "./column";
|
||||
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import {
|
||||
getMediaBlastCampaignPage,
|
||||
listDataMedia,
|
||||
} from "@/service/broadcast/broadcast";
|
||||
import { listEnableCategory } from "@/service/content/content";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { close, loading } from "@/config/swal";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { NewCampaignIcon } from "@/components/icon";
|
||||
|
||||
const CampaignListTable = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[]
|
||||
);
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({});
|
||||
const [rowSelection, setRowSelection] = React.useState({});
|
||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
const [page, setPage] = React.useState(1);
|
||||
const [totalPage, setTotalPage] = React.useState(1);
|
||||
const table = useReactTable({
|
||||
data: dataTable,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
onPaginationChange: setPagination,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
pagination,
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
const pageFromUrl = searchParams?.get("page");
|
||||
if (pageFromUrl) {
|
||||
setPage(Number(pageFromUrl));
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchData();
|
||||
}, [page]);
|
||||
|
||||
async function fetchData() {
|
||||
try {
|
||||
loading();
|
||||
const res = await getMediaBlastCampaignPage(page - 1);
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * 10 + index + 1;
|
||||
});
|
||||
|
||||
console.log("contentData : ", data);
|
||||
|
||||
setDataTable(contentData);
|
||||
setTotalData(data?.totalElements);
|
||||
setTotalPage(data?.totalPages);
|
||||
close();
|
||||
} catch (error) {
|
||||
console.error("Error fetching tasks:", error);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||
<div className="flex justify-between mb-10 items-center">
|
||||
<p className="text-xl font-medium text-default-900">Daftar Campaign</p>
|
||||
<div className="flex flex-row gap-2">
|
||||
<Link href="/admin/broadcast/campaign-list/account-list">
|
||||
<Button color="primary" size="md" className="text-sm">
|
||||
<UserIcon />
|
||||
Daftar Akun
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/admin/broadcast/campaign-list/create">
|
||||
<Button color="primary" size="md" className="text-sm">
|
||||
<NewCampaignIcon size={23} />
|
||||
Buat Campaign Baru
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Table className="overflow-hidden">
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
className="h-[75px]"
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<TablePagination
|
||||
table={table}
|
||||
totalData={totalData}
|
||||
totalPage={totalPage}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CampaignListTable;
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
"use client";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import { z } from "zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { format } from "date-fns";
|
||||
import { CalendarIcon } from "lucide-react";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { getOnlyDate } from "@/utils/globals";
|
||||
import { saveMediaBlastCampaign } from "@/service/broadcast/broadcast";
|
||||
import { error } from "@/config/swal";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
|
||||
const FormSchema = z.object({
|
||||
date: z.date({
|
||||
required_error: "Required",
|
||||
}),
|
||||
title: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
time: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
|
||||
export default function CreateCampaign() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function successSubmit() {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
router.push("/admin/broadcast/campaign-list");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||
const newDate = getOnlyDate(data.date).split("-");
|
||||
const dates = `${newDate[2]}-${newDate[1]}-${newDate[0]}`;
|
||||
const reqData = {
|
||||
title: data.title,
|
||||
sendTime: `${dates} ${data.time}:00`,
|
||||
status: "waiting",
|
||||
};
|
||||
|
||||
const response = await saveMediaBlastCampaign(reqData);
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
successSubmit();
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-3 bg-white rounded-sm p-4"
|
||||
>
|
||||
<p className="fonnt-semibold">Campaign</p>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Judul</FormLabel>
|
||||
<Input
|
||||
value={field.value}
|
||||
placeholder="Masukkan Judul"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<p className="text-sm">Jadwal Kirim</p>
|
||||
<div className="flex flex-row gap-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="date"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
size="md"
|
||||
className={cn(
|
||||
"w-[200px] justify-start text-left font-normal border-gray-200",
|
||||
!field.value && "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{field.value ? (
|
||||
format(field.value, "dd-MM-yyyy")
|
||||
) : (
|
||||
<span>Masukkan Bulan</span>
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={field.value}
|
||||
onSelect={field.onChange}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="time"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<Input
|
||||
type="time"
|
||||
className="max-w-[100px]"
|
||||
value={field.value}
|
||||
placeholder="Masukkan Judul Perencanaan"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row gap-2 mt-4 pt-4">
|
||||
<Button
|
||||
size="md"
|
||||
type="button"
|
||||
variant="outline"
|
||||
color="destructive"
|
||||
className="text-xs"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="md" type="submit" color="primary" className="text-xs">
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
"use client";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import { z } from "zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { format } from "date-fns";
|
||||
import { CalendarIcon } from "lucide-react";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { getOnlyDate } from "@/utils/globals";
|
||||
import {
|
||||
getMediaBlastCampaignById,
|
||||
saveMediaBlastCampaign,
|
||||
} from "@/service/broadcast/broadcast";
|
||||
import { error } from "@/config/swal";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const FormSchema = z.object({
|
||||
date: z.date({
|
||||
required_error: "Required",
|
||||
}),
|
||||
title: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
time: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
status: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
|
||||
export default function EditCampaign() {
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
async function getInitData() {
|
||||
const response = await getMediaBlastCampaignById(String(id));
|
||||
const date = response?.data?.data?.sendTime.split(" ");
|
||||
const dateInput: Date = new Date(date[0]);
|
||||
|
||||
const time = date[1].split(":");
|
||||
|
||||
form.setValue("title", response?.data?.data?.title);
|
||||
form.setValue("status", response?.data?.data?.status);
|
||||
form.setValue("date", new Date(format(dateInput, "dd-MM-yyyy")));
|
||||
form.setValue("time", `${time[0]}:${time[1]}`);
|
||||
}
|
||||
|
||||
if (id != undefined) {
|
||||
getInitData();
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function successSubmit() {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
router.push("/admin/broadcast/campaign-list");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||
const newDate = getOnlyDate(data.date).split("-");
|
||||
const dates = `${newDate[2]}-${newDate[1]}-${newDate[0]}`;
|
||||
const reqData = {
|
||||
id: String(id),
|
||||
title: data.title,
|
||||
sendTime: `${dates} ${data.time}:00`,
|
||||
status: data.status,
|
||||
};
|
||||
|
||||
const response = await saveMediaBlastCampaign(reqData);
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
successSubmit();
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-3 bg-white rounded-sm p-4"
|
||||
>
|
||||
<p className="fonnt-semibold">Campaign</p>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Judul</FormLabel>
|
||||
<Input
|
||||
value={field.value}
|
||||
placeholder="Masukkan Judul"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<p className="text-sm">Jadwal Kirim</p>
|
||||
<div className="flex flex-row gap-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="date"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
size="md"
|
||||
className={cn(
|
||||
"w-[200px] justify-start text-left font-normal border-gray-200",
|
||||
!field.value && "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{field.value ? (
|
||||
format(field.value, "dd-MM-yyyy")
|
||||
) : (
|
||||
<span>Masukkan Bulan</span>
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={field.value}
|
||||
onSelect={field.onChange}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="time"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<Input
|
||||
type="time"
|
||||
className="max-w-[100px]"
|
||||
value={field.value}
|
||||
placeholder="Masukkan Judul Perencanaan"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row gap-2 mt-4 pt-4">
|
||||
<Button
|
||||
size="md"
|
||||
type="button"
|
||||
variant="outline"
|
||||
color="destructive"
|
||||
className="text-xs"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="md" type="submit" color="primary" className="text-xs">
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import CampaignListTable from "./component/table";
|
||||
|
||||
export default function AdminCampaignList() {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<CampaignListTable />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
import * as React from "react";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
|
||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuItem,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import {
|
||||
formatDateToIndonesian,
|
||||
getOnlyDate,
|
||||
htmlToString,
|
||||
} from "@/utils/globals";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible";
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
accessorKey: "no",
|
||||
header: "No",
|
||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "title",
|
||||
header: "Judul",
|
||||
cell: ({ row }) => <span>{row.getValue("title")}</span>,
|
||||
},
|
||||
{
|
||||
accessorKey: "categoryName",
|
||||
header: "Kategori",
|
||||
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
|
||||
},
|
||||
{
|
||||
accessorKey: "createdAt",
|
||||
header: "Tanggal Unggah",
|
||||
cell: ({ row }) => (
|
||||
<span>{formatDateToIndonesian(row.getValue("createdAt"))}</span>
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "statusName",
|
||||
header: "Status",
|
||||
cell: ({ row }) => <span>{row.getValue("statusName")}</span>,
|
||||
},
|
||||
|
||||
{
|
||||
id: "actions",
|
||||
accessorKey: "action",
|
||||
header: "Actions",
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
size="icon"
|
||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||
>
|
||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<Link
|
||||
href={`/contributor/content/image/detail/${row.original.id}`}
|
||||
>
|
||||
Detail
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<Link href={`/admin/broadcast/email/${row.original.id}`}>
|
||||
Email Blast
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<Link href={`/admin/broadcast/whatsapp/${row.original.id}`}>
|
||||
Whatsapp Blast
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default columns;
|
||||
|
|
@ -0,0 +1,403 @@
|
|||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
PaginationState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import {
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Eye,
|
||||
MoreVertical,
|
||||
Search,
|
||||
SquarePen,
|
||||
Trash2,
|
||||
TrendingDown,
|
||||
TrendingUp,
|
||||
UserIcon,
|
||||
} from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||
import { paginationBlog } from "@/service/blog/blog";
|
||||
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import columns from "./column";
|
||||
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { listDataMedia } from "@/service/broadcast/broadcast";
|
||||
import { listEnableCategory } from "@/service/content/content";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { close, loading } from "@/config/swal";
|
||||
import { Link } from "@/i18n/routing";
|
||||
|
||||
const BroadcastTable = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [showData, setShowData] = React.useState("10");
|
||||
const [categories, setCategories] = React.useState<any>();
|
||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[]
|
||||
);
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({});
|
||||
const [rowSelection, setRowSelection] = React.useState({});
|
||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: Number(showData),
|
||||
});
|
||||
const [categoryFilter, setCategoryFilter] = React.useState<number[]>([]);
|
||||
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||
const [page, setPage] = React.useState(1);
|
||||
const [totalPage, setTotalPage] = React.useState(1);
|
||||
const table = useReactTable({
|
||||
data: dataTable,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
onPaginationChange: setPagination,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
pagination,
|
||||
},
|
||||
});
|
||||
|
||||
let typingTimer: any;
|
||||
const doneTypingInterval = 1500;
|
||||
|
||||
const handleKeyUp = () => {
|
||||
clearTimeout(typingTimer);
|
||||
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||
};
|
||||
|
||||
const handleKeyDown = () => {
|
||||
clearTimeout(typingTimer);
|
||||
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||
};
|
||||
|
||||
async function doneTyping() {
|
||||
fetchData();
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
const pageFromUrl = searchParams?.get("page");
|
||||
if (pageFromUrl) {
|
||||
setPage(Number(pageFromUrl));
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchData();
|
||||
setPagination({
|
||||
pageIndex: 0,
|
||||
pageSize: Number(showData),
|
||||
});
|
||||
}, [page, showData]);
|
||||
|
||||
async function fetchData() {
|
||||
try {
|
||||
loading();
|
||||
const res = await listDataMedia(
|
||||
page - 1,
|
||||
showData,
|
||||
"",
|
||||
categoryFilter?.sort().join(","),
|
||||
statusFilter?.sort().join(",")
|
||||
);
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * Number(showData) + index + 1;
|
||||
});
|
||||
|
||||
console.log("contentData : ", data);
|
||||
|
||||
setDataTable(contentData);
|
||||
setTotalData(data?.totalElements);
|
||||
setTotalPage(data?.totalPages);
|
||||
close();
|
||||
} catch (error) {
|
||||
console.error("Error fetching tasks:", error);
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
getCategories();
|
||||
}, []);
|
||||
|
||||
async function getCategories() {
|
||||
const category = await listEnableCategory("");
|
||||
const resCategory = category?.data?.data?.content;
|
||||
setCategories(resCategory);
|
||||
}
|
||||
|
||||
const handleChange = (type: string, id: number, checked: boolean) => {
|
||||
if (type === "category") {
|
||||
if (checked) {
|
||||
const temp: number[] = [...categoryFilter];
|
||||
temp.push(id);
|
||||
setCategoryFilter(temp);
|
||||
} else {
|
||||
const temp = categoryFilter.filter((a) => a !== id);
|
||||
setCategoryFilter(temp);
|
||||
}
|
||||
} else {
|
||||
if (checked) {
|
||||
const temp: number[] = [...statusFilter];
|
||||
temp.push(id);
|
||||
setStatusFilter(temp);
|
||||
} else {
|
||||
const temp = statusFilter.filter((a) => a !== id);
|
||||
setStatusFilter(temp);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||
<div className="flex justify-between mb-10 items-center">
|
||||
<p className="text-xl font-medium text-default-900">Brodcast</p>
|
||||
<Link href="/admin/broadcast/campaign-list">
|
||||
<Button color="primary" size="md" className="text-sm">
|
||||
<UserIcon />
|
||||
Daftar Campaign
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between ">
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
onKeyUp={handleKeyUp}
|
||||
onKeyDown={handleKeyDown}
|
||||
className="max-w-[300px]"
|
||||
/>
|
||||
<div className="flex flex-row gap-2">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button size="md" variant="outline">
|
||||
1 - {showData} Data
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56">
|
||||
<DropdownMenuRadioGroup
|
||||
value={showData}
|
||||
onValueChange={setShowData}
|
||||
>
|
||||
<DropdownMenuRadioItem value="10">
|
||||
1 - 10 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="20">
|
||||
1 - 20 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="25">
|
||||
1 - 25 Data
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="50">
|
||||
1 - 50 Data
|
||||
</DropdownMenuRadioItem>
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button size="md" variant="outline">
|
||||
Filter
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-80 ">
|
||||
<div className="flex flex-col gap-2 px-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<p>Filter</p>
|
||||
<a
|
||||
onClick={() => fetchData()}
|
||||
className="cursor-pointer text-primary"
|
||||
>
|
||||
Simpan
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 overflow-auto max-h-[300px] text-xs custom-scrollbar-table">
|
||||
<p>Kategory</p>
|
||||
{categories?.map((category: any) => (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={category.id}
|
||||
checked={categoryFilter.includes(category.id)}
|
||||
onCheckedChange={(e) =>
|
||||
handleChange("category", category.id, Boolean(e))
|
||||
}
|
||||
/>
|
||||
<label
|
||||
htmlFor={category.id}
|
||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
{category.name}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
<p className="mt-3">Status</p>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="accepted"
|
||||
checked={statusFilter.includes(1)}
|
||||
onCheckedChange={(e) =>
|
||||
handleChange("status", 1, Boolean(e))
|
||||
}
|
||||
/>
|
||||
<label
|
||||
htmlFor="accepted"
|
||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Menunggu Review
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="accepted"
|
||||
checked={statusFilter.includes(2)}
|
||||
onCheckedChange={(e) =>
|
||||
handleChange("status", 2, Boolean(e))
|
||||
}
|
||||
/>
|
||||
<label
|
||||
htmlFor="accepted"
|
||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Diterima
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="accepted"
|
||||
checked={statusFilter.includes(3)}
|
||||
onCheckedChange={(e) =>
|
||||
handleChange("status", 3, Boolean(e))
|
||||
}
|
||||
/>
|
||||
<label
|
||||
htmlFor="accepted"
|
||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Minta Update{" "}
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="accepted"
|
||||
checked={statusFilter.includes(4)}
|
||||
onCheckedChange={(e) =>
|
||||
handleChange("status", 4, Boolean(e))
|
||||
}
|
||||
/>
|
||||
<label
|
||||
htmlFor="accepted"
|
||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Ditolak
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
<Table className="overflow-hidden">
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
className="h-[75px]"
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<TablePagination
|
||||
table={table}
|
||||
totalData={totalData}
|
||||
totalPage={totalPage}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BroadcastTable;
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import ContentBlast from "@/components/form/broadcast/content-blast-form";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
|
||||
export default function CreateEmailBlast() {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<ContentBlast type="email" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import BroadcastTable from "./component/table";
|
||||
|
||||
export default function AdminBroadcast() {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<BroadcastTable />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import ContentBlast from "@/components/form/broadcast/content-blast-form";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
|
||||
export default function CreateWABlast() {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<ContentBlast type="wa" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
import * as React from "react";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
|
||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuItem,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import StatusToogle from "./status";
|
||||
import { error } from "@/config/swal";
|
||||
import { deleteCategory } from "@/service/settings/settings";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import EditCategoryModal from "./edit";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import {
|
||||
Menubar,
|
||||
MenubarContent,
|
||||
MenubarMenu,
|
||||
MenubarTrigger,
|
||||
} from "@/components/ui/menubar";
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
accessorKey: "no",
|
||||
header: "No",
|
||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: "Nama Kategori",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("name")}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "contentType",
|
||||
header: "Tipe Konten",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("contentType")}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "isInternational",
|
||||
header: "Wilayah",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">
|
||||
{row.getValue("isInternational") ? "INT" : "ID"}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: "Status",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">
|
||||
<StatusToogle id={row.original.id} initValue={row.original.isPublish} />
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
accessorKey: "action",
|
||||
header: "Actions",
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
|
||||
const categoryDelete = async (id: number) => {
|
||||
const response = await deleteCategory(id);
|
||||
console.log(response);
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return false;
|
||||
}
|
||||
toast({
|
||||
title: "Sukses",
|
||||
description: "Berhasil Delete",
|
||||
});
|
||||
router.push("/admin/settings/category?dataChange=true");
|
||||
};
|
||||
return (
|
||||
// <Popover>
|
||||
// <PopoverTrigger>
|
||||
// <Button
|
||||
// size="icon"
|
||||
// className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||
// >
|
||||
// <MoreVertical className="h-4 w-4 text-default-800" />
|
||||
// </Button>
|
||||
// </PopoverTrigger>
|
||||
// <PopoverContent className="w-40">
|
||||
// <div className="flex flex-col">
|
||||
// <EditCategoryModal />
|
||||
// <a onClick={() => categoryDelete(row.original.id)}>Delete</a>
|
||||
// </div>
|
||||
// </PopoverContent>
|
||||
// </Popover>
|
||||
|
||||
// <DropdownMenu>
|
||||
// <DropdownMenuTrigger asChild>
|
||||
// <Button
|
||||
// size="icon"
|
||||
// className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||
// >
|
||||
// <MoreVertical className="h-4 w-4 text-default-800" />
|
||||
// </Button>
|
||||
// </DropdownMenuTrigger>
|
||||
// <DropdownMenuContent className="p-0" align="end">
|
||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
// <EditCategoryModal />
|
||||
// </DropdownMenuItem>
|
||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
// <a onClick={() => categoryDelete(row.original.id)}>Delete</a>
|
||||
// </DropdownMenuItem>
|
||||
// </DropdownMenuContent>
|
||||
// </DropdownMenu>
|
||||
|
||||
<Menubar className="border-none">
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger>
|
||||
<Button
|
||||
size="icon"
|
||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||
>
|
||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||
</Button>
|
||||
</MenubarTrigger>
|
||||
<MenubarContent className="flex flex-col gap-2 justify-center items-start p-4">
|
||||
<EditCategoryModal data={row.original} />
|
||||
<a
|
||||
onClick={() => categoryDelete(row.original.id)}
|
||||
className="hover:underline cursor-pointer hover:text-destructive"
|
||||
>
|
||||
Delete
|
||||
</a>
|
||||
</MenubarContent>
|
||||
</MenubarMenu>
|
||||
</Menubar>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default columns;
|
||||
|
|
@ -0,0 +1,430 @@
|
|||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { z } from "zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { getUserRoles, postCategory } from "@/service/settings/settings";
|
||||
import { useEffect, useState } from "react";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { close, error, loading } from "@/config/swal";
|
||||
|
||||
const FormSchema = z.object({
|
||||
title: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
description: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
contentType: z
|
||||
.array(z.string())
|
||||
.refine((value) => value.some((item) => item), {
|
||||
message: "Required",
|
||||
}),
|
||||
selectedUser: z
|
||||
.array(z.string())
|
||||
.refine((value) => value.some((item) => item), {
|
||||
message: "Required",
|
||||
}),
|
||||
publishTo: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
file: z
|
||||
.instanceof(File, {
|
||||
message: "Invalid file format",
|
||||
})
|
||||
.optional()
|
||||
.refine((file) => file && file.size > 0, {
|
||||
message: "File is required",
|
||||
}),
|
||||
});
|
||||
|
||||
const listContent = [
|
||||
{
|
||||
id: "1",
|
||||
name: "Foto",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "Audio Visual",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "Teks",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "Audio",
|
||||
},
|
||||
];
|
||||
|
||||
export default function CreateCategoryModal() {
|
||||
const router = useRouter();
|
||||
const [userList, setUserList] = useState<
|
||||
{ id: string; name: string; isInternal: boolean }[]
|
||||
>([]);
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: { contentType: [], selectedUser: [], publishTo: "national" },
|
||||
});
|
||||
|
||||
const contentType = form.watch("contentType");
|
||||
|
||||
const isAllContentChecked = listContent.every((item) =>
|
||||
contentType?.includes(item.id)
|
||||
);
|
||||
|
||||
const users = form.watch("selectedUser");
|
||||
|
||||
const isAllUserChecked = userList.every((item) => users?.includes(item.id));
|
||||
|
||||
useEffect(() => {
|
||||
getRoles();
|
||||
}, []);
|
||||
|
||||
async function getRoles() {
|
||||
const response = await getUserRoles();
|
||||
let dataRoles = response?.data?.data;
|
||||
for (let i = 0; i < dataRoles.length; i++) {
|
||||
dataRoles[i].id = String(dataRoles[i].id);
|
||||
}
|
||||
setUserList(dataRoles);
|
||||
console.log("dasda", dataRoles);
|
||||
}
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
if (data.file instanceof Blob) {
|
||||
const formMedia = new FormData();
|
||||
|
||||
loading();
|
||||
|
||||
formMedia.append("name", data.title);
|
||||
formMedia.append("description", data.description);
|
||||
formMedia.append("mediaTypes", data.contentType.join(","));
|
||||
formMedia.append("file", data.file);
|
||||
formMedia.append("publishedFor", data.selectedUser.sort().join(","));
|
||||
formMedia.append(
|
||||
"isInt",
|
||||
data.publishTo === "national" ? "false" : "true"
|
||||
);
|
||||
console.log(formMedia);
|
||||
const response = await postCategory(formMedia);
|
||||
close();
|
||||
if (response?.error == true) {
|
||||
error(response.message);
|
||||
return false;
|
||||
}
|
||||
router.push("/admin/settings/category?dataChange=true");
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button color="primary" size="md">
|
||||
Tambah Kategori
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent size="md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Tambah Kategori</DialogTitle>
|
||||
</DialogHeader>
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-3 bg-white rounded-sm"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="contentType"
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<FormLabel>Tipe Konten</FormLabel>
|
||||
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
id="all"
|
||||
checked={isAllContentChecked}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
form.setValue(
|
||||
"contentType",
|
||||
listContent.map((item) => item.id)
|
||||
);
|
||||
} else {
|
||||
form.setValue("contentType", []);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="all" className="text-sm">
|
||||
Semua
|
||||
</label>
|
||||
</div>
|
||||
{listContent.map((item) => (
|
||||
<FormField
|
||||
key={item.id}
|
||||
control={form.control}
|
||||
name="contentType"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key={item.id}
|
||||
className="flex flex-row items-start"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value?.includes(item.id)}
|
||||
onCheckedChange={(checked) => {
|
||||
return checked
|
||||
? field.onChange([
|
||||
...field.value,
|
||||
item.id,
|
||||
])
|
||||
: field.onChange(
|
||||
field.value?.filter(
|
||||
(value) => value !== item.id
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
{item.name}
|
||||
</FormLabel>
|
||||
</div>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="selectedUser"
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<FormLabel>Target Publish</FormLabel>
|
||||
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<div className="flex gap-3 items-center">
|
||||
<Checkbox
|
||||
id="all"
|
||||
checked={isAllUserChecked}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
form.setValue(
|
||||
"selectedUser",
|
||||
userList.map((item) => item.id)
|
||||
);
|
||||
} else {
|
||||
form.setValue("selectedUser", []);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="all" className="text-sm">
|
||||
Semua
|
||||
</label>
|
||||
</div>
|
||||
{userList.map((item) => (
|
||||
<FormField
|
||||
key={item.id}
|
||||
control={form.control}
|
||||
name="selectedUser"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key={item.id}
|
||||
className="flex flex-row items-start "
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value?.includes(item.id)}
|
||||
onCheckedChange={(checked) => {
|
||||
return checked
|
||||
? field.onChange([
|
||||
...field.value,
|
||||
item.id,
|
||||
])
|
||||
: field.onChange(
|
||||
field.value?.filter(
|
||||
(value) => value !== item.id
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
{item.name}
|
||||
</FormLabel>
|
||||
</div>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="publishTo"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-3">
|
||||
<FormLabel>Wilayah Publish</FormLabel>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
className="flex flex-row gap-2"
|
||||
>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="national" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">Nasional</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="international" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
Internasional
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama Kategori</FormLabel>
|
||||
<Input
|
||||
value={field.value}
|
||||
placeholder="Masukkan Nama Kategori"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="file"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama Kategori</FormLabel>
|
||||
{!field.value && (
|
||||
<div className="flex items-center justify-center w-full">
|
||||
<label
|
||||
htmlFor="dropzone-file"
|
||||
className="flex flex-col items-center justify-center w-full h-28 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-gray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
|
||||
>
|
||||
<div className="flex flex-col items-center justify-center pt-5 pb-6">
|
||||
<p className="mb-2 text-sm text-gray-500 dark:text-gray-400">
|
||||
<span className="font-semibold">Unggah Gambar</span>
|
||||
</p>
|
||||
</div>
|
||||
<input
|
||||
id="dropzone-file"
|
||||
type="file"
|
||||
className="hidden"
|
||||
onChange={(e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
field.onChange(file);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{field.value && (
|
||||
<div className="flex flex-row gap-2">
|
||||
<img
|
||||
src={URL.createObjectURL(field.value)}
|
||||
className="w-[30%]"
|
||||
alt="thumbnail"
|
||||
/>
|
||||
<a onClick={() => form.setValue("file", undefined)}>
|
||||
<Icon icon="fa-solid:times" color="red" />
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="description"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Deskripsi</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
placeholder="Deskripsi"
|
||||
// className="resize-none"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
size="md"
|
||||
className="text-xs"
|
||||
>
|
||||
Tambah Kategori
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,443 @@
|
|||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { z } from "zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { getUserRoles, postCategory } from "@/service/settings/settings";
|
||||
import { useEffect, useState } from "react";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { close, error, loading } from "@/config/swal";
|
||||
|
||||
const FormSchema = z.object({
|
||||
title: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
description: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
contentType: z
|
||||
.array(z.string())
|
||||
.refine((value) => value.some((item) => item), {
|
||||
message: "Required",
|
||||
}),
|
||||
selectedUser: z
|
||||
.array(z.string())
|
||||
.refine((value) => value.some((item) => item), {
|
||||
message: "Required",
|
||||
}),
|
||||
publishTo: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
file: z
|
||||
.instanceof(File, {
|
||||
message: "Invalid file format",
|
||||
})
|
||||
.optional()
|
||||
.refine((file) => file && file.size > 0, {
|
||||
message: "File is required",
|
||||
}),
|
||||
id: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
|
||||
const listContent = [
|
||||
{
|
||||
id: "1",
|
||||
name: "Foto",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "Audio Visual",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "Teks",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "Audio",
|
||||
},
|
||||
];
|
||||
|
||||
export default function EditCategoryModal(props: { data: any }) {
|
||||
const { data } = props;
|
||||
const router = useRouter();
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [userList, setUserList] = useState<
|
||||
{ id: string; name: string; isInternal: boolean }[]
|
||||
>([]);
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: { contentType: [], selectedUser: [], publishTo: "national" },
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
form.setValue("id", String(data?.id));
|
||||
form.setValue("title", String(data?.name));
|
||||
form.setValue("description", String(data?.description));
|
||||
form.setValue("contentType", data?.mediaTypes.split(","));
|
||||
form.setValue("selectedUser", data.publishedFor.split(","));
|
||||
form.setValue("publishTo", data?.isInt ? "international" : "national");
|
||||
}, [data]);
|
||||
|
||||
const contentType = form.watch("contentType");
|
||||
|
||||
const isAllContentChecked = listContent.every((item) =>
|
||||
contentType?.includes(item.id)
|
||||
);
|
||||
|
||||
const users = form.watch("selectedUser");
|
||||
|
||||
const isAllUserChecked = userList.every((item) => users?.includes(item.id));
|
||||
|
||||
useEffect(() => {
|
||||
getRoles();
|
||||
}, []);
|
||||
|
||||
async function getRoles() {
|
||||
const response = await getUserRoles();
|
||||
let dataRoles = response?.data?.data;
|
||||
for (let i = 0; i < dataRoles.length; i++) {
|
||||
dataRoles[i].id = String(dataRoles[i].id);
|
||||
}
|
||||
setUserList(dataRoles);
|
||||
console.log("dasda", dataRoles);
|
||||
}
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
if (data.file instanceof Blob) {
|
||||
const formMedia = new FormData();
|
||||
|
||||
loading();
|
||||
formMedia.append("id", data.id);
|
||||
formMedia.append("name", data.title);
|
||||
formMedia.append("description", data.description);
|
||||
formMedia.append("mediaTypes", data.contentType.join(","));
|
||||
formMedia.append("file", data.file);
|
||||
formMedia.append("publishedFor", data.selectedUser.sort().join(","));
|
||||
formMedia.append(
|
||||
"isInt",
|
||||
data.publishTo === "national" ? "false" : "true"
|
||||
);
|
||||
console.log(formMedia);
|
||||
const response = await postCategory(formMedia);
|
||||
close();
|
||||
if (response?.error == true) {
|
||||
error(response.message);
|
||||
return false;
|
||||
}
|
||||
router.push("/admin/settings/category?dataChange=true");
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger>
|
||||
<a className="hover:underline">Edit Kategori</a>
|
||||
</DialogTrigger>
|
||||
<DialogContent size="md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Edit Kategori</DialogTitle>
|
||||
</DialogHeader>
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-3 bg-white rounded-sm"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="contentType"
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<FormLabel>Tipe Konten</FormLabel>
|
||||
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
id="all"
|
||||
checked={isAllContentChecked}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
form.setValue(
|
||||
"contentType",
|
||||
listContent.map((item) => item.id)
|
||||
);
|
||||
} else {
|
||||
form.setValue("contentType", []);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="all" className="text-sm">
|
||||
Semua
|
||||
</label>
|
||||
</div>
|
||||
{listContent.map((item) => (
|
||||
<FormField
|
||||
key={item.id}
|
||||
control={form.control}
|
||||
name="contentType"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key={item.id}
|
||||
className="flex flex-row items-start"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value?.includes(item.id)}
|
||||
onCheckedChange={(checked) => {
|
||||
return checked
|
||||
? field.onChange([
|
||||
...field.value,
|
||||
item.id,
|
||||
])
|
||||
: field.onChange(
|
||||
field.value?.filter(
|
||||
(value) => value !== item.id
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
{item.name}
|
||||
</FormLabel>
|
||||
</div>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="selectedUser"
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<FormLabel>Target Publish</FormLabel>
|
||||
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<div className="flex gap-3 items-center">
|
||||
<Checkbox
|
||||
id="all"
|
||||
checked={isAllUserChecked}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
form.setValue(
|
||||
"selectedUser",
|
||||
userList.map((item) => item.id)
|
||||
);
|
||||
} else {
|
||||
form.setValue("selectedUser", []);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="all" className="text-sm">
|
||||
Semua
|
||||
</label>
|
||||
</div>
|
||||
{userList.map((item) => (
|
||||
<FormField
|
||||
key={item.id}
|
||||
control={form.control}
|
||||
name="selectedUser"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key={item.id}
|
||||
className="flex flex-row items-start "
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value?.includes(item.id)}
|
||||
onCheckedChange={(checked) => {
|
||||
return checked
|
||||
? field.onChange([
|
||||
...field.value,
|
||||
item.id,
|
||||
])
|
||||
: field.onChange(
|
||||
field.value?.filter(
|
||||
(value) => value !== item.id
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
{item.name}
|
||||
</FormLabel>
|
||||
</div>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="publishTo"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-3">
|
||||
<FormLabel>Wilayah Publish</FormLabel>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
className="flex flex-row gap-2"
|
||||
>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="national" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">Nasional</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="international" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
Internasional
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama Kategori</FormLabel>
|
||||
<Input
|
||||
value={field.value}
|
||||
placeholder="Masukkan Nama Kategori"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="file"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama Kategori</FormLabel>
|
||||
{!field.value && (
|
||||
<div className="flex items-center justify-center w-full">
|
||||
<label
|
||||
htmlFor="dropzone-file"
|
||||
className="flex flex-col items-center justify-center w-full h-28 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-gray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
|
||||
>
|
||||
<div className="flex flex-col items-center justify-center pt-5 pb-6">
|
||||
<p className="mb-2 text-sm text-gray-500 dark:text-gray-400">
|
||||
<span className="font-semibold">Unggah Gambar</span>
|
||||
</p>
|
||||
</div>
|
||||
<input
|
||||
id="dropzone-file"
|
||||
type="file"
|
||||
className="hidden"
|
||||
onChange={(e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
field.onChange(file);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{field.value && (
|
||||
<div className="flex flex-row gap-2">
|
||||
<img
|
||||
src={URL.createObjectURL(field.value)}
|
||||
className="w-[30%]"
|
||||
alt="thumbnail"
|
||||
/>
|
||||
<a onClick={() => form.setValue("file", undefined)}>
|
||||
<Icon icon="fa-solid:times" color="red" />
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="description"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Deskripsi</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
placeholder="Deskripsi"
|
||||
// className="resize-none"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
size="md"
|
||||
className="text-xs"
|
||||
>
|
||||
Edit Kategori
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"use client";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { error } from "@/config/swal";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import { publishUnpublishCategory } from "@/service/settings/settings";
|
||||
|
||||
export default function StatusToogle(props: {
|
||||
id: number;
|
||||
initValue: boolean;
|
||||
}) {
|
||||
const { id, initValue } = props;
|
||||
const router = useRouter();
|
||||
const publishCategory = async (id: number, status: string) => {
|
||||
const response = await publishUnpublishCategory(id, status);
|
||||
console.log(response);
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return false;
|
||||
}
|
||||
router.push("/admin/settings/category?dataChange=true");
|
||||
};
|
||||
return (
|
||||
<Switch
|
||||
id={String(id)}
|
||||
checked={initValue}
|
||||
onCheckedChange={(e) => publishCategory(id, String(e))}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
PaginationState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import columns from "./column";
|
||||
|
||||
import { listEnableCategory } from "@/service/content/content";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { close, loading } from "@/config/swal";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import { NewCampaignIcon } from "@/components/icon";
|
||||
import { getCategories } from "@/service/settings/settings";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import CreateCategoryModal from "./create";
|
||||
|
||||
const AdminCategoryTable = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const dataChange = searchParams?.get("dataChange");
|
||||
const [openModal, setOpenModal] = React.useState(false);
|
||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[]
|
||||
);
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({});
|
||||
const [rowSelection, setRowSelection] = React.useState({});
|
||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 50,
|
||||
});
|
||||
|
||||
const [page, setPage] = React.useState(1);
|
||||
const [totalPage, setTotalPage] = React.useState(1);
|
||||
const table = useReactTable({
|
||||
data: dataTable,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
onPaginationChange: setPagination,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
pagination,
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
if (dataChange) {
|
||||
router.push("/admin/settings/category");
|
||||
}
|
||||
fetchData();
|
||||
}, [dataChange]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const pageFromUrl = searchParams?.get("page");
|
||||
if (pageFromUrl) {
|
||||
setPage(Number(pageFromUrl));
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchData();
|
||||
}, [page]);
|
||||
|
||||
async function fetchData() {
|
||||
try {
|
||||
loading();
|
||||
const response = await getCategories();
|
||||
const data = response?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * 50 + index + 1;
|
||||
});
|
||||
|
||||
setDataTable(contentData);
|
||||
setTotalData(data?.totalElements);
|
||||
setTotalPage(1);
|
||||
close();
|
||||
} catch (error) {
|
||||
console.error("Error fetching tasks:", error);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||
<div className="flex justify-between mb-10 items-center">
|
||||
<p className="text-xl font-medium text-default-900">Kategori</p>
|
||||
<CreateCategoryModal />
|
||||
</div>
|
||||
|
||||
<Table className="overflow-hidden">
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
className="h-[75px]"
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{/* <TablePagination
|
||||
table={table}
|
||||
totalData={totalData}
|
||||
totalPage={totalPage}
|
||||
/> */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdminCategoryTable;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
"use client";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import AdminCategoryTable from "./component/table";
|
||||
|
||||
export default function AdminCategory() {
|
||||
return (
|
||||
<>
|
||||
<SiteBreadcrumb />
|
||||
<AdminCategoryTable />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
"use client";
|
||||
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
|
||||
export default function TagCategory() {
|
||||
return (
|
||||
<>
|
||||
<SiteBreadcrumb />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -81,7 +81,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
const res = await getAgendaSettingsList(INITIAL_YEAR, INITIAL_MONTH, "");
|
||||
console.log("API Response:", res);
|
||||
|
||||
if (res.error) {
|
||||
if (res?.error) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ const BlogTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await paginationBlog(limit, page - 1, search);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ const TableAudio = () => {
|
|||
endDateString,
|
||||
search
|
||||
);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ const TableImage = () => {
|
|||
endDateString,
|
||||
search
|
||||
);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ const TableTeks = () => {
|
|||
try {
|
||||
const isForSelf = Number(roleId) == 4;
|
||||
const res = await listNulisAI(limit, page - 1, search);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ const TableSPIT = () => {
|
|||
|
||||
try {
|
||||
const res = await listSPIT(page - 1, limit, search, isPublish);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ const TableTeks = () => {
|
|||
endDateString,
|
||||
search
|
||||
);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ const TableImage = () => {
|
|||
endDateString,
|
||||
search
|
||||
);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ const MediahubTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await getPlanningSentPagination(limit, page - 1, 1, search);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ const MedsosTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await getPlanningSentPagination(limit, page - 1, 2, search);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ const EventTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await paginationSchedule(limit, page - 1, 2, search);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ const PressConferenceTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await paginationSchedule(limit, page - 1, 1, search);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ const PressReleaseTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await paginationSchedule(limit, page - 1, 3, search);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ const TaskTable = () => {
|
|||
limit,
|
||||
isSpecificAttention ? "atensi-khusus" : "tugas-harian"
|
||||
);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ const MediaTrackingTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const response = await getMediaTrackingMonitoring(page, 10);
|
||||
const data = response.data?.data;
|
||||
const data = response?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ const TaskPlanMediahubTable = (props: {
|
|||
async function fetchData() {
|
||||
// try {
|
||||
// const res = await ticketingPagination("", limit, page - 1);
|
||||
// const data = res.data?.data;
|
||||
// const data = res?.data?.data;
|
||||
console.log("datgaa", data);
|
||||
const contentData = data;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
|
|
|
|||
|
|
@ -170,8 +170,8 @@ export default function DetailDaily() {
|
|||
async function getWeeklyPlanning() {
|
||||
const res = await getWeeklyPlanList(new Date().getDate(), 1);
|
||||
|
||||
if (res.data !== null) {
|
||||
const rawUser = res.data?.data;
|
||||
if (res?.data !== null) {
|
||||
const rawUser = res?.data?.data;
|
||||
const optionArr = rawUser.map((option: any) => ({
|
||||
id: option.id,
|
||||
label: option.title,
|
||||
|
|
|
|||
|
|
@ -184,8 +184,8 @@ export default function EditDaily() {
|
|||
async function getWeeklyPlanning() {
|
||||
const res = await getWeeklyPlanList(new Date().getDate(), 1);
|
||||
|
||||
if (res.data !== null) {
|
||||
const rawUser = res.data?.data;
|
||||
if (res?.data !== null) {
|
||||
const rawUser = res?.data?.data;
|
||||
const optionArr = rawUser.map((option: any) => ({
|
||||
id: option.id,
|
||||
label: option.title,
|
||||
|
|
|
|||
|
|
@ -132,8 +132,8 @@ export default function CreateDaily() {
|
|||
async function getWeeklyPlanning() {
|
||||
const res = await getWeeklyPlanList(new Date().getDate(), 1);
|
||||
|
||||
if (res.data !== null) {
|
||||
const rawUser = res.data?.data;
|
||||
if (res?.data !== null) {
|
||||
const rawUser = res?.data?.data;
|
||||
const optionArr = rawUser.map((option: any) => ({
|
||||
id: option.id,
|
||||
label: option.title,
|
||||
|
|
|
|||
|
|
@ -136,8 +136,8 @@ export default function CreateMonthly() {
|
|||
async function getMonthlyPlanning() {
|
||||
const res = await getMonthlyPlanList(new Date().getDate(), 1);
|
||||
|
||||
if (res.data !== null) {
|
||||
const rawUser = res.data?.data;
|
||||
if (res?.data !== null) {
|
||||
const rawUser = res?.data?.data;
|
||||
const optionArr = rawUser.map((option: any) => ({
|
||||
id: option.id,
|
||||
label: option.title,
|
||||
|
|
|
|||
|
|
@ -106,8 +106,8 @@ export default function DetailTaskPlanMediahub() {
|
|||
const TODAY = dayjs().format("YYYY-MM-DD");
|
||||
const res = await getWeeklyPlanList(planningData?.date || TODAY, 1);
|
||||
|
||||
if (res.data !== null) {
|
||||
const rawUser = res.data?.data;
|
||||
if (res?.data !== null) {
|
||||
const rawUser = res?.data?.data;
|
||||
const optionArr = rawUser.map((option: any) => ({
|
||||
id: option.id,
|
||||
label: option.title,
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ const TaskPlanMediahubTable = (props: {
|
|||
async function fetchData() {
|
||||
// try {
|
||||
// const res = await ticketingPagination("", limit, page - 1);
|
||||
// const data = res.data?.data;
|
||||
// const data = res?.data?.data;
|
||||
console.log("datgaa", data);
|
||||
const contentData = data;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
|
|
|
|||
|
|
@ -168,8 +168,8 @@ export default function CreateMonthly() {
|
|||
async function getWeeklyPlanning() {
|
||||
const res = await getWeeklyPlanList(new Date().getDate(), 2);
|
||||
|
||||
if (res.data !== null) {
|
||||
const rawUser = res.data?.data;
|
||||
if (res?.data !== null) {
|
||||
const rawUser = res?.data?.data;
|
||||
const optionArr = rawUser.map((option: any) => ({
|
||||
id: option.id,
|
||||
label: option.title,
|
||||
|
|
|
|||
|
|
@ -135,8 +135,8 @@ export default function CreateMonthly() {
|
|||
async function getMonthlyPlanning() {
|
||||
const res = await getMonthlyPlanList(new Date().getDate(), 2);
|
||||
|
||||
if (res.data !== null) {
|
||||
const rawUser = res.data?.data;
|
||||
if (res?.data !== null) {
|
||||
const rawUser = res?.data?.data;
|
||||
const optionArr = rawUser.map((option: any) => ({
|
||||
id: option.id,
|
||||
label: option.title,
|
||||
|
|
|
|||
|
|
@ -106,8 +106,8 @@ export default function DetailTaskPlanMediahub() {
|
|||
const TODAY = dayjs().format("YYYY-MM-DD");
|
||||
const res = await getWeeklyPlanList(planningData?.date || TODAY, 1);
|
||||
|
||||
if (res.data !== null) {
|
||||
const rawUser = res.data?.data;
|
||||
if (res?.data !== null) {
|
||||
const rawUser = res?.data?.data;
|
||||
const optionArr = rawUser.map((option: any) => ({
|
||||
id: option.id,
|
||||
label: option.title,
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ const ContentTable = () => {
|
|||
endDateString,
|
||||
search
|
||||
);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ const RecentActivity: React.FC = () => {
|
|||
startDateString,
|
||||
endDateString
|
||||
);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const { content } = data || [];
|
||||
|
||||
// Calculate counts for each typeId
|
||||
|
|
|
|||
|
|
@ -7,11 +7,8 @@ import DashCodeHeader from "@/components/partials/header";
|
|||
import MountedProvider from "@/providers/mounted.provider";
|
||||
|
||||
const layout = async ({ children }: { children: React.ReactNode }) => {
|
||||
|
||||
return (
|
||||
<MountedProvider
|
||||
isProtected={true}
|
||||
>
|
||||
<MountedProvider isProtected={true}>
|
||||
<LayoutProvider>
|
||||
<ThemeCustomize />
|
||||
<DashCodeHeader />
|
||||
|
|
|
|||
|
|
@ -64,9 +64,9 @@ const NotificationsList: React.FC = () => {
|
|||
useEffect(() => {
|
||||
async function fetchNotifications() {
|
||||
const response = await getNotifications(page - 1, pageSize);
|
||||
setNotifications(response.data?.data?.content || []);
|
||||
setTotalData(response.data?.data?.totalElements || 0);
|
||||
setTotalPage(response.data?.data?.totalPages);
|
||||
setNotifications(response?.data?.data?.content || []);
|
||||
setTotalData(response?.data?.data?.totalElements || 0);
|
||||
setTotalPage(response?.data?.data?.totalPages);
|
||||
}
|
||||
fetchNotifications();
|
||||
}, [page, pageSize]);
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ const EscalationTable = () => {
|
|||
limit,
|
||||
search
|
||||
);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ const EscalationTable = () => {
|
|||
limit,
|
||||
search
|
||||
);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ const TableAudio = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await listTicketingInternal(page - 1, limit, search);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ const TaskTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await listContest(search, limit, page - 1);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ export default function DetailAudio() {
|
|||
async function initState() {
|
||||
if (id) {
|
||||
const response = await detailMedia(id);
|
||||
const details = response.data?.data;
|
||||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
const filesData = details.files || [];
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ export default function DetailDocument() {
|
|||
async function initState() {
|
||||
if (id) {
|
||||
const response = await detailMedia(id);
|
||||
const details = response.data?.data;
|
||||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
const filesData = details.files || [];
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ export default function DetailImage() {
|
|||
async function initState() {
|
||||
if (id) {
|
||||
const response = await detailMedia(id);
|
||||
const details = response.data?.data;
|
||||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
const filesData = details.files || [];
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ const ImageSliderPage = () => {
|
|||
});
|
||||
console.log(response);
|
||||
|
||||
const data = response.data?.data;
|
||||
const data = response?.data?.data;
|
||||
const contentData = data?.content;
|
||||
setImageData(contentData);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ export default function DetailImage() {
|
|||
async function initState() {
|
||||
if (id) {
|
||||
const response = await detailMedia(id);
|
||||
const details = response.data?.data;
|
||||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
const filesData = details.files || [];
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ const FaqTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await getFaqList();
|
||||
const contentData = res.data?.data;
|
||||
const contentData = res?.data?.data;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ const FaqTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await getFaqList();
|
||||
const contentData = res.data?.data;
|
||||
const contentData = res?.data?.data;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ const FaqTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await getFaqList();
|
||||
const contentData = res.data?.data;
|
||||
const contentData = res?.data?.data;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ const FaqTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await getFaqList();
|
||||
const contentData = res.data?.data;
|
||||
const contentData = res?.data?.data;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ const FaqTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await getFaqList();
|
||||
const contentData = res.data?.data;
|
||||
const contentData = res?.data?.data;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ const FaqTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await getFaqList();
|
||||
const contentData = res.data?.data;
|
||||
const contentData = res?.data?.data;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const KnowledgeBase = () => {
|
|||
|
||||
async function fetchCategoryList() {
|
||||
const response = await getKnowledgeBaseCategoryList();
|
||||
const data = response.data?.data;
|
||||
const data = response?.data?.data;
|
||||
if (data) {
|
||||
setCategories(data);
|
||||
fetchQuestions(data[0]?.id)
|
||||
|
|
@ -34,7 +34,7 @@ const KnowledgeBase = () => {
|
|||
|
||||
const fetchQuestions = async (id: number) => {
|
||||
const response = await getKnowledgeBaseList(id);
|
||||
const data = response.data?.data;
|
||||
const data = response?.data?.data;
|
||||
if (data) {
|
||||
setQuestions(data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ const TicketingTable = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
const res = await ticketingPagination('', limit, page-1);
|
||||
const data = res.data?.data;
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,635 @@
|
|||
"use client";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import { getOnlyDate, getOnlyMonthAndYear } from "@/utils/globals";
|
||||
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||
import {
|
||||
getUserLevelListByParent,
|
||||
listCategory,
|
||||
listData,
|
||||
listDataAll,
|
||||
listDataRegional,
|
||||
} from "@/service/landing/landing";
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
PaginationState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import { Reveal } from "@/components/landing-page/Reveal";
|
||||
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";
|
||||
import FilterImageComponent from "@/components/landing-page/filter-all/image-filter-card";
|
||||
import FilterVideoComponent from "@/components/landing-page/filter-all/video-filter-card";
|
||||
import FilterDocumentComponent from "@/components/landing-page/filter-all/document-filter-card";
|
||||
import FilterAudioComponent from "@/components/landing-page/filter-all/audio-filter-card";
|
||||
|
||||
export default function FilterPage() {
|
||||
const router = useRouter();
|
||||
const asPath = usePathname();
|
||||
const params = useParams();
|
||||
const searchParams = useSearchParams();
|
||||
const locale = params?.locale;
|
||||
const [imageData, setImageData] = useState<any>();
|
||||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
const [totalPage, setTotalPage] = 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] = 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 tag: any = searchParams?.get("tag");
|
||||
const [contentImage, setContentImage] = useState([]);
|
||||
const [, setGetTotalPage] = useState();
|
||||
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 [contentAll, setContentAll] = useState([]);
|
||||
|
||||
// const [startDate, endDate] = dateRange;
|
||||
|
||||
React.useEffect(() => {
|
||||
const pageFromUrl = searchParams?.get("page");
|
||||
if (pageFromUrl) {
|
||||
setPage(Number(pageFromUrl));
|
||||
}
|
||||
}, [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 {
|
||||
getData();
|
||||
}
|
||||
}
|
||||
console.log(monthYearFilter, "monthFilter");
|
||||
initState();
|
||||
}, [
|
||||
change,
|
||||
monthYearFilter,
|
||||
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 getData() {
|
||||
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 listDataAll(
|
||||
"",
|
||||
name,
|
||||
filter,
|
||||
"",
|
||||
tag,
|
||||
filterGroup,
|
||||
startDateString,
|
||||
endDateString,
|
||||
monthYearFilter
|
||||
? getOnlyMonthAndYear(monthYearFilter)
|
||||
?.split("/")[0]
|
||||
?.replace("0", "")
|
||||
: "",
|
||||
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);
|
||||
setContentAll(response?.data?.data?.content);
|
||||
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 listDataAll(
|
||||
"",
|
||||
name,
|
||||
filter,
|
||||
"",
|
||||
"",
|
||||
tag,
|
||||
startDateString,
|
||||
endDateString,
|
||||
monthYearFilter
|
||||
? getOnlyMonthAndYear(monthYearFilter)
|
||||
?.split("/")[0]
|
||||
?.replace("0", "")
|
||||
: "",
|
||||
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);
|
||||
setContentAll(response?.data?.data?.content);
|
||||
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(
|
||||
"",
|
||||
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);
|
||||
}
|
||||
|
||||
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>
|
||||
{" "}
|
||||
Konten {">"} <span className="font-bold">Semua Konten</span>
|
||||
</p>
|
||||
<p className="font-bold">|</p>
|
||||
<p>{`Hasil Pencarian ${title} `}</p>
|
||||
{`Terdapat ${contentAll?.length} konten yang dapat diunduh`}
|
||||
</div>
|
||||
|
||||
{/* Left */}
|
||||
<div className="flex flex-col lg:flex-row gap-6 p-4">
|
||||
<div className="lg:w-[25%] 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 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
|
||||
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 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 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: any) => (
|
||||
<li key={category?.id}>
|
||||
<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 my-4 dark:border-white"></div>
|
||||
{/* Garis */}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-gray-700 dark:text-white">
|
||||
Format Konten
|
||||
</h3>
|
||||
<ul className="mt-2 space-y-2">
|
||||
<li>
|
||||
<label className="inline-flex items-center">
|
||||
<Checkbox
|
||||
id="png"
|
||||
value="png"
|
||||
checked={formatFilter.includes("image")}
|
||||
onCheckedChange={(e) =>
|
||||
handleFormatFilter(Boolean(e), "image")
|
||||
}
|
||||
/>
|
||||
<span className="ml-2 text-gray-700 dark:text-white">
|
||||
Foto
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label className="inline-flex items-center">
|
||||
<Checkbox
|
||||
id="jpeg"
|
||||
value="jpeg"
|
||||
checked={formatFilter.includes("video")}
|
||||
onCheckedChange={(e) =>
|
||||
handleFormatFilter(Boolean(e), "video")
|
||||
}
|
||||
/>
|
||||
<span className="ml-2 text-gray-700 dark:text-white">
|
||||
Audio Visual
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label className="inline-flex items-center">
|
||||
<Checkbox
|
||||
id="jpg"
|
||||
value="jpg"
|
||||
checked={formatFilter.includes("document")}
|
||||
onCheckedChange={(e) =>
|
||||
handleFormatFilter(Boolean(e), "document")
|
||||
}
|
||||
/>
|
||||
<span className="ml-2 text-gray-700 dark:text-white">
|
||||
Teks
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label className="inline-flex items-center">
|
||||
<Checkbox
|
||||
id="jpg"
|
||||
value="jpg"
|
||||
checked={formatFilter.includes("audio")}
|
||||
onCheckedChange={(e) =>
|
||||
handleFormatFilter(Boolean(e), "audio")
|
||||
}
|
||||
/>
|
||||
<span className="ml-2 text-gray-700 dark:text-white">
|
||||
Audio
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="border-t border-black dark:border-white my-4"></div>
|
||||
<div className="text-center">
|
||||
<a
|
||||
onClick={cleanCheckbox}
|
||||
className="text-[#bb3523] cursor-pointer"
|
||||
>
|
||||
<b>Reset Filter</b>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Konten Kanan */}
|
||||
<Reveal>
|
||||
<div className="flex-1 w-full">
|
||||
<div className="flex flex-col items-end mb-4">
|
||||
<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="terpopuler">Terpopuler</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<FilterImageComponent
|
||||
categoryFilter={categoryFilter}
|
||||
sortByOpt={sortByOpt}
|
||||
startDateString={startDateString}
|
||||
endDateString={endDateString}
|
||||
monthYearFilter={monthYearFilter}
|
||||
/>
|
||||
<FilterVideoComponent
|
||||
categoryFilter={categoryFilter}
|
||||
sortByOpt={sortByOpt}
|
||||
startDateString={startDateString}
|
||||
endDateString={endDateString}
|
||||
monthYearFilter={monthYearFilter}
|
||||
/>
|
||||
<FilterDocumentComponent
|
||||
categoryFilter={categoryFilter}
|
||||
sortByOpt={sortByOpt}
|
||||
startDateString={startDateString}
|
||||
endDateString={endDateString}
|
||||
monthYearFilter={monthYearFilter}
|
||||
/>
|
||||
<FilterAudioComponent
|
||||
categoryFilter={categoryFilter}
|
||||
sortByOpt={sortByOpt}
|
||||
startDateString={startDateString}
|
||||
endDateString={endDateString}
|
||||
monthYearFilter={monthYearFilter}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import Footer from "@/components/landing-page/footer";
|
||||
import Navbar from "@/components/landing-page/navbar";
|
||||
|
||||
const layout = async ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
{children}
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default layout;
|
||||
|
|
@ -1,14 +1,46 @@
|
|||
"use client";
|
||||
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 {
|
||||
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";
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
CarouselNext,
|
||||
CarouselPrevious,
|
||||
} from "@/components/ui/carousel";
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
|
|
@ -18,43 +50,57 @@ 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: "WAV" },
|
||||
{ id: 2, title: "MP3" },
|
||||
];
|
||||
|
||||
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 [audioData, setAudioData] = useState<any>();
|
||||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
const [totalPage, setTotalPage] = React.useState<number>(1);
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
|
||||
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
|
||||
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: 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 [contentImage, setContentImage] = useState([]);
|
||||
const [, setGetTotalPage] = useState();
|
||||
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");
|
||||
|
|
@ -63,8 +109,232 @@ 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("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;
|
||||
setAudioData(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;
|
||||
setAudioData(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,
|
||||
data: audioData,
|
||||
columns: columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
|
|
@ -84,165 +354,329 @@ const FilterPage = () => {
|
|||
},
|
||||
});
|
||||
|
||||
const [audioData, setAudioData] = useState<any>();
|
||||
useEffect(() => {
|
||||
initFetch();
|
||||
}, []);
|
||||
const initFetch = async () => {
|
||||
const response = await getListContent({ page: page - 1, size: 6, sortBy: "createdAt", contentTypeId: "4" });
|
||||
console.log(response);
|
||||
setAudioData(response?.data?.data?.content);
|
||||
const data = response.data?.data;
|
||||
const contentData = data?.content;
|
||||
setAudioData(contentData);
|
||||
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>
|
||||
{" "}
|
||||
Audio {">"} <span className="font-bold">Semua Audio</span>
|
||||
</p>
|
||||
<p className="font-bold">|</p>
|
||||
<p>Terdapat 32499 artikel berisi Audio yang dapat diunduh </p>
|
||||
<p>{`Terdapat ${totalContent} artikel berisi Audio yang dapat diunduh`}</p>
|
||||
</div>
|
||||
|
||||
{/* Left */}
|
||||
<div className="flex flex-col lg:flex-row gap-6 p-4">
|
||||
<Reveal>
|
||||
{/* Sidebar Kiri */}
|
||||
<div className="lg:w-1/4 bg-white p-4 rounded-lg shadow-md">
|
||||
<h2 className="text-lg font-semibold mb-4">
|
||||
<Icon icon="stash:filter-light" fontSize={30} />
|
||||
Filter
|
||||
</h2>
|
||||
<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" />
|
||||
</div>
|
||||
<div className="lg:w-[25%] 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 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
|
||||
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" />
|
||||
</div>
|
||||
<div>
|
||||
<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" />
|
||||
</div>
|
||||
|
||||
{/* Kategori */}
|
||||
<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) => (
|
||||
<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>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
{/* Garis */}
|
||||
<div className="border-t border-black my-4"></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">
|
||||
{formatAudio.map((format) => (
|
||||
<li key={format?.id}>
|
||||
<label className="inline-flex items-center">
|
||||
<Checkbox id="terms" />
|
||||
<span className="ml-2 text-gray-700 dark:text-white">{format.title}</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]">
|
||||
<b>Reset Filter</b>
|
||||
</a>
|
||||
<div>
|
||||
<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: any) => (
|
||||
<li key={category?.id}>
|
||||
<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 my-4 dark:border-white"></div>
|
||||
{/* Garis */}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-gray-700 dark:text-white">
|
||||
Format Audio
|
||||
</h3>
|
||||
<ul className="mt-2 space-y-2">
|
||||
<li>
|
||||
<label className="inline-flex items-center">
|
||||
<Checkbox
|
||||
id="png"
|
||||
value="png"
|
||||
checked={formatFilter.includes("wav")}
|
||||
onCheckedChange={(e) =>
|
||||
handleFormatFilter(Boolean(e), "wav")
|
||||
}
|
||||
/>
|
||||
<span className="ml-2 text-gray-700 dark:text-white">
|
||||
WAV
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label className="inline-flex items-center">
|
||||
<Checkbox
|
||||
id="jpeg"
|
||||
value="jpeg"
|
||||
checked={formatFilter.includes("mp3")}
|
||||
onCheckedChange={(e) =>
|
||||
handleFormatFilter(Boolean(e), "mp3")
|
||||
}
|
||||
/>
|
||||
<span className="ml-2 text-gray-700 dark:text-white">
|
||||
MP3
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="border-t border-black dark:border-white my-4"></div>
|
||||
<div className="text-center">
|
||||
<a
|
||||
onClick={cleanCheckbox}
|
||||
className="text-[#bb3523] cursor-pointer"
|
||||
>
|
||||
<b>Reset Filter</b>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
</div>
|
||||
|
||||
{/* Konten Kanan */}
|
||||
<Reveal>
|
||||
<div className="flex-1 w-auto">
|
||||
<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>
|
||||
|
||||
{/* Card */}
|
||||
{audioData?.length > 0 ? (
|
||||
<div className=" grid grid-cols-1 gap-6 lg:w-3/4">
|
||||
{audioData?.map((audio: any) => (
|
||||
<Link
|
||||
href={`/audio/detail/${audio?.slug}`}
|
||||
key={audio?.id}
|
||||
className="flex flex-col sm:flex-row items-center hover:scale-110 transition-transform duration-300 bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full"
|
||||
>
|
||||
<div className="flex items-center justify-center bg-[#bb3523] text-white rounded-lg w-16 h-16">
|
||||
<svg width="32" height="34" viewBox="0 0 32 34" fill="null" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M23.404 0.452014C23.7033 0.35857 24.0204 0.336816 24.3297 0.388509C24.639 0.440203 24.9318 0.563895 25.1845 0.749599C25.4371 0.935304 25.6426 1.17782 25.7843 1.45756C25.9259 1.73731 25.9998 2.04644 26 2.36001V14.414C25.3462 14.2296 24.6766 14.1064 24 14.046V8.36001L10 12.736V27C10 28.1264 9.6197 29.2197 8.92071 30.1029C8.22172 30.9861 7.24499 31.6075 6.14877 31.8663C5.05255 32.125 3.90107 32.0061 2.88089 31.5287C1.86071 31.0514 1.03159 30.2435 0.52787 29.2361C0.024152 28.2286 -0.124656 27.0806 0.105556 25.9781C0.335768 24.8755 0.931513 23.883 1.79627 23.1613C2.66102 22.4396 3.74413 22.031 4.87009 22.0017C5.99606 21.9724 7.09893 22.3242 8.00001 23V6.73601C7.99982 6.30956 8.13596 5.8942 8.38854 5.55059C8.64112 5.20698 8.99692 4.9531 9.40401 4.82601L23.404 0.452014ZM10 10.64L24 6.26601V2.36001L10 6.73601V10.64ZM5.00001 24C4.20436 24 3.44129 24.3161 2.87869 24.8787C2.31608 25.4413 2.00001 26.2044 2.00001 27C2.00001 27.7957 2.31608 28.5587 2.87869 29.1213C3.44129 29.6839 4.20436 30 5.00001 30C5.79566 30 6.55872 29.6839 7.12133 29.1213C7.68394 28.5587 8.00001 27.7957 8.00001 27C8.00001 26.2044 7.68394 25.4413 7.12133 24.8787C6.55872 24.3161 5.79566 24 5.00001 24ZM32 25C32 27.387 31.0518 29.6761 29.364 31.364C27.6761 33.0518 25.387 34 23 34C20.6131 34 18.3239 33.0518 16.636 31.364C14.9482 29.6761 14 27.387 14 25C14 22.6131 14.9482 20.3239 16.636 18.6361C18.3239 16.9482 20.6131 16 23 16C25.387 16 27.6761 16.9482 29.364 18.6361C31.0518 20.3239 32 22.6131 32 25ZM27.47 24.128L21.482 20.828C21.3298 20.7443 21.1583 20.7016 20.9846 20.7043C20.8108 20.707 20.6408 20.7549 20.4912 20.8433C20.3416 20.9317 20.2176 21.0576 20.1315 21.2086C20.0453 21.3595 20 21.5302 20 21.704V28.304C20 28.4778 20.0453 28.6486 20.1315 28.7995C20.2176 28.9504 20.3416 29.0763 20.4912 29.1647C20.6408 29.2531 20.8108 29.301 20.9846 29.3037C21.1583 29.3064 21.3298 29.2638 21.482 29.18L27.47 25.88C27.6268 25.7937 27.7575 25.6669 27.8486 25.5128C27.9397 25.3587 27.9877 25.183 27.9877 25.004C27.9877 24.825 27.9397 24.6493 27.8486 24.4952C27.7575 24.3412 27.6268 24.2143 27.47 24.128Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{audioData?.map((image: any) => (
|
||||
<Carousel className="w-full max-w-7xl mx-auto">
|
||||
<CarouselContent>
|
||||
{audioData?.map((audio: any) => (
|
||||
<CarouselItem
|
||||
key={audio?.id}
|
||||
className="md:basis-1/2 lg:basis-1/3"
|
||||
>
|
||||
<div className="flex flex-row gap-6">
|
||||
<Link
|
||||
href={`/audio/detail/${audio?.slug}`}
|
||||
className="flex flex-col sm:flex-row items-center bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full"
|
||||
>
|
||||
<div className="flex items-center justify-center bg-red-500 text-white rounded-lg w-16 h-8 lg:h-16">
|
||||
<svg
|
||||
width="32"
|
||||
height="34"
|
||||
viewBox="0 0 32 34"
|
||||
fill="null"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M23.404 0.452014C23.7033 0.35857 24.0204 0.336816 24.3297 0.388509C24.639 0.440203 24.9318 0.563895 25.1845 0.749599C25.4371 0.935304 25.6426 1.17782 25.7843 1.45756C25.9259 1.73731 25.9998 2.04644 26 2.36001V14.414C25.3462 14.2296 24.6766 14.1064 24 14.046V8.36001L10 12.736V27C10 28.1264 9.6197 29.2197 8.92071 30.1029C8.22172 30.9861 7.24499 31.6075 6.14877 31.8663C5.05255 32.125 3.90107 32.0061 2.88089 31.5287C1.86071 31.0514 1.03159 30.2435 0.52787 29.2361C0.024152 28.2286 -0.124656 27.0806 0.105556 25.9781C0.335768 24.8755 0.931513 23.883 1.79627 23.1613C2.66102 22.4396 3.74413 22.031 4.87009 22.0017C5.99606 21.9724 7.09893 22.3242 8.00001 23V6.73601C7.99982 6.30956 8.13596 5.8942 8.38854 5.55059C8.64112 5.20698 8.99692 4.9531 9.40401 4.82601L23.404 0.452014ZM10 10.64L24 6.26601V2.36001L10 6.73601V10.64ZM5.00001 24C4.20436 24 3.44129 24.3161 2.87869 24.8787C2.31608 25.4413 2.00001 26.2044 2.00001 27C2.00001 27.7957 2.31608 28.5587 2.87869 29.1213C3.44129 29.6839 4.20436 30 5.00001 30C5.79566 30 6.55872 29.6839 7.12133 29.1213C7.68394 28.5587 8.00001 27.7957 8.00001 27C8.00001 26.2044 7.68394 25.4413 7.12133 24.8787C6.55872 24.3161 5.79566 24 5.00001 24ZM32 25C32 27.387 31.0518 29.6761 29.364 31.364C27.6761 33.0518 25.387 34 23 34C20.6131 34 18.3239 33.0518 16.636 31.364C14.9482 29.6761 14 27.387 14 25C14 22.6131 14.9482 20.3239 16.636 18.6361C18.3239 16.9482 20.6131 16 23 16C25.387 16 27.6761 16.9482 29.364 18.6361C31.0518 20.3239 32 22.6131 32 25ZM27.47 24.128L21.482 20.828C21.3298 20.7443 21.1583 20.7016 20.9846 20.7043C20.8108 20.707 20.6408 20.7549 20.4912 20.8433C20.3416 20.9317 20.2176 21.0576 20.1315 21.2086C20.0453 21.3595 20 21.5302 20 21.704V28.304C20 28.4778 20.0453 28.6486 20.1315 28.7995C20.2176 28.9504 20.3416 29.0763 20.4912 29.1647C20.6408 29.2531 20.8108 29.301 20.9846 29.3037C21.1583 29.3064 21.3298 29.2638 21.482 29.18L27.47 25.88C27.6268 25.7937 27.7575 25.6669 27.8486 25.5128C27.9397 25.3587 27.9877 25.183 27.9877 25.004C27.9877 24.825 27.9397 24.6493 27.8486 24.4952C27.7575 24.3412 27.6268 24.2143 27.47 24.128Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col flex-1">
|
||||
<div className="text-gray-500 dark:text-gray-400 flex flex-row text-sm">
|
||||
{formatDateToIndonesian(new Date(audio?.createdAt))} {audio?.timezone ? audio?.timezone : "WIB"} | <Icon icon="formkit:eye" width="15" height="15" /> 518
|
||||
</div>
|
||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{audio?.title}</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<div className="mt-2">
|
||||
<img src="/assets/wave.svg" className="w-80" />
|
||||
</div>
|
||||
<div className="flex flex-row items-center justify-center text-gray-500 dark:text-gray-400">
|
||||
<img src="/assets/audio-icon.png" alt="#" className="flex items-center justify-center" />
|
||||
<div className="flex mx-2 items-center justify-center">{audio?.duration}</div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<path
|
||||
fill="#f00"
|
||||
d="M7.707 10.293a1 1 0 1 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414L11 11.586V6h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5v5.586zM9 4a1 1 0 0 1 2 0v2H9z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<div className="flex flex-col flex-1">
|
||||
<div className="text-gray-500 dark:text-gray-400 flex flex-row text-sm">
|
||||
{formatDateToIndonesian(
|
||||
new Date(audio?.createdAt)
|
||||
)}{" "}
|
||||
{audio?.timezone ? audio?.timezone : "WIB"} |{" "}
|
||||
<Icon
|
||||
icon="formkit:eye"
|
||||
width="15"
|
||||
height="15"
|
||||
/>{" "}
|
||||
{audio?.clickCount}{" "}
|
||||
</div>
|
||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm h-5 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">
|
||||
{audio?.title}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</CarouselItem>
|
||||
))}
|
||||
</CarouselContent>
|
||||
<CarouselPrevious />
|
||||
<CarouselNext />
|
||||
</Carousel>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-60 w-60 my-4" />
|
||||
<p className="flex items-center justify-center text-black">
|
||||
<img
|
||||
src="/assets/empty-data.png"
|
||||
alt="empty"
|
||||
className="h-60 w-60 my-4"
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<LandingPagination table={table} totalData={totalData} totalPage={totalPage} />
|
||||
<LandingPagination
|
||||
table={table}
|
||||
totalData={totalData}
|
||||
totalPage={totalPage}
|
||||
/>
|
||||
</div>
|
||||
</Reveal>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,395 @@
|
|||
"use client";
|
||||
|
||||
import { close, error, loading, successCallback } from "@/config/swal";
|
||||
import { checkWishlistStatus, deleteWishlist, getInfoProfile, mediaWishlist, saveWishlist } from "@/service/landing/landing";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import HeaderManagement from "@/components/landing-page/header-management";
|
||||
import SidebarManagement from "@/components/landing-page/sidebar-management";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import Swal from "sweetalert2";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
|
||||
const Galery = (props: any) => {
|
||||
const [profile, setProfile] = useState<any>();
|
||||
const [selectedTab, setSelectedTab] = useState("video");
|
||||
const router = useRouter();
|
||||
const MySwal = withReactContent(Swal);
|
||||
const searchParams = useSearchParams();
|
||||
// const { id } = router.query;
|
||||
const page: any = searchParams?.get("page");
|
||||
const title = searchParams?.get("title");
|
||||
const category = searchParams?.get("category");
|
||||
const pages = page ? page - 1 : 0;
|
||||
const { isInstitute, instituteId } = props;
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
const userRoleId = getCookiesDecrypt("urie");
|
||||
|
||||
const [totalContent, setTotalContent] = useState();
|
||||
const [categoryFilter] = useState([]);
|
||||
const [formatFilter] = useState([]);
|
||||
const [sortBy] = useState();
|
||||
const [change] = useState(false);
|
||||
const [contentVideo, setContentVideo] = useState([]);
|
||||
const [contentImage, setContentImage] = useState([]);
|
||||
const [contentDocument, setContentDocument] = useState([]);
|
||||
const [contentAudio, setContentAudio] = useState([]);
|
||||
const [, setGetTotalPage] = useState([]);
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [, setCopySuccess] = useState("");
|
||||
const [, setWishlistId] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
getDataVideo();
|
||||
}, [page, category, title]);
|
||||
|
||||
async function getDataVideo() {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : category || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
const response = await mediaWishlist("2", isInstitute ? instituteId : "", name, filter, "9", pages, sortBy, format);
|
||||
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentVideo(response?.data?.data?.content);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
loading();
|
||||
const response = await getInfoProfile();
|
||||
|
||||
// console.log(response?.data.data);
|
||||
setProfile(response?.data?.data);
|
||||
close();
|
||||
}
|
||||
|
||||
initState();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getDataVideo();
|
||||
}, [change, refresh]);
|
||||
|
||||
useEffect(() => {
|
||||
getDataDocument();
|
||||
}, [page, category, title]);
|
||||
|
||||
async function getDataDocument() {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : category || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
|
||||
const response = await mediaWishlist("3", isInstitute ? instituteId : "", name, filter, "12", pages, sortBy, format);
|
||||
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentDocument(response?.data?.data?.content);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getDataDocument();
|
||||
}, [change, refresh]);
|
||||
|
||||
useEffect(() => {
|
||||
getDataAudio();
|
||||
}, [page, category, title]);
|
||||
|
||||
useEffect(() => {
|
||||
getDataAudio();
|
||||
}, [change, refresh]);
|
||||
|
||||
async function getDataAudio() {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : category || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
|
||||
const response = await mediaWishlist("4", isInstitute ? instituteId : "", name, filter, "6", pages, sortBy, format);
|
||||
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentAudio(response?.data?.data?.content);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getDataAudio();
|
||||
}, [change]);
|
||||
|
||||
useEffect(() => {
|
||||
getDataImage();
|
||||
}, [page, category, title, refresh]);
|
||||
|
||||
async function getDataImage() {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : category || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
|
||||
const response = await mediaWishlist("1", isInstitute ? instituteId : "", name, filter, "12", pages, sortBy, format);
|
||||
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentImage(response?.data?.data?.content);
|
||||
// console.log("response", response);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
getDataImage();
|
||||
}
|
||||
|
||||
initState();
|
||||
}, [page, refresh]);
|
||||
|
||||
async function checkWishlist(uploadId: any) {
|
||||
if (userId) {
|
||||
const res = await checkWishlistStatus(uploadId);
|
||||
console.log(res?.data?.data);
|
||||
// const isAlreadyOnWishlist = res?.data?.data == "-1" ? false : true;
|
||||
// if (isAlreadyOnWishlist == true) {
|
||||
// warning("Konten sudah Ada", `#`);
|
||||
// }
|
||||
setWishlistId(res?.data?.data); // setIsSaved(isAlreadyOnWishlist);
|
||||
// console.log("isSave", isAlreadyOnWishlist);
|
||||
}
|
||||
}
|
||||
|
||||
const handleSaveWishlist = async (uploadId: any) => {
|
||||
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||
router.push("/auth/login");
|
||||
} else {
|
||||
console.log("data", uploadId);
|
||||
const data = {
|
||||
mediaUploadId: uploadId,
|
||||
};
|
||||
|
||||
loading();
|
||||
checkWishlist(uploadId);
|
||||
|
||||
const res = await saveWishlist(data);
|
||||
if (res?.error) {
|
||||
error(res?.message);
|
||||
console.log("simpan data", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
successCallback("Konten Berhasil Disimpan");
|
||||
}
|
||||
};
|
||||
|
||||
async function deleteProcess(id: any) {
|
||||
loading();
|
||||
const resDelete = await deleteWishlist(id);
|
||||
|
||||
if (resDelete?.error) {
|
||||
error(resDelete.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
setRefresh((prevRefresh) => !prevRefresh);
|
||||
close();
|
||||
}
|
||||
|
||||
const handleDelete = (id: any) => {
|
||||
MySwal.fire({
|
||||
title: "Hapus Data",
|
||||
text: "",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#3085d6",
|
||||
confirmButtonColor: "#d33",
|
||||
confirmButtonText: "Hapus",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
deleteProcess(id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const copyToClip = async (url: any) => {
|
||||
await navigator.clipboard.writeText(`https://mediahub.polri.go.id/video/detail/${url}`);
|
||||
setCopySuccess("Copied");
|
||||
// toast.success("Link Berhasil Di Copy");
|
||||
};
|
||||
|
||||
const [hasMounted, setHasMounted] = useState(false);
|
||||
|
||||
// Hooks
|
||||
useEffect(() => {
|
||||
setHasMounted(true);
|
||||
}, []);
|
||||
|
||||
// Render
|
||||
if (!hasMounted) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderManagement />
|
||||
<div className="flex flex-row">
|
||||
<SidebarManagement />
|
||||
<div className="w-2/3 p-12">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold">Galeri Saya</h1>
|
||||
</div>
|
||||
<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">
|
||||
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
|
||||
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
||||
<TabsTrigger
|
||||
value="video"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
>
|
||||
Audio Visual
|
||||
</TabsTrigger>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">|</div>
|
||||
<TabsTrigger
|
||||
value="audio"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
>
|
||||
Audio
|
||||
</TabsTrigger>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">|</div>
|
||||
<TabsTrigger
|
||||
value="image"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
>
|
||||
Foto
|
||||
</TabsTrigger>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">|</div>
|
||||
<TabsTrigger
|
||||
value="text"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
>
|
||||
Teks
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className="px-0 lg:px-10">
|
||||
{selectedTab == "video" ? (
|
||||
contentVideo?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{contentVideo?.map((video: any) => (
|
||||
<Card key={video?.id} className="hover:scale-110 transition-transform duration-300">
|
||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||
<Link href={`/video/detail/${video?.mediaUpload?.slug}`}>
|
||||
<img src={video?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{video?.mediaUpload?.title}</div>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
</p>
|
||||
)
|
||||
) : selectedTab == "audio" ? (
|
||||
contentAudio?.length > 0 ? (
|
||||
<div className=" grid grid-cols-1 gap-6 ">
|
||||
{contentAudio?.map((audio: any) => (
|
||||
<Link
|
||||
href={`/audio/detail/${audio?.mediaUpload?.slug}`}
|
||||
key={audio?.id}
|
||||
className="flex flex-col sm:flex-row items-center hover:scale-110 transition-transform duration-300 bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full"
|
||||
>
|
||||
<div className="flex items-center justify-center bg-red-500 text-white rounded-lg w-16 h-8 lg:h-16">
|
||||
<svg width="32" height="34" viewBox="0 0 32 34" fill="null" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M23.404 0.452014C23.7033 0.35857 24.0204 0.336816 24.3297 0.388509C24.639 0.440203 24.9318 0.563895 25.1845 0.749599C25.4371 0.935304 25.6426 1.17782 25.7843 1.45756C25.9259 1.73731 25.9998 2.04644 26 2.36001V14.414C25.3462 14.2296 24.6766 14.1064 24 14.046V8.36001L10 12.736V27C10 28.1264 9.6197 29.2197 8.92071 30.1029C8.22172 30.9861 7.24499 31.6075 6.14877 31.8663C5.05255 32.125 3.90107 32.0061 2.88089 31.5287C1.86071 31.0514 1.03159 30.2435 0.52787 29.2361C0.024152 28.2286 -0.124656 27.0806 0.105556 25.9781C0.335768 24.8755 0.931513 23.883 1.79627 23.1613C2.66102 22.4396 3.74413 22.031 4.87009 22.0017C5.99606 21.9724 7.09893 22.3242 8.00001 23V6.73601C7.99982 6.30956 8.13596 5.8942 8.38854 5.55059C8.64112 5.20698 8.99692 4.9531 9.40401 4.82601L23.404 0.452014ZM10 10.64L24 6.26601V2.36001L10 6.73601V10.64ZM5.00001 24C4.20436 24 3.44129 24.3161 2.87869 24.8787C2.31608 25.4413 2.00001 26.2044 2.00001 27C2.00001 27.7957 2.31608 28.5587 2.87869 29.1213C3.44129 29.6839 4.20436 30 5.00001 30C5.79566 30 6.55872 29.6839 7.12133 29.1213C7.68394 28.5587 8.00001 27.7957 8.00001 27C8.00001 26.2044 7.68394 25.4413 7.12133 24.8787C6.55872 24.3161 5.79566 24 5.00001 24ZM32 25C32 27.387 31.0518 29.6761 29.364 31.364C27.6761 33.0518 25.387 34 23 34C20.6131 34 18.3239 33.0518 16.636 31.364C14.9482 29.6761 14 27.387 14 25C14 22.6131 14.9482 20.3239 16.636 18.6361C18.3239 16.9482 20.6131 16 23 16C25.387 16 27.6761 16.9482 29.364 18.6361C31.0518 20.3239 32 22.6131 32 25ZM27.47 24.128L21.482 20.828C21.3298 20.7443 21.1583 20.7016 20.9846 20.7043C20.8108 20.707 20.6408 20.7549 20.4912 20.8433C20.3416 20.9317 20.2176 21.0576 20.1315 21.2086C20.0453 21.3595 20 21.5302 20 21.704V28.304C20 28.4778 20.0453 28.6486 20.1315 28.7995C20.2176 28.9504 20.3416 29.0763 20.4912 29.1647C20.6408 29.2531 20.8108 29.301 20.9846 29.3037C21.1583 29.3064 21.3298 29.2638 21.482 29.18L27.47 25.88C27.6268 25.7937 27.7575 25.6669 27.8486 25.5128C27.9397 25.3587 27.9877 25.183 27.9877 25.004C27.9877 24.825 27.9397 24.6493 27.8486 24.4952C27.7575 24.3412 27.6268 24.2143 27.47 24.128Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex flex-col flex-1">
|
||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{audio?.mediaUpload?.title}</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<div className="mt-2">
|
||||
<img src="/assets/wave.svg" className="w-80" />
|
||||
</div>
|
||||
<div className="flex flex-row items-center justify-center text-gray-500 dark:text-gray-400">
|
||||
<img src="/assets/audio-icon.png" alt="#" className="flex items-center justify-center" />
|
||||
<div className="flex mx-2 items-center justify-center">{audio?.mediaUpload?.duration}</div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<path
|
||||
fill="#f00"
|
||||
d="M7.707 10.293a1 1 0 1 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414L11 11.586V6h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5v5.586zM9 4a1 1 0 0 1 2 0v2H9z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
</p>
|
||||
)
|
||||
) : selectedTab == "image" ? (
|
||||
contentImage?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{contentImage?.map((image: any) => (
|
||||
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
|
||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||
<Link href={`/image/detail/${image?.mediaUpload?.slug}`}>
|
||||
<img src={image?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.mediaUpload?.title}</div>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
</p>
|
||||
)
|
||||
) : contentDocument.length > 0 ? (
|
||||
<div className=" grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{contentDocument?.map((document: any) => (
|
||||
<Link href={`/document/detail/${document?.mediaUpload?.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">
|
||||
<div className="flex items-center justify-center rounded-lg w-16 h-16">
|
||||
<svg width="28" height="34" viewBox="0 0 28 34" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M5.6665 17.4167C5.6665 17.0851 5.7982 16.7672 6.03262 16.5328C6.26704 16.2984 6.58498 16.1667 6.9165 16.1667C7.24802 16.1667 7.56597 16.2984 7.80039 16.5328C8.03481 16.7672 8.1665 17.0851 8.1665 17.4167C8.1665 17.7482 8.03481 18.0661 7.80039 18.3005C7.56597 18.535 7.24802 18.6667 6.9165 18.6667C6.58498 18.6667 6.26704 18.535 6.03262 18.3005C5.7982 18.0661 5.6665 17.7482 5.6665 17.4167ZM6.9165 21.1667C6.58498 21.1667 6.26704 21.2984 6.03262 21.5328C5.7982 21.7672 5.6665 22.0851 5.6665 22.4167C5.6665 22.7482 5.7982 23.0661 6.03262 23.3005C6.26704 23.535 6.58498 23.6667 6.9165 23.6667C7.24802 23.6667 7.56597 23.535 7.80039 23.3005C8.03481 23.0661 8.1665 22.7482 8.1665 22.4167C8.1665 22.0851 8.03481 21.7672 7.80039 21.5328C7.56597 21.2984 7.24802 21.1667 6.9165 21.1667ZM5.6665 27.4167C5.6665 27.0851 5.7982 26.7672 6.03262 26.5328C6.26704 26.2984 6.58498 26.1667 6.9165 26.1667C7.24802 26.1667 7.56597 26.2984 7.80039 26.5328C8.03481 26.7672 8.1665 27.0851 8.1665 27.4167C8.1665 27.7482 8.03481 28.0661 7.80039 28.3005C7.56597 28.535 7.24802 28.6667 6.9165 28.6667C6.58498 28.6667 6.26704 28.535 6.03262 28.3005C5.7982 28.0661 5.6665 27.7482 5.6665 27.4167ZM11.9165 16.1667C11.585 16.1667 11.267 16.2984 11.0326 16.5328C10.7982 16.7672 10.6665 17.0851 10.6665 17.4167C10.6665 17.7482 10.7982 18.0661 11.0326 18.3005C11.267 18.535 11.585 18.6667 11.9165 18.6667H21.0832C21.4147 18.6667 21.7326 18.535 21.9671 18.3005C22.2015 18.0661 22.3332 17.7482 22.3332 17.4167C22.3332 17.0851 22.2015 16.7672 21.9671 16.5328C21.7326 16.2984 21.4147 16.1667 21.0832 16.1667H11.9165ZM10.6665 22.4167C10.6665 22.0851 10.7982 21.7672 11.0326 21.5328C11.267 21.2984 11.585 21.1667 11.9165 21.1667H21.0832C21.4147 21.1667 21.7326 21.2984 21.9671 21.5328C22.2015 21.7672 22.3332 22.0851 22.3332 22.4167C22.3332 22.7482 22.2015 23.0661 21.9671 23.3005C21.7326 23.535 21.4147 23.6667 21.0832 23.6667H11.9165C11.585 23.6667 11.267 23.535 11.0326 23.3005C10.7982 23.0661 10.6665 22.7482 10.6665 22.4167ZM11.9165 26.1667C11.585 26.1667 11.267 26.2984 11.0326 26.5328C10.7982 26.7672 10.6665 27.0851 10.6665 27.4167C10.6665 27.7482 10.7982 28.0661 11.0326 28.3005C11.267 28.535 11.585 28.6667 11.9165 28.6667H21.0832C21.4147 28.6667 21.7326 28.535 21.9671 28.3005C22.2015 28.0661 22.3332 27.7482 22.3332 27.4167C22.3332 27.0851 22.2015 26.7672 21.9671 26.5328C21.7326 26.2984 21.4147 26.1667 21.0832 26.1667H11.9165ZM26.3565 11.0233L16.6415 1.31C16.6157 1.28605 16.5885 1.26378 16.5598 1.24333C16.5392 1.22742 16.5192 1.21074 16.4998 1.19333C16.3852 1.08512 16.2632 0.984882 16.1348 0.893332C16.0922 0.865802 16.0476 0.841298 16.0015 0.819999L15.9215 0.779999L15.8382 0.731666C15.7482 0.679999 15.6565 0.626665 15.5615 0.586665C15.2296 0.454104 14.8783 0.376423 14.5215 0.356665C14.4885 0.354519 14.4557 0.350625 14.4232 0.344999C14.3779 0.338012 14.3323 0.334114 14.2865 0.333332H3.99984C3.11578 0.333332 2.26794 0.684521 1.64281 1.30964C1.01769 1.93476 0.666504 2.78261 0.666504 3.66667V30.3333C0.666504 31.2174 1.01769 32.0652 1.64281 32.6904C2.26794 33.3155 3.11578 33.6667 3.99984 33.6667H23.9998C24.8839 33.6667 25.7317 33.3155 26.3569 32.6904C26.982 32.0652 27.3332 31.2174 27.3332 30.3333V13.38C27.333 12.496 26.9817 11.6483 26.3565 11.0233ZM24.8332 30.3333C24.8332 30.5543 24.7454 30.7663 24.5891 30.9226C24.4328 31.0789 24.2208 31.1667 23.9998 31.1667H3.99984C3.77882 31.1667 3.56686 31.0789 3.41058 30.9226C3.2543 30.7663 3.1665 30.5543 3.1665 30.3333V3.66667C3.1665 3.44565 3.2543 3.23369 3.41058 3.07741C3.56686 2.92113 3.77882 2.83333 3.99984 2.83333H13.9998V10.3333C13.9998 11.2174 14.351 12.0652 14.9761 12.6904C15.6013 13.3155 16.4491 13.6667 17.3332 13.6667H24.8332V30.3333ZM16.4998 4.70166L22.9632 11.1667H17.3332C17.1122 11.1667 16.9002 11.0789 16.7439 10.9226C16.5876 10.7663 16.4998 10.5543 16.4998 10.3333V4.70166Z"
|
||||
fill="black"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col flex-1 gap-2">
|
||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{document?.mediaUpload?.title}</div>
|
||||
<div className="flex gap-2 items-center text-xs text-red-500 dark:text-red-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512">
|
||||
<path fill="#f00" d="M224 30v256h-64l96 128l96-128h-64V30zM32 434v48h448v-48z" />
|
||||
</svg>
|
||||
Download Dokumen
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Galery;
|
||||
|
|
@ -0,0 +1,395 @@
|
|||
"use client";
|
||||
|
||||
import { close, error, loading, successCallback } from "@/config/swal";
|
||||
import { checkWishlistStatus, deleteWishlist, getInfoProfile, mediaWishlist, saveWishlist } from "@/service/landing/landing";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import HeaderManagement from "@/components/landing-page/header-management";
|
||||
import SidebarManagement from "@/components/landing-page/sidebar-management";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import Swal from "sweetalert2";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
|
||||
const Galery = (props: any) => {
|
||||
const [profile, setProfile] = useState<any>();
|
||||
const [selectedTab, setSelectedTab] = useState("video");
|
||||
const router = useRouter();
|
||||
const MySwal = withReactContent(Swal);
|
||||
const searchParams = useSearchParams();
|
||||
// const { id } = router.query;
|
||||
const page: any = searchParams?.get("page");
|
||||
const title = searchParams?.get("title");
|
||||
const category = searchParams?.get("category");
|
||||
const pages = page ? page - 1 : 0;
|
||||
const { isInstitute, instituteId } = props;
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
const userRoleId = getCookiesDecrypt("urie");
|
||||
|
||||
const [totalContent, setTotalContent] = useState();
|
||||
const [categoryFilter] = useState([]);
|
||||
const [formatFilter] = useState([]);
|
||||
const [sortBy] = useState();
|
||||
const [change] = useState(false);
|
||||
const [contentVideo, setContentVideo] = useState([]);
|
||||
const [contentImage, setContentImage] = useState([]);
|
||||
const [contentDocument, setContentDocument] = useState([]);
|
||||
const [contentAudio, setContentAudio] = useState([]);
|
||||
const [, setGetTotalPage] = useState([]);
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [, setCopySuccess] = useState("");
|
||||
const [, setWishlistId] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
getDataVideo();
|
||||
}, [page, category, title]);
|
||||
|
||||
async function getDataVideo() {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : category || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
const response = await mediaWishlist("2", isInstitute ? instituteId : "", name, filter, "9", pages, sortBy, format);
|
||||
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentVideo(response?.data?.data?.content);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
loading();
|
||||
const response = await getInfoProfile();
|
||||
|
||||
// console.log(response?.data.data);
|
||||
setProfile(response?.data?.data);
|
||||
close();
|
||||
}
|
||||
|
||||
initState();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getDataVideo();
|
||||
}, [change, refresh]);
|
||||
|
||||
useEffect(() => {
|
||||
getDataDocument();
|
||||
}, [page, category, title]);
|
||||
|
||||
async function getDataDocument() {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : category || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
|
||||
const response = await mediaWishlist("3", isInstitute ? instituteId : "", name, filter, "12", pages, sortBy, format);
|
||||
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentDocument(response?.data?.data?.content);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getDataDocument();
|
||||
}, [change, refresh]);
|
||||
|
||||
useEffect(() => {
|
||||
getDataAudio();
|
||||
}, [page, category, title]);
|
||||
|
||||
useEffect(() => {
|
||||
getDataAudio();
|
||||
}, [change, refresh]);
|
||||
|
||||
async function getDataAudio() {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : category || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
|
||||
const response = await mediaWishlist("4", isInstitute ? instituteId : "", name, filter, "6", pages, sortBy, format);
|
||||
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentAudio(response?.data?.data?.content);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getDataAudio();
|
||||
}, [change]);
|
||||
|
||||
useEffect(() => {
|
||||
getDataImage();
|
||||
}, [page, category, title, refresh]);
|
||||
|
||||
async function getDataImage() {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : category || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
|
||||
const response = await mediaWishlist("1", isInstitute ? instituteId : "", name, filter, "12", pages, sortBy, format);
|
||||
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentImage(response?.data?.data?.content);
|
||||
// console.log("response", response);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
getDataImage();
|
||||
}
|
||||
|
||||
initState();
|
||||
}, [page, refresh]);
|
||||
|
||||
async function checkWishlist(uploadId: any) {
|
||||
if (userId) {
|
||||
const res = await checkWishlistStatus(uploadId);
|
||||
console.log(res?.data?.data);
|
||||
// const isAlreadyOnWishlist = res?.data?.data == "-1" ? false : true;
|
||||
// if (isAlreadyOnWishlist == true) {
|
||||
// warning("Konten sudah Ada", `#`);
|
||||
// }
|
||||
setWishlistId(res?.data?.data); // setIsSaved(isAlreadyOnWishlist);
|
||||
// console.log("isSave", isAlreadyOnWishlist);
|
||||
}
|
||||
}
|
||||
|
||||
const handleSaveWishlist = async (uploadId: any) => {
|
||||
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||
router.push("/auth/login");
|
||||
} else {
|
||||
console.log("data", uploadId);
|
||||
const data = {
|
||||
mediaUploadId: uploadId,
|
||||
};
|
||||
|
||||
loading();
|
||||
checkWishlist(uploadId);
|
||||
|
||||
const res = await saveWishlist(data);
|
||||
if (res?.error) {
|
||||
error(res.message);
|
||||
console.log("simpan data", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
successCallback("Konten Berhasil Disimpan");
|
||||
}
|
||||
};
|
||||
|
||||
async function deleteProcess(id: any) {
|
||||
loading();
|
||||
const resDelete = await deleteWishlist(id);
|
||||
|
||||
if (resDelete?.error) {
|
||||
error(resDelete.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
setRefresh((prevRefresh) => !prevRefresh);
|
||||
close();
|
||||
}
|
||||
|
||||
const handleDelete = (id: any) => {
|
||||
MySwal.fire({
|
||||
title: "Hapus Data",
|
||||
text: "",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#3085d6",
|
||||
confirmButtonColor: "#d33",
|
||||
confirmButtonText: "Hapus",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
deleteProcess(id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const copyToClip = async (url: any) => {
|
||||
await navigator.clipboard.writeText(`https://mediahub.polri.go.id/video/detail/${url}`);
|
||||
setCopySuccess("Copied");
|
||||
// toast.success("Link Berhasil Di Copy");
|
||||
};
|
||||
|
||||
const [hasMounted, setHasMounted] = useState(false);
|
||||
|
||||
// Hooks
|
||||
useEffect(() => {
|
||||
setHasMounted(true);
|
||||
}, []);
|
||||
|
||||
// Render
|
||||
if (!hasMounted) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderManagement />
|
||||
<div className="flex flex-row">
|
||||
<SidebarManagement />
|
||||
<div className="w-2/3 p-12">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold">Galeri {profile?.institute?.name}</h1>
|
||||
</div>
|
||||
<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">
|
||||
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
|
||||
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
||||
<TabsTrigger
|
||||
value="video"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
>
|
||||
Audio Visual
|
||||
</TabsTrigger>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">|</div>
|
||||
<TabsTrigger
|
||||
value="audio"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
>
|
||||
Audio
|
||||
</TabsTrigger>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">|</div>
|
||||
<TabsTrigger
|
||||
value="image"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
>
|
||||
Foto
|
||||
</TabsTrigger>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">|</div>
|
||||
<TabsTrigger
|
||||
value="text"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
>
|
||||
Teks
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className="px-0 lg:px-10">
|
||||
{selectedTab == "video" ? (
|
||||
contentVideo?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{contentVideo?.map((video: any) => (
|
||||
<Card key={video?.id} className="hover:scale-110 transition-transform duration-300">
|
||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||
<Link href={`/video/detail/${video?.mediaUpload?.slug}`}>
|
||||
<img src={video?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{video?.mediaUpload?.title}</div>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
</p>
|
||||
)
|
||||
) : selectedTab == "audio" ? (
|
||||
contentAudio?.length > 0 ? (
|
||||
<div className=" grid grid-cols-1 gap-6 ">
|
||||
{contentAudio?.map((audio: any) => (
|
||||
<Link
|
||||
href={`/audio/detail/${audio?.mediaUpload?.slug}`}
|
||||
key={audio?.id}
|
||||
className="flex flex-col sm:flex-row items-center hover:scale-110 transition-transform duration-300 bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full"
|
||||
>
|
||||
<div className="flex items-center justify-center bg-red-500 text-white rounded-lg w-16 h-8 lg:h-16">
|
||||
<svg width="32" height="34" viewBox="0 0 32 34" fill="null" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M23.404 0.452014C23.7033 0.35857 24.0204 0.336816 24.3297 0.388509C24.639 0.440203 24.9318 0.563895 25.1845 0.749599C25.4371 0.935304 25.6426 1.17782 25.7843 1.45756C25.9259 1.73731 25.9998 2.04644 26 2.36001V14.414C25.3462 14.2296 24.6766 14.1064 24 14.046V8.36001L10 12.736V27C10 28.1264 9.6197 29.2197 8.92071 30.1029C8.22172 30.9861 7.24499 31.6075 6.14877 31.8663C5.05255 32.125 3.90107 32.0061 2.88089 31.5287C1.86071 31.0514 1.03159 30.2435 0.52787 29.2361C0.024152 28.2286 -0.124656 27.0806 0.105556 25.9781C0.335768 24.8755 0.931513 23.883 1.79627 23.1613C2.66102 22.4396 3.74413 22.031 4.87009 22.0017C5.99606 21.9724 7.09893 22.3242 8.00001 23V6.73601C7.99982 6.30956 8.13596 5.8942 8.38854 5.55059C8.64112 5.20698 8.99692 4.9531 9.40401 4.82601L23.404 0.452014ZM10 10.64L24 6.26601V2.36001L10 6.73601V10.64ZM5.00001 24C4.20436 24 3.44129 24.3161 2.87869 24.8787C2.31608 25.4413 2.00001 26.2044 2.00001 27C2.00001 27.7957 2.31608 28.5587 2.87869 29.1213C3.44129 29.6839 4.20436 30 5.00001 30C5.79566 30 6.55872 29.6839 7.12133 29.1213C7.68394 28.5587 8.00001 27.7957 8.00001 27C8.00001 26.2044 7.68394 25.4413 7.12133 24.8787C6.55872 24.3161 5.79566 24 5.00001 24ZM32 25C32 27.387 31.0518 29.6761 29.364 31.364C27.6761 33.0518 25.387 34 23 34C20.6131 34 18.3239 33.0518 16.636 31.364C14.9482 29.6761 14 27.387 14 25C14 22.6131 14.9482 20.3239 16.636 18.6361C18.3239 16.9482 20.6131 16 23 16C25.387 16 27.6761 16.9482 29.364 18.6361C31.0518 20.3239 32 22.6131 32 25ZM27.47 24.128L21.482 20.828C21.3298 20.7443 21.1583 20.7016 20.9846 20.7043C20.8108 20.707 20.6408 20.7549 20.4912 20.8433C20.3416 20.9317 20.2176 21.0576 20.1315 21.2086C20.0453 21.3595 20 21.5302 20 21.704V28.304C20 28.4778 20.0453 28.6486 20.1315 28.7995C20.2176 28.9504 20.3416 29.0763 20.4912 29.1647C20.6408 29.2531 20.8108 29.301 20.9846 29.3037C21.1583 29.3064 21.3298 29.2638 21.482 29.18L27.47 25.88C27.6268 25.7937 27.7575 25.6669 27.8486 25.5128C27.9397 25.3587 27.9877 25.183 27.9877 25.004C27.9877 24.825 27.9397 24.6493 27.8486 24.4952C27.7575 24.3412 27.6268 24.2143 27.47 24.128Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex flex-col flex-1">
|
||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{audio?.mediaUpload?.title}</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<div className="mt-2">
|
||||
<img src="/assets/wave.svg" className="w-80" />
|
||||
</div>
|
||||
<div className="flex flex-row items-center justify-center text-gray-500 dark:text-gray-400">
|
||||
<img src="/assets/audio-icon.png" alt="#" className="flex items-center justify-center" />
|
||||
<div className="flex mx-2 items-center justify-center">{audio?.mediaUpload?.duration}</div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<path
|
||||
fill="#f00"
|
||||
d="M7.707 10.293a1 1 0 1 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414L11 11.586V6h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5v5.586zM9 4a1 1 0 0 1 2 0v2H9z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
</p>
|
||||
)
|
||||
) : selectedTab == "image" ? (
|
||||
contentImage?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{contentImage?.map((image: any) => (
|
||||
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
|
||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||
<Link href={`/image/detail/${image?.mediaUpload?.slug}`}>
|
||||
<img src={image?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.mediaUpload?.title}</div>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
</p>
|
||||
)
|
||||
) : contentDocument.length > 0 ? (
|
||||
<div className=" grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{contentDocument?.map((document: any) => (
|
||||
<Link href={`/document/detail/${document?.mediaUpload?.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">
|
||||
<div className="flex items-center justify-center rounded-lg w-16 h-16">
|
||||
<svg width="28" height="34" viewBox="0 0 28 34" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M5.6665 17.4167C5.6665 17.0851 5.7982 16.7672 6.03262 16.5328C6.26704 16.2984 6.58498 16.1667 6.9165 16.1667C7.24802 16.1667 7.56597 16.2984 7.80039 16.5328C8.03481 16.7672 8.1665 17.0851 8.1665 17.4167C8.1665 17.7482 8.03481 18.0661 7.80039 18.3005C7.56597 18.535 7.24802 18.6667 6.9165 18.6667C6.58498 18.6667 6.26704 18.535 6.03262 18.3005C5.7982 18.0661 5.6665 17.7482 5.6665 17.4167ZM6.9165 21.1667C6.58498 21.1667 6.26704 21.2984 6.03262 21.5328C5.7982 21.7672 5.6665 22.0851 5.6665 22.4167C5.6665 22.7482 5.7982 23.0661 6.03262 23.3005C6.26704 23.535 6.58498 23.6667 6.9165 23.6667C7.24802 23.6667 7.56597 23.535 7.80039 23.3005C8.03481 23.0661 8.1665 22.7482 8.1665 22.4167C8.1665 22.0851 8.03481 21.7672 7.80039 21.5328C7.56597 21.2984 7.24802 21.1667 6.9165 21.1667ZM5.6665 27.4167C5.6665 27.0851 5.7982 26.7672 6.03262 26.5328C6.26704 26.2984 6.58498 26.1667 6.9165 26.1667C7.24802 26.1667 7.56597 26.2984 7.80039 26.5328C8.03481 26.7672 8.1665 27.0851 8.1665 27.4167C8.1665 27.7482 8.03481 28.0661 7.80039 28.3005C7.56597 28.535 7.24802 28.6667 6.9165 28.6667C6.58498 28.6667 6.26704 28.535 6.03262 28.3005C5.7982 28.0661 5.6665 27.7482 5.6665 27.4167ZM11.9165 16.1667C11.585 16.1667 11.267 16.2984 11.0326 16.5328C10.7982 16.7672 10.6665 17.0851 10.6665 17.4167C10.6665 17.7482 10.7982 18.0661 11.0326 18.3005C11.267 18.535 11.585 18.6667 11.9165 18.6667H21.0832C21.4147 18.6667 21.7326 18.535 21.9671 18.3005C22.2015 18.0661 22.3332 17.7482 22.3332 17.4167C22.3332 17.0851 22.2015 16.7672 21.9671 16.5328C21.7326 16.2984 21.4147 16.1667 21.0832 16.1667H11.9165ZM10.6665 22.4167C10.6665 22.0851 10.7982 21.7672 11.0326 21.5328C11.267 21.2984 11.585 21.1667 11.9165 21.1667H21.0832C21.4147 21.1667 21.7326 21.2984 21.9671 21.5328C22.2015 21.7672 22.3332 22.0851 22.3332 22.4167C22.3332 22.7482 22.2015 23.0661 21.9671 23.3005C21.7326 23.535 21.4147 23.6667 21.0832 23.6667H11.9165C11.585 23.6667 11.267 23.535 11.0326 23.3005C10.7982 23.0661 10.6665 22.7482 10.6665 22.4167ZM11.9165 26.1667C11.585 26.1667 11.267 26.2984 11.0326 26.5328C10.7982 26.7672 10.6665 27.0851 10.6665 27.4167C10.6665 27.7482 10.7982 28.0661 11.0326 28.3005C11.267 28.535 11.585 28.6667 11.9165 28.6667H21.0832C21.4147 28.6667 21.7326 28.535 21.9671 28.3005C22.2015 28.0661 22.3332 27.7482 22.3332 27.4167C22.3332 27.0851 22.2015 26.7672 21.9671 26.5328C21.7326 26.2984 21.4147 26.1667 21.0832 26.1667H11.9165ZM26.3565 11.0233L16.6415 1.31C16.6157 1.28605 16.5885 1.26378 16.5598 1.24333C16.5392 1.22742 16.5192 1.21074 16.4998 1.19333C16.3852 1.08512 16.2632 0.984882 16.1348 0.893332C16.0922 0.865802 16.0476 0.841298 16.0015 0.819999L15.9215 0.779999L15.8382 0.731666C15.7482 0.679999 15.6565 0.626665 15.5615 0.586665C15.2296 0.454104 14.8783 0.376423 14.5215 0.356665C14.4885 0.354519 14.4557 0.350625 14.4232 0.344999C14.3779 0.338012 14.3323 0.334114 14.2865 0.333332H3.99984C3.11578 0.333332 2.26794 0.684521 1.64281 1.30964C1.01769 1.93476 0.666504 2.78261 0.666504 3.66667V30.3333C0.666504 31.2174 1.01769 32.0652 1.64281 32.6904C2.26794 33.3155 3.11578 33.6667 3.99984 33.6667H23.9998C24.8839 33.6667 25.7317 33.3155 26.3569 32.6904C26.982 32.0652 27.3332 31.2174 27.3332 30.3333V13.38C27.333 12.496 26.9817 11.6483 26.3565 11.0233ZM24.8332 30.3333C24.8332 30.5543 24.7454 30.7663 24.5891 30.9226C24.4328 31.0789 24.2208 31.1667 23.9998 31.1667H3.99984C3.77882 31.1667 3.56686 31.0789 3.41058 30.9226C3.2543 30.7663 3.1665 30.5543 3.1665 30.3333V3.66667C3.1665 3.44565 3.2543 3.23369 3.41058 3.07741C3.56686 2.92113 3.77882 2.83333 3.99984 2.83333H13.9998V10.3333C13.9998 11.2174 14.351 12.0652 14.9761 12.6904C15.6013 13.3155 16.4491 13.6667 17.3332 13.6667H24.8332V30.3333ZM16.4998 4.70166L22.9632 11.1667H17.3332C17.1122 11.1667 16.9002 11.0789 16.7439 10.9226C16.5876 10.7663 16.4998 10.5543 16.4998 10.3333V4.70166Z"
|
||||
fill="black"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col flex-1 gap-2">
|
||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{document?.mediaUpload?.title}</div>
|
||||
<div className="flex gap-2 items-center text-xs text-red-500 dark:text-red-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512">
|
||||
<path fill="#f00" d="M224 30v256h-64l96 128l96-128h-64V30zM32 434v48h448v-48z" />
|
||||
</svg>
|
||||
Download Dokumen
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Galery;
|
||||
|
|
@ -13,6 +13,7 @@ const layout = async ({ children }: { children: React.ReactNode }) => {
|
|||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
|
||||
{children}
|
||||
<Footer />
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,379 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { getInfoProfile, getListPorvinces, getUsersTeams } from "@/service/landing/landing";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel";
|
||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { formatDateToIndonesian } from "@/utils/globals";
|
||||
import { useParams } from "next/navigation";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
const dummyContent = [
|
||||
{ title: "Operasi Zebra Nataru", thumbnail: "/assets/img-header-blog.png", createdAt: "17 Agustus 2025", timezone: "14:20 WIB", clickCount: "28" },
|
||||
{ title: "Operasi Zebra Nataru", thumbnail: "/assets/hot-topik-2.jpg", createdAt: "17 Agustus 2025", timezone: "14:20 WIB", clickCount: "28" },
|
||||
{ title: "Operasi Zebra Nataru", thumbnail: "/assets/hot-topik-1.jpg", createdAt: "17 Agustus 2025", timezone: "14:20 WIB", clickCount: "28" },
|
||||
{ title: "Operasi Zebra Nataru", thumbnail: "/assets/hot-topik-1.jpg", createdAt: "17 Agustus 2025", timezone: "14:20 WIB", clickCount: "28" },
|
||||
{ title: "Operasi Zebra Nataru", thumbnail: "/assets/hot-topik-1.jpg", createdAt: "17 Agustus 2025", timezone: "14:20 WIB", clickCount: "28" },
|
||||
{ title: "Operasi Zebra Nataru", thumbnail: "/assets/hot-topik-1.jpg", createdAt: "17 Agustus 2025", timezone: "14:20 WIB", clickCount: "28" },
|
||||
];
|
||||
|
||||
const ContentManagement = () => {
|
||||
const [profile, setProfile] = useState<any>();
|
||||
const [province, setProvince] = useState([]);
|
||||
const [, setUser] = useState();
|
||||
const [selectedTab, setSelectedTab] = useState("video");
|
||||
const params = useParams();
|
||||
|
||||
// const currentRoute = router.pathname;
|
||||
// const profilePicture = Cookies.get("profile_picture");
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
const response = await getInfoProfile();
|
||||
setProfile(response?.data?.data);
|
||||
}
|
||||
|
||||
async function getProvinces() {
|
||||
const response = await getListPorvinces();
|
||||
|
||||
// console.log(response?.data.data);
|
||||
setProvince(response?.data?.data);
|
||||
}
|
||||
|
||||
// async function getDisticts() {
|
||||
// const response = await getListDistricts();
|
||||
// console.log(response?.data.data);
|
||||
// setDistrict(response?.data.data);
|
||||
// }
|
||||
initState();
|
||||
getProvinces(); // getDisticts();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
if (profile != undefined) {
|
||||
const response = await getUsersTeams(profile?.instituteId);
|
||||
|
||||
// console.log(response?.data?.data);
|
||||
setUser(response?.data?.data);
|
||||
}
|
||||
}
|
||||
|
||||
initState();
|
||||
}, [profile]);
|
||||
|
||||
function addDefaultProfile(ev: any) {
|
||||
ev.target.src = "/assets/avatar-profile.png";
|
||||
}
|
||||
|
||||
const [hasMounted, setHasMounted] = useState(false);
|
||||
// Hooks
|
||||
useEffect(() => {
|
||||
setHasMounted(true);
|
||||
}, []);
|
||||
|
||||
// Render
|
||||
if (!hasMounted) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{/* Header */}
|
||||
<div className="bg-[#504e52] p-12">
|
||||
<div className="flex justify-between mx-10">
|
||||
<div className="flex items-center gap-2 ">
|
||||
<img src="/assets/avatar-profile.png" alt="avatar" className="w-14 h-14" />
|
||||
<div className="flex flex-col mx-2">
|
||||
<p className="text-white text-sm font-semibold">{profile?.fullname}</p>
|
||||
<p className="text-white text-sm font-light">{profile?.username}</p>
|
||||
<p className="text-white text-sm font-light">
|
||||
Aktif Sejak
|
||||
{`${new Date(profile?.createdAt).getDate()}/${new Date(profile?.createdAt).getMonth() + 1}/${new Date(profile?.createdAt).getFullYear()} ${new Date(profile?.createdAt).getHours()}:${new Date(
|
||||
profile?.createdAt
|
||||
).getMinutes()}`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Link href="/profile" className="flex items-center text-white gap-2">
|
||||
<Icon icon="tdesign:setting-1-filled" />
|
||||
Pengaturan
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col lg:flex-row">
|
||||
{/* Konten Kiri */}
|
||||
<div className="p-12 w-1/3">
|
||||
<div className="border rounded-2xl border-black m-4">
|
||||
<h1 className="text-xl p-5">Tentang Saya</h1>
|
||||
<div>
|
||||
<ul className="px-10 mb-4">
|
||||
<li className="mb-5 font-light">
|
||||
<p className="font-semibold">Email :</p>
|
||||
{/* <p>msabdayagra@gmail.com</p> */}
|
||||
<p>{profile?.email}</p>
|
||||
</li>
|
||||
<li className="mb-5 font-light">
|
||||
<p className="font-semibold">No Handphone :</p>
|
||||
{/* <p>0812-7561-7204</p> */}
|
||||
<p>{profile?.phoneNumber}</p>
|
||||
</li>
|
||||
<li className="mb-5 font-light">
|
||||
<p className="font-semibold">Alamat :</p>
|
||||
{/* <p>Jl. Besar Tembung no.12</p> */}
|
||||
<p>{profile?.address}</p>
|
||||
</li>
|
||||
<li className="mb-5 font-light">
|
||||
<p className="font-semibold">Kategori :</p>
|
||||
{/* <p>POLRI</p> */}
|
||||
<p>{profile?.institute?.categoryRole?.name}</p>
|
||||
</li>
|
||||
<li className="mb-5 font-light">
|
||||
<p className="font-semibold">Instansi/Perusahaan :</p>
|
||||
{/* <p>Div Humas Polri</p> */}
|
||||
<p>{profile?.institute?.name}</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-12">
|
||||
<div className="mb-3">
|
||||
<div className="hover:bg-slate-800 cursor-pointer rounded-lg flex justify-between">
|
||||
<div className="flex items-center gap-2 text-lg">
|
||||
<Icon icon="material-symbols-light:perm-media-rounded" />
|
||||
<p className="text-sm">Galeri {profile?.institute?.name}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Icon icon="ri:arrow-right-s-line" fontSize={20} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="hover:bg-slate-800 cursor-pointer rounded-lg flex justify-between">
|
||||
<div className="flex items-center gap-2 text-lg">
|
||||
<Icon icon="heroicons:photo-solid" />
|
||||
<p>Galeri Saya</p>
|
||||
</div>
|
||||
<div>
|
||||
<Icon icon="ri:arrow-right-s-line" fontSize={20} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="hover:bg-slate-800 cursor-pointer rounded-lg flex justify-between">
|
||||
<div className="flex items-center gap-2 text-lg">
|
||||
<Icon icon="material-symbols-light:perm-media-rounded" />
|
||||
<p>Galeri Rewrite</p>
|
||||
</div>
|
||||
<div>
|
||||
<Icon icon="ri:arrow-right-s-line" fontSize={20} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="hover:bg-slate-800 cursor-pointer rounded-lg flex justify-between">
|
||||
<div className="flex items-center gap-2 text-lg">
|
||||
<Icon icon="mdi:users-group" />
|
||||
<p>Tim Pengguna</p>
|
||||
</div>
|
||||
<div>
|
||||
<Icon icon="ri:arrow-right-s-line" fontSize={20} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Kontent Kanan */}
|
||||
<div className="w-2/3 p-12">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold">Galeri {profile?.institute?.name}</h1>
|
||||
</div>
|
||||
<div className="px-4 lg:px-10 py-4">
|
||||
<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">
|
||||
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
|
||||
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
||||
<TabsTrigger
|
||||
value="video"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
>
|
||||
Audio Visual
|
||||
</TabsTrigger>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">|</div>
|
||||
<TabsTrigger
|
||||
value="audio"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
>
|
||||
Audio
|
||||
</TabsTrigger>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">|</div>
|
||||
<TabsTrigger
|
||||
value="image"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
>
|
||||
Foto
|
||||
</TabsTrigger>
|
||||
<div className="text-[#bb3523] text-lg hidden md:inline-block">|</div>
|
||||
<TabsTrigger
|
||||
value="text"
|
||||
className="relative text-xs md:text-xl font-bold text-black dark:text-white dark:bg-transparent before:absolute before:top-full before:left-0 before:h-px before:w-full data-[state=active]:before:bg-primary"
|
||||
>
|
||||
Teks
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className="px-0 lg:px-10">
|
||||
{selectedTab == "video" ? (
|
||||
profile?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{profile?.map((video: any) => (
|
||||
<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?.thumbnail} 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}{" "}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 20 20">
|
||||
<path
|
||||
fill="#f00"
|
||||
d="M7.707 10.293a1 1 0 1 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414L11 11.586V6h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5v5.586zM9 4a1 1 0 0 1 2 0v2H9z"
|
||||
/>
|
||||
</svg>{" "}
|
||||
</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>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
</p>
|
||||
)
|
||||
) : selectedTab == "audio" ? (
|
||||
profile?.length > 0 ? (
|
||||
<div className=" grid grid-cols-1 gap-6 ">
|
||||
{profile?.map((audio: any) => (
|
||||
<Link
|
||||
href={`/audio/detail/${audio?.slug}`}
|
||||
key={audio?.id}
|
||||
className="flex flex-col sm:flex-row items-center hover:scale-110 transition-transform duration-300 bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full"
|
||||
>
|
||||
<div className="flex items-center justify-center bg-[#bb3523] text-white rounded-lg w-16 h-16">
|
||||
<svg width="32" height="34" viewBox="0 0 32 34" fill="null" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M23.404 0.452014C23.7033 0.35857 24.0204 0.336816 24.3297 0.388509C24.639 0.440203 24.9318 0.563895 25.1845 0.749599C25.4371 0.935304 25.6426 1.17782 25.7843 1.45756C25.9259 1.73731 25.9998 2.04644 26 2.36001V14.414C25.3462 14.2296 24.6766 14.1064 24 14.046V8.36001L10 12.736V27C10 28.1264 9.6197 29.2197 8.92071 30.1029C8.22172 30.9861 7.24499 31.6075 6.14877 31.8663C5.05255 32.125 3.90107 32.0061 2.88089 31.5287C1.86071 31.0514 1.03159 30.2435 0.52787 29.2361C0.024152 28.2286 -0.124656 27.0806 0.105556 25.9781C0.335768 24.8755 0.931513 23.883 1.79627 23.1613C2.66102 22.4396 3.74413 22.031 4.87009 22.0017C5.99606 21.9724 7.09893 22.3242 8.00001 23V6.73601C7.99982 6.30956 8.13596 5.8942 8.38854 5.55059C8.64112 5.20698 8.99692 4.9531 9.40401 4.82601L23.404 0.452014ZM10 10.64L24 6.26601V2.36001L10 6.73601V10.64ZM5.00001 24C4.20436 24 3.44129 24.3161 2.87869 24.8787C2.31608 25.4413 2.00001 26.2044 2.00001 27C2.00001 27.7957 2.31608 28.5587 2.87869 29.1213C3.44129 29.6839 4.20436 30 5.00001 30C5.79566 30 6.55872 29.6839 7.12133 29.1213C7.68394 28.5587 8.00001 27.7957 8.00001 27C8.00001 26.2044 7.68394 25.4413 7.12133 24.8787C6.55872 24.3161 5.79566 24 5.00001 24ZM32 25C32 27.387 31.0518 29.6761 29.364 31.364C27.6761 33.0518 25.387 34 23 34C20.6131 34 18.3239 33.0518 16.636 31.364C14.9482 29.6761 14 27.387 14 25C14 22.6131 14.9482 20.3239 16.636 18.6361C18.3239 16.9482 20.6131 16 23 16C25.387 16 27.6761 16.9482 29.364 18.6361C31.0518 20.3239 32 22.6131 32 25ZM27.47 24.128L21.482 20.828C21.3298 20.7443 21.1583 20.7016 20.9846 20.7043C20.8108 20.707 20.6408 20.7549 20.4912 20.8433C20.3416 20.9317 20.2176 21.0576 20.1315 21.2086C20.0453 21.3595 20 21.5302 20 21.704V28.304C20 28.4778 20.0453 28.6486 20.1315 28.7995C20.2176 28.9504 20.3416 29.0763 20.4912 29.1647C20.6408 29.2531 20.8108 29.301 20.9846 29.3037C21.1583 29.3064 21.3298 29.2638 21.482 29.18L27.47 25.88C27.6268 25.7937 27.7575 25.6669 27.8486 25.5128C27.9397 25.3587 27.9877 25.183 27.9877 25.004C27.9877 24.825 27.9397 24.6493 27.8486 24.4952C27.7575 24.3412 27.6268 24.2143 27.47 24.128Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col flex-1">
|
||||
<div className="text-gray-500 dark:text-gray-400 flex flex-row text-sm">
|
||||
{formatDateToIndonesian(new Date(audio?.createdAt))} {audio?.timezone ? audio?.timezone : "WIB"} | <Icon icon="formkit:eye" width="15" height="15" /> 518
|
||||
</div>
|
||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{audio?.title}</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<div className="mt-2">
|
||||
<img src="/assets/wave.svg" className="w-80" />
|
||||
</div>
|
||||
<div className="flex flex-row items-center justify-center text-gray-500 dark:text-gray-400">
|
||||
<img src="/assets/audio-icon.png" alt="#" className="flex items-center justify-center" />
|
||||
<div className="flex mx-2 items-center justify-center">{audio?.duration}</div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<path
|
||||
fill="#f00"
|
||||
d="M7.707 10.293a1 1 0 1 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414L11 11.586V6h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5v5.586zM9 4a1 1 0 0 1 2 0v2H9z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
</p>
|
||||
)
|
||||
) : selectedTab == "image" ? (
|
||||
profile?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{profile?.map((image: any) => (
|
||||
<Card key={image?.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={`/image/detail/${image?.slug}`}>
|
||||
<img src={image?.thumbnail} 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(image?.createdAt))} {image?.timezone ? image?.timezone : "WIB"}| <Icon icon="formkit:eye" width="15" height="15" />
|
||||
{image?.clickCount}{" "}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 20 20">
|
||||
<path
|
||||
fill="#f00"
|
||||
d="M7.707 10.293a1 1 0 1 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414L11 11.586V6h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5v5.586zM9 4a1 1 0 0 1 2 0v2H9z"
|
||||
/>
|
||||
</svg>{" "}
|
||||
</div>
|
||||
<div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible w-full">{image?.title}</div>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
</p>
|
||||
)
|
||||
) : profile.length > 0 ? (
|
||||
<div className=" grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{profile?.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">
|
||||
<div className="flex items-center justify-center rounded-lg w-16 h-16">
|
||||
<svg width="28" height="34" viewBox="0 0 28 34" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M5.6665 17.4167C5.6665 17.0851 5.7982 16.7672 6.03262 16.5328C6.26704 16.2984 6.58498 16.1667 6.9165 16.1667C7.24802 16.1667 7.56597 16.2984 7.80039 16.5328C8.03481 16.7672 8.1665 17.0851 8.1665 17.4167C8.1665 17.7482 8.03481 18.0661 7.80039 18.3005C7.56597 18.535 7.24802 18.6667 6.9165 18.6667C6.58498 18.6667 6.26704 18.535 6.03262 18.3005C5.7982 18.0661 5.6665 17.7482 5.6665 17.4167ZM6.9165 21.1667C6.58498 21.1667 6.26704 21.2984 6.03262 21.5328C5.7982 21.7672 5.6665 22.0851 5.6665 22.4167C5.6665 22.7482 5.7982 23.0661 6.03262 23.3005C6.26704 23.535 6.58498 23.6667 6.9165 23.6667C7.24802 23.6667 7.56597 23.535 7.80039 23.3005C8.03481 23.0661 8.1665 22.7482 8.1665 22.4167C8.1665 22.0851 8.03481 21.7672 7.80039 21.5328C7.56597 21.2984 7.24802 21.1667 6.9165 21.1667ZM5.6665 27.4167C5.6665 27.0851 5.7982 26.7672 6.03262 26.5328C6.26704 26.2984 6.58498 26.1667 6.9165 26.1667C7.24802 26.1667 7.56597 26.2984 7.80039 26.5328C8.03481 26.7672 8.1665 27.0851 8.1665 27.4167C8.1665 27.7482 8.03481 28.0661 7.80039 28.3005C7.56597 28.535 7.24802 28.6667 6.9165 28.6667C6.58498 28.6667 6.26704 28.535 6.03262 28.3005C5.7982 28.0661 5.6665 27.7482 5.6665 27.4167ZM11.9165 16.1667C11.585 16.1667 11.267 16.2984 11.0326 16.5328C10.7982 16.7672 10.6665 17.0851 10.6665 17.4167C10.6665 17.7482 10.7982 18.0661 11.0326 18.3005C11.267 18.535 11.585 18.6667 11.9165 18.6667H21.0832C21.4147 18.6667 21.7326 18.535 21.9671 18.3005C22.2015 18.0661 22.3332 17.7482 22.3332 17.4167C22.3332 17.0851 22.2015 16.7672 21.9671 16.5328C21.7326 16.2984 21.4147 16.1667 21.0832 16.1667H11.9165ZM10.6665 22.4167C10.6665 22.0851 10.7982 21.7672 11.0326 21.5328C11.267 21.2984 11.585 21.1667 11.9165 21.1667H21.0832C21.4147 21.1667 21.7326 21.2984 21.9671 21.5328C22.2015 21.7672 22.3332 22.0851 22.3332 22.4167C22.3332 22.7482 22.2015 23.0661 21.9671 23.3005C21.7326 23.535 21.4147 23.6667 21.0832 23.6667H11.9165C11.585 23.6667 11.267 23.535 11.0326 23.3005C10.7982 23.0661 10.6665 22.7482 10.6665 22.4167ZM11.9165 26.1667C11.585 26.1667 11.267 26.2984 11.0326 26.5328C10.7982 26.7672 10.6665 27.0851 10.6665 27.4167C10.6665 27.7482 10.7982 28.0661 11.0326 28.3005C11.267 28.535 11.585 28.6667 11.9165 28.6667H21.0832C21.4147 28.6667 21.7326 28.535 21.9671 28.3005C22.2015 28.0661 22.3332 27.7482 22.3332 27.4167C22.3332 27.0851 22.2015 26.7672 21.9671 26.5328C21.7326 26.2984 21.4147 26.1667 21.0832 26.1667H11.9165ZM26.3565 11.0233L16.6415 1.31C16.6157 1.28605 16.5885 1.26378 16.5598 1.24333C16.5392 1.22742 16.5192 1.21074 16.4998 1.19333C16.3852 1.08512 16.2632 0.984882 16.1348 0.893332C16.0922 0.865802 16.0476 0.841298 16.0015 0.819999L15.9215 0.779999L15.8382 0.731666C15.7482 0.679999 15.6565 0.626665 15.5615 0.586665C15.2296 0.454104 14.8783 0.376423 14.5215 0.356665C14.4885 0.354519 14.4557 0.350625 14.4232 0.344999C14.3779 0.338012 14.3323 0.334114 14.2865 0.333332H3.99984C3.11578 0.333332 2.26794 0.684521 1.64281 1.30964C1.01769 1.93476 0.666504 2.78261 0.666504 3.66667V30.3333C0.666504 31.2174 1.01769 32.0652 1.64281 32.6904C2.26794 33.3155 3.11578 33.6667 3.99984 33.6667H23.9998C24.8839 33.6667 25.7317 33.3155 26.3569 32.6904C26.982 32.0652 27.3332 31.2174 27.3332 30.3333V13.38C27.333 12.496 26.9817 11.6483 26.3565 11.0233ZM24.8332 30.3333C24.8332 30.5543 24.7454 30.7663 24.5891 30.9226C24.4328 31.0789 24.2208 31.1667 23.9998 31.1667H3.99984C3.77882 31.1667 3.56686 31.0789 3.41058 30.9226C3.2543 30.7663 3.1665 30.5543 3.1665 30.3333V3.66667C3.1665 3.44565 3.2543 3.23369 3.41058 3.07741C3.56686 2.92113 3.77882 2.83333 3.99984 2.83333H13.9998V10.3333C13.9998 11.2174 14.351 12.0652 14.9761 12.6904C15.6013 13.3155 16.4491 13.6667 17.3332 13.6667H24.8332V30.3333ZM16.4998 4.70166L22.9632 11.1667H17.3332C17.1122 11.1667 16.9002 11.0789 16.7439 10.9226C16.5876 10.7663 16.4998 10.5543 16.4998 10.3333V4.70166Z"
|
||||
fill="black"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col flex-1 gap-2">
|
||||
<div className="text-gray-500 dark:text-gray-400 flex flex-row items-center gap-2 text-xs">
|
||||
{formatDateToIndonesian(new Date(document?.createdAt))} {document?.timezone ? document?.timezone : "WIB"} | <Icon icon="formkit:eye" width="15" height="15" /> 518
|
||||
</div>
|
||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{document?.title}</div>
|
||||
<div className="flex gap-2 items-center text-xs text-red-500 dark:text-red-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512">
|
||||
<path fill="#f00" d="M224 30v256h-64l96 128l96-128h-64V30zM32 434v48h448v-48z" />
|
||||
</svg>
|
||||
Download Dokumen
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContentManagement;
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
"use client";
|
||||
|
||||
import { close, error, loading } from "@/config/swal";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import { getContentRewrite, getInfoProfile } from "@/service/landing/landing";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import * as Yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { generateDataArticle } from "@/service/content/ai";
|
||||
import HeaderManagement from "@/components/landing-page/header-management";
|
||||
import SidebarManagement from "@/components/landing-page/sidebar-management";
|
||||
import { saveContentRewrite } from "@/service/content/content";
|
||||
import JoditEditor from "jodit-react";
|
||||
|
||||
const page = (props: any) => {
|
||||
const { states } = props;
|
||||
const [profile, setProfile] = useState();
|
||||
const MySwal = withReactContent(Swal);
|
||||
const searchParams = useSearchParams();
|
||||
const router = useRouter();
|
||||
const [, setLoadingState] = useState(false);
|
||||
const id: any = searchParams?.get("title");
|
||||
const [content, setContent] = useState<any>([]);
|
||||
const [isFromSPIT, setIsFromSPIT] = useState(false);
|
||||
const [listSuggestion, setListSuggestion] = useState();
|
||||
const [main, setMain] = useState();
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
const userRoleId = getCookiesDecrypt("urie");
|
||||
const [articleIds, setArticleIds] = useState<any>([]);
|
||||
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
|
||||
const [selectedArticleId, setSelectedArticleId] = useState(null);
|
||||
const [articleBody, setArticleBody] = useState<any>("");
|
||||
const [selectedAdvConfig, setSelectedAdvConfig] = useState("");
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
|
||||
const [selectedContextType, setSelectedContextType] = useState("");
|
||||
const [selectedLanguage, setSelectedLanguage] = useState("");
|
||||
const [selectedTitle, setSelectedTitle] = useState("");
|
||||
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
|
||||
const [selectedSEO, setSelectedSEO] = useState("");
|
||||
const [selectedSize, setSelectedSize] = useState("");
|
||||
const [detailArticle, setDetailArticle] = useState<any>(null);
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
|
||||
// useEffect(() => {
|
||||
// let userLevelId: number | undefined;
|
||||
|
||||
// if (userLevelId != undefined && userLevelId == 216) {
|
||||
// setIsMabesApprover(true);
|
||||
// }
|
||||
// }, [userLevelId]);
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
loading();
|
||||
const response = await getInfoProfile();
|
||||
|
||||
// console.log(response?.data.data);
|
||||
setProfile(response?.data?.data);
|
||||
close();
|
||||
}
|
||||
|
||||
async function getInitData() {
|
||||
const response = await getContentRewrite(id);
|
||||
const data = response?.data?.data;
|
||||
setContent(data);
|
||||
|
||||
const cleanArticleBody = data?.articleBody?.replace(/<img[^>]*>/g, "");
|
||||
setArticleBody(cleanArticleBody || "");
|
||||
}
|
||||
|
||||
initState();
|
||||
getInitData();
|
||||
}, []);
|
||||
|
||||
let componentMounted = true;
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
title: Yup.string().required("Judul tidak boleh kosong"),
|
||||
});
|
||||
|
||||
const formOptions = {
|
||||
resolver: yupResolver(validationSchema),
|
||||
};
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
reset,
|
||||
formState: { errors },
|
||||
setValue,
|
||||
} = useForm(formOptions);
|
||||
|
||||
const handleGenerateArtikel = async () => {
|
||||
loading();
|
||||
const request: any = {
|
||||
advConfig: selectedAdvConfig,
|
||||
style: selectedWritingStyle,
|
||||
website: "None",
|
||||
connectToWeb: true,
|
||||
lang: selectedLanguage,
|
||||
pointOfView: "None",
|
||||
title: content?.title,
|
||||
imageSource: "Web",
|
||||
mainKeyword: content?.title,
|
||||
additionalKeywords: content?.htmlDescription,
|
||||
targetCountry: null,
|
||||
articleSize: selectedSize,
|
||||
projectId: 2,
|
||||
createdBy: roleId,
|
||||
clientId: "ngDLPPiorplznw2jTqVe3YFCz5xqKfUJ",
|
||||
};
|
||||
|
||||
const res = await generateDataArticle(request);
|
||||
close();
|
||||
|
||||
if (res?.error) {
|
||||
console.error(res.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
const newArticleId = res?.data?.data?.id;
|
||||
setIsGeneratedArticle(true);
|
||||
|
||||
setArticleIds((prevIds: any) => {
|
||||
if (prevIds.length < 5) {
|
||||
return [...prevIds, newArticleId];
|
||||
} else {
|
||||
const updatedIds = [...prevIds];
|
||||
updatedIds[4] = newArticleId;
|
||||
return updatedIds;
|
||||
}
|
||||
});
|
||||
|
||||
// Cookies.set("nulisAIArticleIdTemp", articleIds);
|
||||
};
|
||||
const save = async (data: any) => {
|
||||
const request = {
|
||||
id: 1,
|
||||
articleId: id.split("-")?.[0],
|
||||
title: data.title,
|
||||
articleBody: detailArticle?.articleBody,
|
||||
metaTitle: detailArticle?.metaTitle,
|
||||
metaDescription: detailArticle?.metaDescription,
|
||||
mainKeyword: detailArticle?.mainKeyword,
|
||||
additionalKeyword: detailArticle?.additionalKeyword,
|
||||
articleSize: detailArticle?.articleSize,
|
||||
style: detailArticle?.style,
|
||||
website: detailArticle?.website,
|
||||
imageUrl: detailArticle?.imageUrl,
|
||||
};
|
||||
loading();
|
||||
const res = await saveContentRewrite(request);
|
||||
if (res?.error) {
|
||||
error(res?.message);
|
||||
return false;
|
||||
}
|
||||
successSubmit();
|
||||
};
|
||||
|
||||
function successSubmit() {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onSubmit(data: any) {
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderManagement />
|
||||
<div className="flex flex-row">
|
||||
<SidebarManagement />
|
||||
<div className="w-2/3 p-12">
|
||||
<div className="flex flex-col">
|
||||
<div className="text-xl font-bold mb-5">Detail Content Rewrite</div>
|
||||
<div className="p-8 border border-black rounded-lg">
|
||||
<form method="POST" onSubmit={handleSubmit(onSubmit)}>
|
||||
{/* {content && ( */}
|
||||
<>
|
||||
<div className="w-full">
|
||||
<div className="mb-3">
|
||||
<p className="font-semibold">Judul</p>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
className={`w-full p-2 border rounded-md mb-3 border-black ${errors.title ? "is-invalid" : ""}`}
|
||||
{...register("title", {
|
||||
value: content?.title,
|
||||
})}
|
||||
id="title"
|
||||
defaultValue={content?.title}
|
||||
// onChange={(e) => setSelectedTitle(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between lg:flex-row">
|
||||
<div className="w-50%">
|
||||
<div className="mb-3">
|
||||
<p className="font-semibold">Main Keyword</p>
|
||||
</div>
|
||||
<div>
|
||||
<input type="text" className="border mb-3 w-full rounded-md p-2 border-black" id="mainKeyword" name="mainKeyword" placeholder="Masukan Main Keyword disini!" defaultValue={content?.mainKeyword} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-50%">
|
||||
<div className="mb-3">
|
||||
<label htmlFor="title" className="font-semibold">
|
||||
Additional Keyword{" "}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<input
|
||||
className="border mb-3 rounded-md p-2 border-black"
|
||||
type="text"
|
||||
id="additionalKeyword"
|
||||
name="additionalKeyword"
|
||||
// onChange={(e) =>
|
||||
// setSelectedMainKeyword(e.target.value)
|
||||
// }
|
||||
placeholder="Masukan Additional Keyword disini!"
|
||||
defaultValue={content?.additionalKeyword}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col justify-between lg:flex-row">
|
||||
<div className="w-50%">
|
||||
<div className="font-semibold mb-3">
|
||||
<label htmlFor="metaTitle">Meta Title</label>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
className="border rounded-md mb-3 p-2 border-black"
|
||||
id="metaTitle"
|
||||
name="metaTitle"
|
||||
// onChange={(e) =>
|
||||
// setSelectedMainKeyword(e.target.value)
|
||||
// }
|
||||
placeholder="Masukan Meta Title disini!"
|
||||
defaultValue={content?.metaTitle}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-50%">
|
||||
<div className="font-semibold mb-3">
|
||||
<label htmlFor="metaDescription">Meta Description</label>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
className="border rounded-md mb-3 p-2 border-black"
|
||||
id="metaDescription"
|
||||
name="metaDescription"
|
||||
// onChange={(e) =>
|
||||
// setSelectedMainKeyword(e.target.value)
|
||||
// }
|
||||
placeholder="Masukan Meta Description disini!"
|
||||
defaultValue={content?.metaDescription}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="font-semibold mb-3">
|
||||
<label htmlFor="description">Deskripsi Artikel</label>
|
||||
</div>
|
||||
{/* <JoditEditor value={content?.metaDescription} key={articleBody.id} onChange={(event: any) => setArticleBody(event.editor?.getData())} /> */}
|
||||
{/* <JoditEditor ref={editor} value={field.value} className="dark:text-black" onChange={field.onChange} /> */}
|
||||
{articleBody === null || articleBody === "" ? <div className="w-full px-0 text-sm">Deskripsi tidak boleh kosong</div> : ""}
|
||||
</div>
|
||||
</>
|
||||
{/* )} */}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default page;
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
"use client";
|
||||
|
||||
import HeaderManagement from "@/components/landing-page/header-management";
|
||||
import SidebarManagement from "@/components/landing-page/sidebar-management";
|
||||
import { close, error, loading, successCallback } from "@/config/swal";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import { checkWishlistStatus, deleteWishlist, getContentRewritePagination, getInfoProfile, saveWishlist } from "@/service/landing/landing";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Link } from "@/i18n/routing";
|
||||
|
||||
const page = (props: any) => {
|
||||
const [, setProfile] = useState();
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const MySwal = withReactContent(Swal);
|
||||
const page: any = searchParams?.get("page");
|
||||
const title = searchParams?.get("title");
|
||||
const category = searchParams?.get("category");
|
||||
const pages = page ? page - 1 : 0;
|
||||
|
||||
const { isInstitute, instituteId } = props;
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
const userRoleId = getCookiesDecrypt("urie");
|
||||
|
||||
const [, setGetTotalPage] = useState();
|
||||
const [totalContent, setTotalContent] = useState<any>();
|
||||
const [contentImage, setContentImage] = useState([]);
|
||||
|
||||
const [categoryFilter] = useState([]);
|
||||
const [formatFilter] = useState([]);
|
||||
const [sortBy] = useState();
|
||||
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [, setCopySuccess] = useState("");
|
||||
|
||||
const [, setWishlistId] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
loading();
|
||||
const response = await getInfoProfile();
|
||||
console.log(response?.data.data);
|
||||
setProfile(response?.data?.data);
|
||||
close();
|
||||
}
|
||||
|
||||
initState();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getData();
|
||||
}, [page, category, title, refresh]);
|
||||
|
||||
async function getData() {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : category || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
|
||||
const response = await getContentRewritePagination(pages);
|
||||
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentImage(response?.data?.data?.content);
|
||||
// console.log("response", response);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
console.log("KONTEN", response?.data?.data?.content);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
getData();
|
||||
}
|
||||
|
||||
initState();
|
||||
}, [page, refresh]);
|
||||
|
||||
function addDefaultSrc(ev: any) {
|
||||
ev.target.src = "/assets/img/image404.png";
|
||||
}
|
||||
|
||||
async function deleteProcess(id: any) {
|
||||
loading();
|
||||
const resDelete = await deleteWishlist(id);
|
||||
|
||||
if (resDelete?.error) {
|
||||
error(resDelete.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
setRefresh((prevRefresh) => !prevRefresh);
|
||||
close();
|
||||
}
|
||||
|
||||
const handleDelete = (id: any) => {
|
||||
MySwal.fire({
|
||||
title: "Hapus Data",
|
||||
text: "",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#3085d6",
|
||||
confirmButtonColor: "#d33",
|
||||
confirmButtonText: "Hapus",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
deleteProcess(id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const copyToClip = async (url: any) => {
|
||||
await navigator.clipboard.writeText(`https://mediahub.polri.go.id/image/detail/${url}`);
|
||||
setCopySuccess("Copied");
|
||||
// toast.success("Link Berhasil Di Copy");
|
||||
};
|
||||
|
||||
async function checkWishlist(uploadId: any) {
|
||||
if (userId) {
|
||||
const res = await checkWishlistStatus(uploadId);
|
||||
console.log(res?.data?.data);
|
||||
// const isAlreadyOnWishlist = res?.data?.data == "-1" ? false : true;
|
||||
// if (isAlreadyOnWishlist == true) {
|
||||
// warning("Konten sudah Ada", `#`);
|
||||
// }
|
||||
setWishlistId(res?.data?.data); // setIsSaved(isAlreadyOnWishlist);
|
||||
// console.log("isSave", isAlreadyOnWishlist);
|
||||
}
|
||||
}
|
||||
|
||||
const handleSaveWishlist = async (uploadId: any) => {
|
||||
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||
router.push("/auth/login");
|
||||
} else {
|
||||
console.log("data", uploadId);
|
||||
const data = {
|
||||
mediaUploadId: uploadId,
|
||||
};
|
||||
|
||||
loading();
|
||||
checkWishlist(uploadId);
|
||||
|
||||
const res = await saveWishlist(data);
|
||||
if (res?.error) {
|
||||
error(res.message);
|
||||
console.log("simpan data", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
successCallback("Konten Berhasil Disimpan");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderManagement />
|
||||
<div className="flex flex-row">
|
||||
<SidebarManagement />
|
||||
<div className="w-2/3 p-12">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold mb-4">Galeri Content Rewrite</h1>
|
||||
</div>
|
||||
<div className="px-0 lg:px-10">
|
||||
{contentImage?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{contentImage?.map((image: any) => (
|
||||
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
|
||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||
<Link href={`/content-management/rewrite/detail/${image.id}`}>
|
||||
<img src={image?.thumbnailUrl} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.title}</div>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-52 w-52 my-4" />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default page;
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
"use client";
|
||||
|
||||
import HeaderManagement from "@/components/landing-page/header-management";
|
||||
import SidebarManagement from "@/components/landing-page/sidebar-management";
|
||||
import { close, error, loading } from "@/config/swal";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import { getInfoProfile, getUsersTeams } from "@/service/landing/landing";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
import { saveUserReports } from "@/service/content/content";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
const page = () => {
|
||||
const [user, setUser] = useState<any>();
|
||||
const [profile, setProfile] = useState<any>();
|
||||
const instituteId = getCookiesDecrypt("uinse");
|
||||
const [userSelected, setUserSelected] = useState();
|
||||
const [reportMessage, setReportMessage] = useState<string>();
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
||||
// const launchModal = (user: any) => {
|
||||
// setUserSelected(user);
|
||||
// $("#modalDetailProfile").modal("show");
|
||||
// };
|
||||
async function onSubmit() {
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function save() {
|
||||
loading();
|
||||
const data = {
|
||||
userId: user?.id,
|
||||
message: reportMessage,
|
||||
};
|
||||
|
||||
const response = await saveUserReports(data);
|
||||
|
||||
if (response?.error) {
|
||||
error(response?.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
close();
|
||||
|
||||
successSubmit();
|
||||
}
|
||||
|
||||
function successSubmit() {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
("hide");
|
||||
// $("#modalReportProfile").modal("hide");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
async function getTeams() {
|
||||
if (instituteId != undefined) {
|
||||
loading();
|
||||
const response = await getUsersTeams(instituteId);
|
||||
setUser(response?.data?.data);
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
async function getProfile() {
|
||||
loading();
|
||||
const response = await getInfoProfile();
|
||||
setProfile(response?.data?.data);
|
||||
close();
|
||||
}
|
||||
|
||||
getTeams();
|
||||
getProfile();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderManagement />
|
||||
<div className="flex flex-row">
|
||||
<SidebarManagement />
|
||||
<div className="w-2/3 p-12">
|
||||
<div className="flex flex-col">
|
||||
<p className="text-lg font-semibold">Tim {profile?.institute?.name}</p>
|
||||
<p className="text-base mb-3">{user?.length} Anggota</p>
|
||||
</div>
|
||||
<div className="flex flex-row gap-5">
|
||||
{user?.map((row: any) => (
|
||||
<div key={row?.id}>
|
||||
<Dialog>
|
||||
<DialogTrigger>
|
||||
<div className="flex flex-col items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="5em" height="5em" viewBox="0 0 16 16">
|
||||
<path fill="currentColor" d="M11 7c0 1.66-1.34 3-3 3S5 8.66 5 7s1.34-3 3-3s3 1.34 3 3" />
|
||||
<path
|
||||
fill="black"
|
||||
fill-rule="evenodd"
|
||||
d="M16 8c0 4.42-3.58 8-8 8s-8-3.58-8-8s3.58-8 8-8s8 3.58 8 8M4 13.75C4.16 13.484 5.71 11 7.99 11c2.27 0 3.83 2.49 3.99 2.75A6.98 6.98 0 0 0 14.99 8c0-3.87-3.13-7-7-7s-7 3.13-7 7c0 2.38 1.19 4.49 3.01 5.75"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<p className="font-semibold text-md">{row?.fullname}</p>
|
||||
<p className="text-sm font-light">{row?.username || "username"}</p>
|
||||
</div>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="flex flex-row justify-center" size="sm">
|
||||
<div className="flex flex-col w-full gap-2">
|
||||
<div>
|
||||
<h1 className="font-semibold">{row?.fullname}</h1>
|
||||
</div>
|
||||
<div className="border-b border-black w-full"></div>
|
||||
<div className="gap-1">
|
||||
<div className="font-light">{row?.email}</div>
|
||||
<div className="font-light">{row?.phoneNumber}</div>
|
||||
<div className="font-light">{row?.address}</div>
|
||||
</div>
|
||||
<div className="border-b border-black w-full"></div>
|
||||
<div className="place-items-end">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline" className="bg-red-500 rounded-md">
|
||||
Report
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<div className="flex flex-col w-full gap-2">
|
||||
<div>
|
||||
<h1 className="font-semibold">{row?.fullname}</h1>
|
||||
</div>
|
||||
<div className="border-b border-black w-full"></div>
|
||||
<div className="flex flex-col">
|
||||
<h1 className="text-red-600 mb-2">Alasan Report Akun</h1>
|
||||
<textarea id="formControlTextarea1" rows={4} className="border border-black font-light" onChange={(e) => setReportMessage(e.target.value)} />
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
{/* <Button className="bg-purple-500 text-white" type="submit">
|
||||
Batal
|
||||
</Button> */}
|
||||
<Button className="bg-red-500 text-white" type="submit" onClick={() => onSubmit()}>
|
||||
Kirim
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default page;
|
||||
|
|
@ -154,15 +154,15 @@ const FilterPage = () => {
|
|||
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;
|
||||
// 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);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
} else {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
|
||||
|
|
@ -186,15 +186,15 @@ const FilterPage = () => {
|
|||
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;
|
||||
// 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);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -254,9 +254,9 @@ const FilterPage = () => {
|
|||
);
|
||||
close();
|
||||
|
||||
setGetTotalPage(response.data?.data?.totalPages);
|
||||
setContentDocument(response.data?.data?.content);
|
||||
setTotalContent(response.data?.data?.totalElements);
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentDocument(response?.data?.data?.content);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
|
||||
const table = useReactTable({
|
||||
|
|
@ -287,7 +287,7 @@ const FilterPage = () => {
|
|||
const response = await getListContent({ page: page - 1, size: 6, sortBy: "createdAt", contentTypeId: "3" });
|
||||
console.log(response);
|
||||
setDocumentData(response?.data?.data?.content);
|
||||
const data = response.data?.data;
|
||||
const data = response?.data?.data;
|
||||
const contentData = data?.content;
|
||||
setDocumentData(contentData);
|
||||
setTotalData(data?.totalElements);
|
||||
|
|
|
|||
|
|
@ -155,15 +155,15 @@ const FilterPage = () => {
|
|||
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;
|
||||
// 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);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
} else {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
|
||||
|
|
@ -187,15 +187,15 @@ const FilterPage = () => {
|
|||
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;
|
||||
// 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);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -255,9 +255,9 @@ const FilterPage = () => {
|
|||
);
|
||||
close();
|
||||
|
||||
setGetTotalPage(response.data?.data?.totalPages);
|
||||
setContentImage(response.data?.data?.content);
|
||||
setTotalContent(response.data?.data?.totalElements);
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentImage(response?.data?.data?.content);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
|
||||
const table = useReactTable({
|
||||
|
|
|
|||
|
|
@ -1,11 +1,3 @@
|
|||
import LayoutProvider from "@/providers/layout.provider";
|
||||
import LayoutContentProvider from "@/providers/content.provider";
|
||||
import DashCodeSidebar from "@/components/partials/sidebar";
|
||||
import DashCodeFooter from "@/components/partials/footer";
|
||||
import ThemeCustomize from "@/components/partials/customizer";
|
||||
import DashCodeHeader from "@/components/partials/header";
|
||||
|
||||
import { redirect } from "@/components/navigation";
|
||||
import Footer from "@/components/landing-page/footer";
|
||||
import Navbar from "@/components/landing-page/navbar";
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ const ChangePassword: React.FC = () => {
|
|||
useEffect(() => {
|
||||
async function initState() {
|
||||
const response = await getInfoProfile();
|
||||
const details = response.data?.data;
|
||||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
console.log("data", details);
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ const UbahProfile: React.FC = () => {
|
|||
useEffect(() => {
|
||||
async function initState() {
|
||||
const response = await getInfoProfile();
|
||||
const details = response.data?.data;
|
||||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
console.log("data", details);
|
||||
|
|
|
|||
|
|
@ -132,8 +132,8 @@ const Schedule = (props: any) => {
|
|||
useEffect(() => {
|
||||
async function getDataSchedule() {
|
||||
const response = await detailSchedule(id);
|
||||
setDetail(response.data?.data);
|
||||
setContent(response.data?.data?.files);
|
||||
setDetail(response?.data?.data);
|
||||
setContent(response?.data?.data?.files);
|
||||
}
|
||||
|
||||
getDataSchedule();
|
||||
|
|
@ -272,8 +272,8 @@ const Schedule = (props: any) => {
|
|||
const getItem = async (itemFound: any) => {
|
||||
loading();
|
||||
const response = await detailSchedule(itemFound?.id);
|
||||
setDetail(response.data?.data);
|
||||
setContent(response.data?.data?.files);
|
||||
setDetail(response?.data?.data);
|
||||
setContent(response?.data?.data?.files);
|
||||
console.log("item Found", itemFound);
|
||||
close();
|
||||
setOpenDialog(true);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,21 @@
|
|||
"use client";
|
||||
|
||||
import { useParams, usePathname, useRouter } from "next/navigation";
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import { getDetail } from "@/service/landing/landing";
|
||||
import {
|
||||
checkWishlistStatus,
|
||||
deleteWishlist,
|
||||
getDetail,
|
||||
saveWishlist,
|
||||
} from "@/service/landing/landing";
|
||||
import VideoPlayer from "@/utils/video-player";
|
||||
import NewContent from "@/components/landing-page/new-content";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import { close, error, loading } from "@/config/swal";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
|
||||
const DetailVideo = () => {
|
||||
const [selectedSize, setSelectedSize] = useState<string>("L");
|
||||
|
|
@ -15,19 +23,99 @@ const DetailVideo = () => {
|
|||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const params = useParams();
|
||||
const slug = params?.slug;
|
||||
const slug = String(params?.slug);
|
||||
const [detailDataVideo, setDetailDataVideo] = useState<any>();
|
||||
const [isSaved, setIsSaved] = useState(false);
|
||||
const [wishlistId, setWishlistId] = useState();
|
||||
const { toast } = useToast();
|
||||
const [isDownloadAll, setIsDownloadAll] = useState(false);
|
||||
const [downloadProgress, setDownloadProgress] = useState(0);
|
||||
const [isFromSPIT, setIsFromSPIT] = useState(false);
|
||||
const [main, setMain] = useState<any>();
|
||||
const [resolutionSelected, setResolutionSelected] = useState("720");
|
||||
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
|
||||
useEffect(() => {
|
||||
initFetch();
|
||||
checkWishlist();
|
||||
}, []);
|
||||
|
||||
const initFetch = async () => {
|
||||
const response = await getDetail(String(slug));
|
||||
console.log("detailVideo", response);
|
||||
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
||||
setMain({
|
||||
id: response?.data?.data?.files[0]?.id,
|
||||
type: response?.data?.data?.fileType.name,
|
||||
url:
|
||||
Number(response?.data?.data?.fileType?.id) == 4
|
||||
? response?.data?.data?.files[0]?.secondaryUrl
|
||||
: Number(response?.data?.data?.fileType?.id) == 2
|
||||
? `${process.env.NEXT_PUBLIC_API}/media/view?id=${response?.data?.data?.files[0]?.id}&operation=file&type=video`
|
||||
: response?.data?.data?.files[0]?.url,
|
||||
thumbnailFileUrl: response?.data?.data?.files[0]?.thumbnailFileUrl,
|
||||
names: response?.data?.data?.files[0]?.fileName,
|
||||
format: response?.data?.data?.files[0]?.format,
|
||||
widthPixel: response?.data?.data?.files[0]?.widthPixel,
|
||||
heightPixel: response?.data?.data?.files[0]?.heightPixel,
|
||||
size: response?.data?.data?.files[0]?.size,
|
||||
caption: response?.data?.data?.files[0]?.caption,
|
||||
});
|
||||
setDetailDataVideo(response?.data?.data);
|
||||
};
|
||||
|
||||
const doBookmark = async () => {
|
||||
if (userId) {
|
||||
const data = {
|
||||
mediaUploadId: slug?.split("-")?.[0],
|
||||
};
|
||||
|
||||
loading();
|
||||
const res = await saveWishlist(data);
|
||||
if (res?.error) {
|
||||
error(res.message);
|
||||
return false;
|
||||
}
|
||||
close();
|
||||
toast({
|
||||
title: "Konten berhasil disimpan",
|
||||
});
|
||||
checkWishlist();
|
||||
} else {
|
||||
router.push("/auth");
|
||||
}
|
||||
};
|
||||
async function checkWishlist() {
|
||||
if (userId) {
|
||||
const res = await checkWishlistStatus(slug.split("-")?.[0]);
|
||||
console.log(res?.data?.data);
|
||||
const isAlreadyOnWishlist = res?.data?.data !== "-1";
|
||||
setWishlistId(res?.data?.data);
|
||||
setIsSaved(isAlreadyOnWishlist);
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteWishlist = async () => {
|
||||
if (userId) {
|
||||
loading();
|
||||
const res = await deleteWishlist(wishlistId);
|
||||
|
||||
if (res?.error) {
|
||||
error(res.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
toast({
|
||||
title: "Konten berhasil dihapus",
|
||||
});
|
||||
close();
|
||||
checkWishlist();
|
||||
} else {
|
||||
router.push("/auth");
|
||||
}
|
||||
};
|
||||
|
||||
const sizes = [
|
||||
{ label: "XL", value: "3198 x 1798 px" },
|
||||
{ label: "L", value: "2399 x 1349 px" },
|
||||
|
|
@ -36,6 +124,104 @@ const DetailVideo = () => {
|
|||
{ label: "XS", value: "800 x 450 px" },
|
||||
];
|
||||
|
||||
async function sendActivityLog(activityTypeId: number) {
|
||||
const data = {
|
||||
activityTypeId,
|
||||
mediaId: slug.split("-")?.[0],
|
||||
url: window.location.href,
|
||||
};
|
||||
// set activity
|
||||
// const response = await postActivityLog(data, token);
|
||||
// console.log(response);
|
||||
}
|
||||
|
||||
const handleDownload = () => {
|
||||
if (downloadProgress === 0) {
|
||||
if (!userId) {
|
||||
router.push("/auth/login");
|
||||
} else {
|
||||
sendActivityLog(2);
|
||||
sendActivityLog(3);
|
||||
|
||||
if (isDownloadAll) {
|
||||
let url: string;
|
||||
const baseId = slug.split("-")?.[0];
|
||||
|
||||
// if (type === "1") {
|
||||
url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}&resolution=${resolutionSelected}`;
|
||||
// } else if (type === "2") {
|
||||
// url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}&resolution=${imageSizeSelected}`;
|
||||
// } else {
|
||||
// url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}`;
|
||||
// }
|
||||
|
||||
downloadFile(url, "FileDownload.zip");
|
||||
} else {
|
||||
if (isFromSPIT && main?.url?.includes("spit.humas")) {
|
||||
downloadFile(`${main?.url}`, `${main.names}`);
|
||||
} else {
|
||||
const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=video&resolution=${resolutionSelected}p`;
|
||||
downloadFile(url, `${main.names}`);
|
||||
}
|
||||
}
|
||||
// } else if (type === "1" && resolutionSelected?.length > 0) {
|
||||
// if (isFromSPIT && main?.url?.includes("spit.humas")) {
|
||||
// downloadFile(`${main?.url}`, `${main.names}`);
|
||||
// } else {
|
||||
// const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=video&resolution=${resolutionSelected}p`;
|
||||
// downloadFile(url, `${main.names}`);
|
||||
// }
|
||||
// } else if (type === "2" && imageSizeSelected?.length > 0) {
|
||||
// const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=image&resolution=${imageSizeSelected}`;
|
||||
// downloadFile(url, `${main.names}`);
|
||||
// } else if (type === "3" || type === "4") {
|
||||
// downloadFile(`${main?.url}`, `${main.names}`);
|
||||
// }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const downloadFile = (fileUrl: string, name: string) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open("GET", fileUrl, true);
|
||||
xhr.responseType = "blob";
|
||||
|
||||
xhr.addEventListener("progress", (event) => {
|
||||
if (event.lengthComputable) {
|
||||
const percentCompleted = Math.round((event.loaded / event.total) * 100);
|
||||
setDownloadProgress(percentCompleted);
|
||||
}
|
||||
});
|
||||
|
||||
xhr.addEventListener("readystatechange", () => {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
const contentType =
|
||||
xhr.getResponseHeader("content-type") || "application/octet-stream";
|
||||
const extension = contentType.split("/")[1];
|
||||
const filename = `${name}.${extension}`;
|
||||
|
||||
const blob = new Blob([xhr.response], {
|
||||
type: contentType,
|
||||
});
|
||||
const downloadUrl = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
|
||||
a.href = downloadUrl;
|
||||
a.download = filename;
|
||||
document.body.append(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
}
|
||||
});
|
||||
|
||||
xhr.onloadend = () => {
|
||||
setDownloadProgress(0);
|
||||
};
|
||||
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="px-4 md:px-24 py-4">
|
||||
|
|
@ -51,7 +237,12 @@ const DetailVideo = () => {
|
|||
{/* Footer Informasi */}
|
||||
<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 <span className="font-semibold text-black">{detailDataVideo?.uploadedBy?.userLevel?.name}</span> | Diupdate pada {detailDataVideo?.updatedAt} WIB |
|
||||
oleh
|
||||
<span className="font-semibold text-black">
|
||||
{detailDataVideo?.uploadedBy?.userLevel?.name}
|
||||
</span>
|
||||
| Diupdate pada {detailDataVideo?.updatedAt}{" "}
|
||||
WIB |
|
||||
<Icon icon="formkit:eye" width="15" height="15" />
|
||||
|
||||
{detailDataVideo?.clickCount}
|
||||
|
|
@ -61,42 +252,83 @@ const DetailVideo = () => {
|
|||
|
||||
{/* Keterangan */}
|
||||
<div className="w-full">
|
||||
<h1 className="flex flex-row font-bold text-2xl my-8">{detailDataVideo?.title}</h1>
|
||||
<div className="font-light text-justify" 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>
|
||||
|
||||
{/* Bagian Kanan */}
|
||||
<div className="md:w-1/4 p-4 bg-[#f7f7f7] rounded-lg mx-4 h-fit">
|
||||
<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" />
|
||||
</svg>
|
||||
<p className="text-base lg:text-lg">Simpan</p>
|
||||
</div>
|
||||
{isSaved ? (
|
||||
<a
|
||||
onClick={() => handleDeleteWishlist()}
|
||||
className="flex flex-col mb-3 items-center justify-center cursor-pointer"
|
||||
>
|
||||
<Icon icon="material-symbols:bookmark" width={40} />
|
||||
<p className="text-base lg:text-lg">Hapus</p>
|
||||
</a>
|
||||
) : (
|
||||
<a
|
||||
onClick={() => doBookmark()}
|
||||
className="flex flex-col mb-3 items-center justify-center cursor-pointer"
|
||||
>
|
||||
<Icon icon="material-symbols:bookmark-outline" width={40} />
|
||||
<p className="text-base lg:text-lg">Simpan</p>
|
||||
</a>
|
||||
)}
|
||||
|
||||
{/* garis */}
|
||||
<div className="border-t border-black my-4"></div>
|
||||
|
||||
<Link href="" className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 flex justify-center items-center rounded">
|
||||
{detailDataVideo?.category?.name}
|
||||
<Link
|
||||
href={`/all/filter?title=polda&category=${detailDataVideo?.category.id}`}
|
||||
className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 flex justify-center items-center rounded"
|
||||
>
|
||||
{detailDataVideo?.categoryName}
|
||||
</Link>
|
||||
|
||||
<div className="flex justify-center flex-wrap gap-2 mb-4">
|
||||
<p className="bg-gray-200 text-gray-700 text-xs px-3 py-1 rounded-full cursor-pointer hover:bg-gray-500">poldajabar</p>
|
||||
<p className="bg-gray-200 text-gray-700 text-xs px-3 py-1 rounded-full cursor-pointer hover:bg-gray-500">pilkadamai2024</p>
|
||||
{detailDataVideo?.tags.split(",").map((tag: string) => (
|
||||
<a
|
||||
onClick={() => router.push(`/all/filter?tag=${tag}`)}
|
||||
key={tag}
|
||||
className="bg-gray-200 text-gray-700 text-xs px-3 py-1 rounded-full cursor-pointer hover:bg-gray-500 hover:text-white"
|
||||
>
|
||||
{tag}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="border-t border-black my-4"></div>
|
||||
|
||||
{/* Opsi Ukuran Foto */}
|
||||
<h4 className="flex text-lg justify-center items-center font-semibold my-3">Opsi Ukuran Foto</h4>
|
||||
<h4 className="flex text-lg justify-center items-center font-semibold my-3">
|
||||
Opsi Ukuran Foto
|
||||
</h4>
|
||||
|
||||
<div className="border-t border-black my-4"></div>
|
||||
|
||||
<div className="space-y-2">
|
||||
{sizes.map((size) => (
|
||||
<label key={size.label} className="flex items-center space-x-2 cursor-pointer">
|
||||
<input type="radio" name="size" value={size.label} checked={selectedSize === size.label} onChange={() => setSelectedSize(size.label)} className="text-red-600 focus:ring-red-600" />
|
||||
<label
|
||||
key={size.label}
|
||||
className="flex items-center space-x-2 cursor-pointer"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name="size"
|
||||
value={size.label}
|
||||
checked={selectedSize === size.label}
|
||||
onChange={() => setSelectedSize(size.label)}
|
||||
className="text-red-600 focus:ring-red-600"
|
||||
/>
|
||||
<div className="text-sm">
|
||||
{size.label} ----------------- {size.value}
|
||||
</div>
|
||||
|
|
@ -107,15 +339,30 @@ const DetailVideo = () => {
|
|||
{/* Download Semua */}
|
||||
<div className="mt-4">
|
||||
<label className="flex items-center space-x-2 text-sm">
|
||||
<input type="checkbox" className="text-red-600 focus:ring-red-600" />
|
||||
<input
|
||||
type="checkbox"
|
||||
className="text-red-600 focus:ring-red-600"
|
||||
onChange={() => setIsDownloadAll(!isDownloadAll)}
|
||||
/>
|
||||
<span>Download Semua File?</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Tombol Download */}
|
||||
<button className="mt-4 bg-red-600 text-white w-full py-2 flex justify-center items-center gap-1 rounded-md text-sm hover:bg-red-700">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
||||
<path fill="white" d="m12 16l-5-5l1.4-1.45l2.6 2.6V4h2v8.15l2.6-2.6L17 11zm-6 4q-.825 0-1.412-.587T4 18v-3h2v3h12v-3h2v3q0 .825-.587 1.413T18 20z" />
|
||||
<button
|
||||
onClick={handleDownload}
|
||||
className="mt-4 bg-red-600 text-white w-full py-2 flex justify-center items-center gap-1 rounded-md text-sm hover:bg-red-700"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="white"
|
||||
d="m12 16l-5-5l1.4-1.45l2.6 2.6V4h2v8.15l2.6-2.6L17 11zm-6 4q-.825 0-1.412-.587T4 18v-3h2v3h12v-3h2v3q0 .825-.587 1.413T18 20z"
|
||||
/>
|
||||
</svg>
|
||||
Download
|
||||
</button>
|
||||
|
|
@ -127,8 +374,13 @@ const DetailVideo = () => {
|
|||
<div className="flex flex-col my-16 p-10 bg-[#f7f7f7]">
|
||||
<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] text-white rounded-lg w-fit px-4 py-1">Kirim</button>
|
||||
<Textarea
|
||||
placeholder="Type your comments here."
|
||||
className="flex w-full"
|
||||
/>
|
||||
<button className="flex items-start bg-[#bb3523] text-white rounded-lg w-fit px-4 py-1">
|
||||
Kirim
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,31 @@ 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, getOnlyDate, getOnlyMonthAndYear } from "@/utils/globals";
|
||||
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 {
|
||||
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, useRouter } from "@/i18n/routing";
|
||||
|
|
@ -33,8 +54,11 @@ const FilterPage = () => {
|
|||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
const [totalPage, setTotalPage] = React.useState<number>(1);
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
|
||||
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
|
||||
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,
|
||||
|
|
@ -48,13 +72,13 @@ const FilterPage = () => {
|
|||
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 [sortByOpt, setSortByOpt] = useState<any>(
|
||||
sortBy === "popular" ? "clickCount" : "createdAt"
|
||||
);
|
||||
const isRegional = asPath?.includes("regional");
|
||||
const isSatker = asPath?.includes("satker");
|
||||
const [formatFilter, setFormatFilter] = useState<any>([]);
|
||||
|
|
@ -90,8 +114,14 @@ const FilterPage = () => {
|
|||
|
||||
useEffect(() => {
|
||||
if (categorie) {
|
||||
setCategoryFilter(categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
|
||||
console.log("Kategori", categorie, categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
|
||||
setCategoryFilter(
|
||||
categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]
|
||||
);
|
||||
console.log(
|
||||
"Kategori",
|
||||
categorie,
|
||||
categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]
|
||||
);
|
||||
}
|
||||
}, [categorie]);
|
||||
|
||||
|
|
@ -109,7 +139,19 @@ const FilterPage = () => {
|
|||
}
|
||||
console.log(monthYearFilter, "monthFilter");
|
||||
initState();
|
||||
}, [change, asPath, monthYearFilter, page, sortBy, sortByOpt, title, startDateString, endDateString, categorie, formatFilter]);
|
||||
}, [
|
||||
change,
|
||||
asPath,
|
||||
monthYearFilter,
|
||||
page,
|
||||
sortBy,
|
||||
sortByOpt,
|
||||
title,
|
||||
startDateString,
|
||||
endDateString,
|
||||
categorie,
|
||||
formatFilter,
|
||||
]);
|
||||
|
||||
async function getCategories() {
|
||||
const category = await listCategory("2");
|
||||
|
|
@ -132,7 +174,10 @@ const FilterPage = () => {
|
|||
async function getDataAll() {
|
||||
if (asPath?.includes("/polda/") == true) {
|
||||
if (asPath?.split("/")[2] !== "[polda_name]") {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
|
||||
const filter =
|
||||
categoryFilter?.length > 0
|
||||
? categoryFilter?.sort().join(",")
|
||||
: categorie || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
|
|
@ -150,22 +195,31 @@ const FilterPage = () => {
|
|||
filterGroup,
|
||||
startDateString,
|
||||
endDateString,
|
||||
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
|
||||
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
|
||||
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;
|
||||
// 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);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
} else {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
|
||||
const filter =
|
||||
categoryFilter?.length > 0
|
||||
? categoryFilter?.sort().join(",")
|
||||
: categorie || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
|
|
@ -182,19 +236,23 @@ const FilterPage = () => {
|
|||
"",
|
||||
startDateString,
|
||||
endDateString,
|
||||
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
|
||||
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
|
||||
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;
|
||||
// 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);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -233,7 +291,10 @@ const FilterPage = () => {
|
|||
};
|
||||
|
||||
async function getDataRegional() {
|
||||
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
|
||||
const filter =
|
||||
categoryFilter?.length > 0
|
||||
? categoryFilter?.sort().join(",")
|
||||
: categorie || "";
|
||||
|
||||
const name = title == undefined ? "" : title;
|
||||
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
|
||||
|
|
@ -246,17 +307,21 @@ const FilterPage = () => {
|
|||
"",
|
||||
startDateString,
|
||||
endDateString,
|
||||
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
|
||||
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
|
||||
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);
|
||||
setGetTotalPage(response?.data?.data?.totalPages);
|
||||
setContentVideo(response?.data?.data?.content);
|
||||
setTotalContent(response?.data?.data?.totalElements);
|
||||
}
|
||||
|
||||
const table = useReactTable({
|
||||
|
|
@ -284,10 +349,15 @@ const FilterPage = () => {
|
|||
initFetch();
|
||||
}, [page]);
|
||||
const initFetch = async () => {
|
||||
const response = await getListContent({ page: page - 1, size: 6, sortBy: "createdAt", contentTypeId: "2" });
|
||||
const response = await getListContent({
|
||||
page: page - 1,
|
||||
size: 6,
|
||||
sortBy: "createdAt",
|
||||
contentTypeId: "2",
|
||||
});
|
||||
console.log(response);
|
||||
setVideoData(response?.data?.data?.content);
|
||||
const data = response.data?.data;
|
||||
const data = response?.data?.data;
|
||||
const contentData = data?.content;
|
||||
setVideoData(contentData);
|
||||
setTotalData(data?.totalElements);
|
||||
|
|
@ -347,6 +417,8 @@ const FilterPage = () => {
|
|||
}
|
||||
}
|
||||
|
||||
let typingTimer: any;
|
||||
const doneTypingInterval = 1500;
|
||||
const handleKeyUp = () => {
|
||||
clearTimeout(typingTimer);
|
||||
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||
|
|
@ -371,7 +443,8 @@ const FilterPage = () => {
|
|||
<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>
|
||||
Audio Visual {">"}{" "}
|
||||
<span className="font-bold">Semua Audio Visual</span>
|
||||
</p>
|
||||
<p className="font-bold">|</p>
|
||||
<p>{`Terdapat ${totalContent} artikel berisi Audio Visual yang dapat diunduh`}</p>
|
||||
|
|
@ -387,7 +460,10 @@ const FilterPage = () => {
|
|||
<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">
|
||||
<label
|
||||
htmlFor="search"
|
||||
className="block text-sm font-medium text-gray-700 dark:text-white"
|
||||
>
|
||||
Pencarian
|
||||
</label>
|
||||
<Input
|
||||
|
|
@ -403,7 +479,9 @@ const FilterPage = () => {
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-white">Tahun & Bulan</label>
|
||||
<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"
|
||||
|
|
@ -415,7 +493,9 @@ const FilterPage = () => {
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-white">Tanggal</label>
|
||||
<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
|
||||
|
|
@ -428,18 +508,44 @@ const FilterPage = () => {
|
|||
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 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>
|
||||
<h3 className="text-sm font-medium text-gray-700 dark:text-white">
|
||||
Kategori
|
||||
</h3>
|
||||
<ul className="mt-2 space-y-2">
|
||||
{categories.map((category: any) => (
|
||||
<li key={category?.id}>
|
||||
<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
|
||||
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>
|
||||
))}
|
||||
|
|
@ -449,43 +555,93 @@ const FilterPage = () => {
|
|||
<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 Foto
|
||||
</h3>
|
||||
<ul className="mt-2 space-y-2">
|
||||
<li>
|
||||
<label className="inline-flex items-center">
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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 onClick={cleanCheckbox} className="text-[#bb3523] cursor-pointer">
|
||||
<a
|
||||
onClick={cleanCheckbox}
|
||||
className="text-[#bb3523] cursor-pointer"
|
||||
>
|
||||
<b>Reset Filter</b>
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -497,7 +653,11 @@ 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 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">
|
||||
<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="terpopuler">Terpopuler</option>
|
||||
</select>
|
||||
|
|
@ -506,21 +666,36 @@ const FilterPage = () => {
|
|||
{videoData?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{videoData?.map((video: any) => (
|
||||
<Card key={video?.id} className="hover:scale-110 transition-transform duration-300">
|
||||
<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 justify-center cursor-pointer rounded-lg place-self-center" />
|
||||
<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" />
|
||||
{formatDateToIndonesian(new Date(video?.createdAt))}{" "}
|
||||
{video?.timezone ? video?.timezone : "WIB"}|{" "}
|
||||
<Icon icon="formkit:eye" width="15" height="15" />
|
||||
{video?.clickCount}{" "}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 20 20">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill="#f00"
|
||||
d="M7.707 10.293a1 1 0 1 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414L11 11.586V6h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5v5.586zM9 4a1 1 0 0 1 2 0v2H9z"
|
||||
/>
|
||||
</svg>{" "}
|
||||
</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>
|
||||
<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>
|
||||
|
|
@ -528,11 +703,19 @@ const FilterPage = () => {
|
|||
</div>
|
||||
) : (
|
||||
<p className="flex items-center justify-center">
|
||||
<img src="/assets/empty-data.png" alt="empty" className="h-60 w-60 my-4" />
|
||||
<img
|
||||
src="/assets/empty-data.png"
|
||||
alt="empty"
|
||||
className="h-60 w-60 my-4"
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<LandingPagination table={table} totalData={totalData} totalPage={totalPage} />
|
||||
<LandingPagination
|
||||
table={table}
|
||||
totalData={totalData}
|
||||
totalPage={totalPage}
|
||||
/>
|
||||
</div>
|
||||
</Reveal>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -574,3 +574,7 @@ html[dir="rtl"] .react-select .select__loading-indicator {
|
|||
.custom-scrollbar-table::-webkit-scrollbar-thumb:hover {
|
||||
background: #9ca3af;
|
||||
}
|
||||
|
||||
.ck-editor__editable_inline {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
|
@ -14,9 +14,7 @@ import MountedProvider from "@/providers/mounted.provider";
|
|||
|
||||
const Home = ({ params: { locale } }: { params: { locale: string } }) => {
|
||||
return (
|
||||
<MountedProvider
|
||||
isProtected={false}
|
||||
>
|
||||
<MountedProvider isProtected={false}>
|
||||
<ReactLenis root>
|
||||
<Navbar />
|
||||
<Hero />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
// components/custom-editor.js
|
||||
|
||||
import React from "react";
|
||||
import { CKEditor } from "@ckeditor/ckeditor5-react";
|
||||
import Editor from "ckeditor5-custom-build";
|
||||
|
||||
function CustomEditor(props) {
|
||||
return (
|
||||
<CKEditor
|
||||
editor={Editor}
|
||||
data={props.initialData}
|
||||
onChange={(event, editor) => {
|
||||
const data = editor.getData();
|
||||
console.log({ event, editor, data });
|
||||
props.onChange(data);
|
||||
}}
|
||||
config={{
|
||||
toolbar: [ 'heading', 'fontsize', 'bold', 'italic', 'link', 'numberedList', 'bulletedList', 'undo', 'redo', 'alignment', 'outdent', 'indent', 'blockQuote', 'insertTable', 'codeBlock', 'sourceEditing']
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomEditor;
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import React from "react";
|
||||
import { CKEditor } from "@ckeditor/ckeditor5-react";
|
||||
import Editor from "ckeditor5-custom-build";
|
||||
|
||||
function ViewEditor(props) {
|
||||
return (
|
||||
<CKEditor
|
||||
editor={Editor}
|
||||
data={props.initialData}
|
||||
disabled={true}
|
||||
config={{
|
||||
// toolbar: [],
|
||||
isReadOnly: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default ViewEditor;
|
||||
|
|
@ -136,7 +136,7 @@ export default function FormBlogDetail() {
|
|||
async function initState() {
|
||||
if (id) {
|
||||
const response = await getBlog(id);
|
||||
const details = response.data?.data;
|
||||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ export default function FormBlogUpdate() {
|
|||
async function initState() {
|
||||
if (id) {
|
||||
const response = await getBlog(id);
|
||||
const details = response.data?.data;
|
||||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ export default function FormBlog() {
|
|||
// const getCategories = async () => {
|
||||
// try {
|
||||
// const category = await listEnableCategory(fileTypeId);
|
||||
// const resCategory: Category[] = category.data.data.content;
|
||||
// const resCategory: Category[] = category?.data?.data?.content;
|
||||
|
||||
// setCategories(resCategory);
|
||||
// console.log("data category", resCategory);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,293 @@
|
|||
"use client";
|
||||
import { z } from "zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import {
|
||||
Form,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
import {
|
||||
getLocaleTime,
|
||||
getLocaleTimestamp,
|
||||
textEllipsis,
|
||||
} from "@/utils/globals";
|
||||
import {
|
||||
detailMediaSummary,
|
||||
getMediaBlastCampaignList,
|
||||
saveMediaBlastBroadcast,
|
||||
saveMediaBlastCampaign,
|
||||
} from "@/service/broadcast/broadcast";
|
||||
import { error } from "@/config/swal";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import JoditEditor from "jodit-react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useParams } from "next/navigation";
|
||||
import Select from "react-select";
|
||||
import makeAnimated from "react-select/animated";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
|
||||
const animatedComponent = makeAnimated();
|
||||
|
||||
const FormSchema = z.object({
|
||||
title: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
url: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
thumbnail: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
detail: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
selected: z
|
||||
.array(
|
||||
z.object({
|
||||
id: z.number(),
|
||||
label: z.string(),
|
||||
value: z.string(),
|
||||
})
|
||||
)
|
||||
.refine((value) => value.length > 0, {
|
||||
message: "Required",
|
||||
}),
|
||||
});
|
||||
|
||||
export default function ContentBlast(props: { type: string }) {
|
||||
const editor = useRef(null);
|
||||
const id = useParams()?.id;
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
const { type } = props;
|
||||
|
||||
const [dataSelectCampaign, setDataSelectCampaign] = useState([]);
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: { selected: [], detail: "" },
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
if (form.getValues("detail") == "") {
|
||||
form.setError("detail", {
|
||||
type: "manual",
|
||||
message: "Required",
|
||||
});
|
||||
} else {
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||
const selectedCampaign = form.getValues("selected");
|
||||
|
||||
for (let i = 0; i < selectedCampaign.length; i++) {
|
||||
const reqData = {
|
||||
mediaUploadId: id,
|
||||
mediaBlastCampaignId: selectedCampaign[i].id,
|
||||
subject: type == "wa" ? `*${data.title}*` : data.title,
|
||||
body: data.detail?.replace(/\n/g, ""),
|
||||
type: type,
|
||||
isScheduled: false,
|
||||
thumbnail: data?.thumbnail,
|
||||
sendDate: getLocaleTimestamp(new Date()),
|
||||
sendTime: getLocaleTime(new Date()),
|
||||
contentUrl: data.url,
|
||||
};
|
||||
|
||||
console.log("req =>", reqData);
|
||||
const response = await saveMediaBlastBroadcast(reqData);
|
||||
}
|
||||
setOpenModal(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
const response = await detailMediaSummary(String(id));
|
||||
const details = response?.data?.data;
|
||||
if (details != undefined) {
|
||||
form.setValue("thumbnail", details.smallThumbnailLink);
|
||||
let body = `<div><p>Berita hari ini !!!</p><div style='margin-top:20px;border:1px solid;border-radius:10px;width:500px;background-color:#f7f7f7'><div><img style='width:500px;height:auto;border-radius:10px;object-fit:cover' src='${
|
||||
details?.smallThumbnailLink
|
||||
}'></div><a style='padding:5px 20px;margin:0;text-decoration:none' href='${
|
||||
details?.pageUrl
|
||||
}'>${details?.pageUrl}</a><h3 style='padding:5px 20px;margin:0'>${
|
||||
details?.title
|
||||
}</h3><p style='padding:0 20px;margin:0;margin-bottom:10px'>
|
||||
${textEllipsis(details?.description, 150)}</p></div></div>`;
|
||||
form.setValue("title", `${details?.title}`);
|
||||
form.setValue(
|
||||
"url",
|
||||
details?.pageUrl || "https://mediahub.polri.go.id"
|
||||
);
|
||||
if (type == "wa") {
|
||||
body = `${textEllipsis(details?.description, 150)}`;
|
||||
form.setValue("detail", body);
|
||||
} else {
|
||||
form.setValue("detail", body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getCampaign() {
|
||||
const response = await getMediaBlastCampaignList();
|
||||
const campaign = response?.data?.data?.content;
|
||||
handleLabelCampaign(campaign);
|
||||
console.log(campaign);
|
||||
}
|
||||
|
||||
initState();
|
||||
getCampaign();
|
||||
}, [id]);
|
||||
|
||||
function handleLabelCampaign(data: any) {
|
||||
const optionArr: any = [];
|
||||
|
||||
data.map((option: any) => {
|
||||
optionArr.push({
|
||||
id: option.id,
|
||||
label: option.title,
|
||||
value: option.title,
|
||||
});
|
||||
});
|
||||
console.log("option arr", optionArr);
|
||||
setDataSelectCampaign(optionArr);
|
||||
}
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-3 bg-white rounded-sm p-4"
|
||||
>
|
||||
<p className="fonnt-semibold">Broadcast</p>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="selected"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Subject</FormLabel>
|
||||
<Select
|
||||
options={dataSelectCampaign}
|
||||
closeMenuOnSelect={false}
|
||||
components={animatedComponent}
|
||||
onChange={field.onChange}
|
||||
isMulti
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Subject</FormLabel>
|
||||
<Input
|
||||
value={field.value}
|
||||
placeholder="Masukkan Judul"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="detail"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detail Perencanaan</FormLabel>
|
||||
{type === "wa" ? (
|
||||
<Textarea value={field.value} onChange={field.onChange} />
|
||||
) : (
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={field.value}
|
||||
className="dark:text-black"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex flex-row gap-2 mt-4 pt-4">
|
||||
<Button
|
||||
size="md"
|
||||
type="button"
|
||||
variant="outline"
|
||||
color="destructive"
|
||||
className="text-xs"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="md" type="submit" color="primary" className="text-xs">
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
<Dialog open={openModal}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Email Terkirim</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col justify-center items-center gap-3 mb-3 text-sm">
|
||||
<img
|
||||
src="/assets/img/illust-for-broadcast-sent.png"
|
||||
className="w-[70%]"
|
||||
/>
|
||||
Untuk melihat Email Terkirim silahkan cek menu “Sent”!
|
||||
</div>
|
||||
<DialogFooter className="flex justify-center">
|
||||
<Button type="button" color="success">
|
||||
Menu "Sent"
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => router.push("/admin/broadcast")}
|
||||
color="primary"
|
||||
>
|
||||
Okay
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
|
@ -121,8 +121,8 @@ export default function FormCollaboration() {
|
|||
async function getTicketPriority() {
|
||||
const res = await getTicketingPriority();
|
||||
|
||||
if (res.data !== null) {
|
||||
const rawData = res.data?.data;
|
||||
if (res?.data !== null) {
|
||||
const rawData = res?.data?.data;
|
||||
setTicketPriority(rawData);
|
||||
}
|
||||
}
|
||||
|
|
@ -130,8 +130,8 @@ export default function FormCollaboration() {
|
|||
async function getUser() {
|
||||
const res = await getCuratorUser();
|
||||
|
||||
if (res.data !== null) {
|
||||
const rawUser = res.data?.data?.content;
|
||||
if (res?.data !== null) {
|
||||
const rawUser = res?.data?.data?.content;
|
||||
console.log("raw user", rawUser);
|
||||
|
||||
// Tentukan tipe array sebagai Option[]
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue