Compare commits
5 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
073dfe3b1c | |
|
|
71416a40d0 | |
|
|
9b044937fc | |
|
|
6254d43173 | |
|
|
149b434d22 |
|
|
@ -11,4 +11,3 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Dependencies
|
||||||
|
node_modules
|
||||||
|
.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# Next.js
|
||||||
|
.next/
|
||||||
|
out/
|
||||||
|
build
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# Debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Local env files
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# Vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# Typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
||||||
|
|
||||||
|
# PWA
|
||||||
|
**/public/sw.js
|
||||||
|
**/public/workbox-*.js
|
||||||
|
**/public/worker-*.js
|
||||||
|
**/public/sw.js.map
|
||||||
|
**/public/workbox-*.js.map
|
||||||
|
**/public/worker-*.js.map
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"printWidth": 100,
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"arrowParens": "always",
|
||||||
|
"endOfLine": "auto",
|
||||||
|
"plugins": ["prettier-plugin-tailwindcss", "@ianvs/prettier-plugin-sort-imports"],
|
||||||
|
"importOrder": [
|
||||||
|
"^(react/(.*)$)|^(react$)",
|
||||||
|
"^(next/(.*)$)|^(next$)",
|
||||||
|
"<THIRD_PARTY_MODULES>",
|
||||||
|
"",
|
||||||
|
"^@/types/(.*)$",
|
||||||
|
"^@/config/(.*)$",
|
||||||
|
"^@/lib/(.*)$",
|
||||||
|
"^@/hooks/(.*)$",
|
||||||
|
"^@/components/ui/(.*)$",
|
||||||
|
"^@/components/(.*)$",
|
||||||
|
"^@/styles/(.*)$",
|
||||||
|
"^@/app/(.*)$",
|
||||||
|
"",
|
||||||
|
"^[./]"
|
||||||
|
],
|
||||||
|
"importOrderParserPlugins": ["typescript", "jsx", "decorators-legacy"]
|
||||||
|
}
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
'use server'
|
|
||||||
import { redirect } from "next/navigation";
|
|
||||||
import { revalidatePath } from "next/cache";
|
|
||||||
import { postMessage } from "@/app/[locale]/(protected)/app/chat/utils";
|
|
||||||
|
|
||||||
|
|
||||||
export const postMessageAction = async (id: string, message: string,) => {
|
|
||||||
const response = await postMessage(id, message)
|
|
||||||
revalidatePath("/");
|
|
||||||
return response;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
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: "fullname",
|
|
||||||
header: "Nama",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("fullname")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "address",
|
|
||||||
header: "Wilayah",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("address")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "role.name",
|
|
||||||
header: "Bidang Keahlian",
|
|
||||||
cell: ({ row }) => <span>{row.original.role?.name ?? "-"}</span>,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
accessorKey: "experience",
|
|
||||||
header: "Pengalaman",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("experience")}</span>,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
accessorKey: "experience",
|
|
||||||
header: "Posisi",
|
|
||||||
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;
|
|
||||||
|
|
@ -1,302 +0,0 @@
|
||||||
"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";
|
|
||||||
import { listDataExperts } from "@/service/experts/experts";
|
|
||||||
|
|
||||||
const dummyData = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: "Prof. Dr. Ravi",
|
|
||||||
region: "Nasional",
|
|
||||||
skills: "Komunikasi",
|
|
||||||
experience: "Akademisi",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: "Prof. Dr. Novan",
|
|
||||||
region: "DKI Jakarta",
|
|
||||||
skills: "Hukum",
|
|
||||||
experience: "Akademisi + Praktisi",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const AddExpertTable = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
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 [limit, setLimit] = React.useState(10);
|
|
||||||
const table = useReactTable({
|
|
||||||
data: dataTable,
|
|
||||||
columns,
|
|
||||||
onSortingChange: setSorting,
|
|
||||||
onColumnFiltersChange: setColumnFilters,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
getSortedRowModel: getSortedRowModel(),
|
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
|
||||||
onColumnVisibilityChange: setColumnVisibility,
|
|
||||||
onRowSelectionChange: setRowSelection,
|
|
||||||
onPaginationChange: setPagination,
|
|
||||||
state: {
|
|
||||||
sorting,
|
|
||||||
columnFilters,
|
|
||||||
columnVisibility,
|
|
||||||
rowSelection,
|
|
||||||
pagination,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
let typingTimer: any;
|
|
||||||
const doneTypingInterval = 1500;
|
|
||||||
|
|
||||||
const handleKeyUp = () => {
|
|
||||||
clearTimeout(typingTimer);
|
|
||||||
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleKeyDown = () => {
|
|
||||||
clearTimeout(typingTimer);
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
async function fetchData() {
|
|
||||||
// const formattedStartDate = startDate
|
|
||||||
// ? format(new Date(startDate), "yyyy-MM-dd")
|
|
||||||
// : "";
|
|
||||||
// const formattedEndDate = endDate
|
|
||||||
// ? format(new Date(endDate), "yyyy-MM-dd")
|
|
||||||
// : "";
|
|
||||||
try {
|
|
||||||
// const isForSelf = Number(roleId) === 4;
|
|
||||||
const res = await listDataExperts(limit, page - 1);
|
|
||||||
|
|
||||||
const data = res?.data?.data;
|
|
||||||
const contentData = data?.content;
|
|
||||||
contentData.forEach((item: any, index: number) => {
|
|
||||||
item.no = (page - 1) * limit + index + 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
setDataTable(contentData);
|
|
||||||
setTotalData(data?.totalElements);
|
|
||||||
setTotalPage(data?.totalPages);
|
|
||||||
} 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;
|
|
||||||
|
|
@ -1,480 +0,0 @@
|
||||||
"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";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import {
|
|
||||||
AdministrationLevelList,
|
|
||||||
getListCompetencies,
|
|
||||||
getListExperiences,
|
|
||||||
saveUserInternal,
|
|
||||||
saveUserRolePlacements,
|
|
||||||
} from "@/service/management-user/management-user";
|
|
||||||
import { loading } from "@/config/swal";
|
|
||||||
import { Eye, EyeOff } from "lucide-react";
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
name: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
username: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
password: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
phoneNumber: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
email: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
skills: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
experiences: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
company: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type Placements = {
|
|
||||||
index: number;
|
|
||||||
roleId?: string;
|
|
||||||
userLevelId?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function AddExpertForm() {
|
|
||||||
const MySwal = withReactContent(Swal);
|
|
||||||
const router = useRouter();
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
});
|
|
||||||
const [incrementId, setIncrementId] = useState(1);
|
|
||||||
const [placementRows, setPlacementRows] = useState<Placements[]>([
|
|
||||||
{ index: 0, roleId: "", userLevelId: 0 },
|
|
||||||
]);
|
|
||||||
const [userCompetencies, setUserCompetencies] = useState<any>();
|
|
||||||
const [userExperiences, setUserExperiences] = useState<any>();
|
|
||||||
const [userLevels, setUserLevels] = useState<any>();
|
|
||||||
const [passwordType, setPasswordType] = useState("password");
|
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
|
||||||
|
|
||||||
const togglePasswordType = () => {
|
|
||||||
setPasswordType((prevType) =>
|
|
||||||
prevType === "password" ? "text" : "password"
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const roleSelection = [
|
|
||||||
{
|
|
||||||
id: "11",
|
|
||||||
name: "Koor Kurator",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "12",
|
|
||||||
name: "Kurator",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
const dataReq = {
|
|
||||||
firstName: data.name,
|
|
||||||
username: data.username,
|
|
||||||
email: data.email,
|
|
||||||
password: data.password,
|
|
||||||
address: "",
|
|
||||||
roleId: "EXP-ID",
|
|
||||||
phoneNumber: data.phoneNumber,
|
|
||||||
userCompetencyId: data.skills,
|
|
||||||
userExperienceId: data.experiences,
|
|
||||||
companyName: data.company,
|
|
||||||
};
|
|
||||||
|
|
||||||
loading();
|
|
||||||
const res = await saveUserInternal(dataReq);
|
|
||||||
const resData = res?.data?.data;
|
|
||||||
const userProfileId = resData?.id;
|
|
||||||
|
|
||||||
var placementArr: any[] = [];
|
|
||||||
placementRows.forEach((row: any) => {
|
|
||||||
placementArr.push({
|
|
||||||
roleId: Number(row.roleId),
|
|
||||||
userLevelId: Number(row.userLevelId),
|
|
||||||
userProfileId: userProfileId,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const dataReq2 = {
|
|
||||||
userId: userProfileId,
|
|
||||||
placements: placementArr,
|
|
||||||
};
|
|
||||||
const res2 = await saveUserRolePlacements(dataReq2);
|
|
||||||
const resData2 = res2?.data?.data;
|
|
||||||
|
|
||||||
success("/admin/add-experts");
|
|
||||||
};
|
|
||||||
|
|
||||||
function success(redirect: string): void {
|
|
||||||
MySwal.fire({
|
|
||||||
title: '<p class="text-green-600 font-bold">Sukses</p>',
|
|
||||||
icon: "success",
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
confirmButtonText: '<span class="text-white">OK</span>',
|
|
||||||
allowOutsideClick: false,
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
router.push(redirect);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getDataAdditional();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
async function getDataAdditional() {
|
|
||||||
const resCompetencies = await getListCompetencies();
|
|
||||||
setUserCompetencies(resCompetencies?.data?.data);
|
|
||||||
|
|
||||||
const resExperiences = await getListExperiences();
|
|
||||||
setUserExperiences(resExperiences?.data?.data);
|
|
||||||
|
|
||||||
const resUserLevels = await AdministrationLevelList();
|
|
||||||
const data = resUserLevels?.data?.data;
|
|
||||||
var levelsArr: any[] = [];
|
|
||||||
data.forEach((levels: any) => {
|
|
||||||
levelsArr.push({
|
|
||||||
id: levels.id,
|
|
||||||
label: levels.name,
|
|
||||||
name: levels.name,
|
|
||||||
value: String(levels.id),
|
|
||||||
levelNumber: levels.levelNumber,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
setUserLevels(levelsArr);
|
|
||||||
}
|
|
||||||
|
|
||||||
function successSubmit() {
|
|
||||||
MySwal.fire({
|
|
||||||
title: "Sukses",
|
|
||||||
icon: "success",
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
confirmButtonText: "OK",
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
router.push("/admin/add-experts");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSelectionChange = (
|
|
||||||
index: number,
|
|
||||||
type: "roleId" | "userLevelId",
|
|
||||||
value: string
|
|
||||||
) => {
|
|
||||||
setPlacementRows((prevRows) =>
|
|
||||||
prevRows.map((row) =>
|
|
||||||
row.index === index ? { ...row, [type]: value } : row
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRemoveRow = (index: number) => {
|
|
||||||
console.log(index);
|
|
||||||
console.log(placementRows);
|
|
||||||
const newPlacements = placementRows.filter((row) => row.index != index);
|
|
||||||
console.log(newPlacements);
|
|
||||||
setPlacementRows(newPlacements);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAddRow = () => {
|
|
||||||
setPlacementRows((prevRows: any) => [
|
|
||||||
...prevRows,
|
|
||||||
{ index: incrementId, roleId: "", userLevelId: 0 },
|
|
||||||
]);
|
|
||||||
setIncrementId((prevId) => prevId + 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
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="username"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Username</FormLabel>
|
|
||||||
<Input
|
|
||||||
value={field.value}
|
|
||||||
placeholder="Masukkan Username"
|
|
||||||
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="password"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Password</FormLabel>
|
|
||||||
<div className="relative">
|
|
||||||
<Input
|
|
||||||
value={field.value}
|
|
||||||
type={showPassword ? "text" : "password"}
|
|
||||||
placeholder="Masukkan Password"
|
|
||||||
onChange={field.onChange}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setShowPassword(!showPassword)}
|
|
||||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-default-500 hover:text-default-700"
|
|
||||||
tabIndex={-1}
|
|
||||||
>
|
|
||||||
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
{userCompetencies?.map((item: any) => (
|
|
||||||
<SelectItem key={item.id} value={String(item.id)}>
|
|
||||||
{item.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="experiences"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Pengalaman</FormLabel>
|
|
||||||
<Select onValueChange={field.onChange} value={field.value}>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Pilih Pengalaman" />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{userExperiences?.map((item: any) => (
|
|
||||||
<SelectItem key={item.id} value={String(item.id)}>
|
|
||||||
{item.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</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="mt-4">
|
|
||||||
<FormLabel>Posisi</FormLabel>
|
|
||||||
{placementRows?.map((row: any) => (
|
|
||||||
<div key={row.index} className="flex items-center gap-2 my-2">
|
|
||||||
<Select
|
|
||||||
onValueChange={(e) =>
|
|
||||||
handleSelectionChange(row.index, "roleId", e)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Pilih Role" />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{roleSelection?.map((item: any) => (
|
|
||||||
<SelectItem key={item.id} value={String(item.id)}>
|
|
||||||
{item.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<Select
|
|
||||||
onValueChange={(e) =>
|
|
||||||
handleSelectionChange(row.index, "userLevelId", e)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Pilih User Level" />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{userLevels?.map((item: any) => (
|
|
||||||
<SelectItem key={item.id} value={String(item.id)}>
|
|
||||||
{item.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
{placementRows.length > 1 && (
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
size="md"
|
|
||||||
color="destructive"
|
|
||||||
onClick={() => handleRemoveRow(row.index)}
|
|
||||||
>
|
|
||||||
Hapus
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<Button type="button" size="md" onClick={() => handleAddRow()}>
|
|
||||||
Tambah
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import AddExpertTable from "./component/table";
|
|
||||||
|
|
||||||
export default function AddExpert() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<AddExpertTable />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,306 +0,0 @@
|
||||||
"use client";
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import {
|
|
||||||
Accordion,
|
|
||||||
AccordionContent,
|
|
||||||
AccordionItem,
|
|
||||||
AccordionTrigger,
|
|
||||||
} from "@/components/ui/accordion";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { addDays, format } from "date-fns";
|
|
||||||
import { Calendar as CalendarIcon } from "lucide-react";
|
|
||||||
import { DateRange } from "react-day-picker";
|
|
||||||
import Cookies from "js-cookie";
|
|
||||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
import { generateTicket } from "@/service/tableau/tableau-service";
|
|
||||||
|
|
||||||
const users = [
|
|
||||||
{ id: 1, name: "POLRI" },
|
|
||||||
{ id: 2, name: "JURNALIS" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function ContentManagement() {
|
|
||||||
const [startDate, setStartDate] = useState<any>(new Date());
|
|
||||||
const [endDate, setEndDate] = useState<any>(new Date());
|
|
||||||
const [hasMounted, setHasMounted] = useState(false);
|
|
||||||
// const t = useTranslations("AnalyticsDashboard");
|
|
||||||
const levelName = getCookiesDecrypt("ulnae");
|
|
||||||
const poldaState = Cookies.get("state");
|
|
||||||
const provState = Cookies.get("state-prov");
|
|
||||||
|
|
||||||
const [ticket1, setTicket1] = useState("");
|
|
||||||
const [ticket2, setTicket2] = useState("");
|
|
||||||
const [ticket3, setTicket3] = useState("");
|
|
||||||
const [ticket4, setTicket4] = useState("");
|
|
||||||
const [ticket5, setTicket5] = useState("");
|
|
||||||
const [ticket6, setTicket6] = useState("");
|
|
||||||
const [isInternational, setIsInternational] = useState([false, false, false]);
|
|
||||||
|
|
||||||
const baseUrl = "https://analytic.sitani.info/";
|
|
||||||
const url = "https://analytic.sitani.info/trusted/";
|
|
||||||
|
|
||||||
const view1 =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? isInternational[0]
|
|
||||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-indo-polri?"
|
|
||||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-indo-polri?"
|
|
||||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-indo-polri?provinsi-polda=${provState}&`;
|
|
||||||
|
|
||||||
const view2 =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? isInternational[1]
|
|
||||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-indo-jurnalis?"
|
|
||||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-indo-jurnalis?"
|
|
||||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-indo-jurnalis?provinsi-polda=${poldaState}&`;
|
|
||||||
|
|
||||||
const view3 =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? isInternational[2]
|
|
||||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-jurnalis-intl?"
|
|
||||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-jurnalis-intl?"
|
|
||||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-jurnalis-intl?provinsi-polda=${poldaState}&`;
|
|
||||||
|
|
||||||
const view4 =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? isInternational[3]
|
|
||||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-indo-satker?"
|
|
||||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-indo-satker?"
|
|
||||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-indo-satker?provinsi-polda=${poldaState}&`;
|
|
||||||
|
|
||||||
const view5 =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? isInternational[4]
|
|
||||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-indo-jnl?"
|
|
||||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-indo-jnl?"
|
|
||||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-indo-jnl?provinsi-polda=${poldaState}&`;
|
|
||||||
|
|
||||||
const view6 =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? isInternational[5]
|
|
||||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-intl?"
|
|
||||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-intl?"
|
|
||||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-intl?provinsi-polda=${poldaState}&`;
|
|
||||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function initState() {
|
|
||||||
const response1 = await generateTicket();
|
|
||||||
setTicket1(response1?.data?.data);
|
|
||||||
|
|
||||||
const response2 = await generateTicket();
|
|
||||||
setTicket2(response2?.data?.data);
|
|
||||||
|
|
||||||
const response3 = await generateTicket();
|
|
||||||
setTicket3(response3?.data?.data);
|
|
||||||
|
|
||||||
const response4 = await generateTicket();
|
|
||||||
setTicket4(response4?.data?.data);
|
|
||||||
|
|
||||||
const response5 = await generateTicket();
|
|
||||||
setTicket5(response5?.data?.data);
|
|
||||||
|
|
||||||
const response6 = await generateTicket();
|
|
||||||
setTicket6(response6?.data?.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
initState();
|
|
||||||
}, [isInternational]);
|
|
||||||
|
|
||||||
// Hooks
|
|
||||||
useEffect(() => {
|
|
||||||
setHasMounted(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Render
|
|
||||||
if (!hasMounted) return null;
|
|
||||||
|
|
||||||
const handleInternational = (index: number, val: boolean) => {
|
|
||||||
const updatedIsInternational = [...isInternational];
|
|
||||||
|
|
||||||
updatedIsInternational[index] = val;
|
|
||||||
setIsInternational(updatedIsInternational);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<Accordion id="polri" type="single" collapsible className="w-full">
|
|
||||||
<AccordionItem value="item-1" className="bg-white w-full">
|
|
||||||
<AccordionTrigger className="bg-white">
|
|
||||||
KONTEN YANG DISIMPAN OLEH PENGGUNA POLRI INDONESIA
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="my-5">
|
|
||||||
{ticket1 == "" ? (
|
|
||||||
<iframe
|
|
||||||
src={`${baseUrl + view1 + param}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<iframe
|
|
||||||
src={`${`${url + ticket1}/${view1}${param}`}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion id="2" type="single" collapsible className="w-full">
|
|
||||||
<AccordionItem value="item-2" className="bg-white w-full">
|
|
||||||
<AccordionTrigger className="bg-white">
|
|
||||||
PENAMBAHAN JUMLAH PENGGUNA JURNALIS INDONESIA
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="my-5">
|
|
||||||
{ticket2 == "" ? (
|
|
||||||
<iframe
|
|
||||||
src={`${baseUrl + view2 + param}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<iframe
|
|
||||||
src={`${`${url + ticket2}/${view2}${param}`}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion id="3" type="single" collapsible className="w-full">
|
|
||||||
<AccordionItem value="item-1" className="bg-white w-full">
|
|
||||||
<AccordionTrigger className="bg-white">
|
|
||||||
KONTEN YANG DISIMPAN OLEH PENGGUNA JURNALIS INTERNASIONAL
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="my-5">
|
|
||||||
{ticket3 == "" ? (
|
|
||||||
<iframe
|
|
||||||
src={`${baseUrl + view3 + param}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<iframe
|
|
||||||
src={`${`${url + ticket3}/${view3}${param}`}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion id="4" type="single" collapsible className="w-full">
|
|
||||||
<AccordionItem value="item-1" className="bg-white w-full">
|
|
||||||
<AccordionTrigger className="bg-white">
|
|
||||||
PENAMBAHAN JUMLAH PENGGUNA POLRI INDONESIA
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="my-5">
|
|
||||||
{ticket4 == "" ? (
|
|
||||||
<iframe
|
|
||||||
src={`${baseUrl + view4 + param}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<iframe
|
|
||||||
src={`${`${url + ticket4}/${view4}${param}`}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion id="5" type="single" collapsible className="w-full">
|
|
||||||
<AccordionItem value="item-1" className="bg-white w-full">
|
|
||||||
<AccordionTrigger className="bg-white">
|
|
||||||
PENAMBAHAN JUMLAH PENGGUNA JURNALIS INDONESIA
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="my-5">
|
|
||||||
{ticket5 == "" ? (
|
|
||||||
<iframe
|
|
||||||
src={`${baseUrl + view5 + param}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<iframe
|
|
||||||
src={`${`${url + ticket5}/${view5}${param}`}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion id="6" type="single" collapsible className="w-full">
|
|
||||||
<AccordionItem value="item-1" className="bg-white w-full">
|
|
||||||
<AccordionTrigger className="bg-white">
|
|
||||||
PENAMBAHAN JUMLAH PENGGUNA JURNALIS INTERNASIONAL
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="my-5">
|
|
||||||
{ticket6 == "" ? (
|
|
||||||
<iframe
|
|
||||||
src={`${baseUrl + view6 + param}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<iframe
|
|
||||||
src={`${`${url + ticket6}/${view6}${param}`}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
"use client";
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import {
|
|
||||||
Accordion,
|
|
||||||
AccordionContent,
|
|
||||||
AccordionItem,
|
|
||||||
AccordionTrigger,
|
|
||||||
} from "@/components/ui/accordion";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { addDays, format } from "date-fns";
|
|
||||||
import { Calendar as CalendarIcon } from "lucide-react";
|
|
||||||
import { DateRange } from "react-day-picker";
|
|
||||||
import Cookies from "js-cookie";
|
|
||||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
import { generateTicket } from "@/service/tableau/tableau-service";
|
|
||||||
|
|
||||||
const users = [
|
|
||||||
{ id: 1, name: "POLRI" },
|
|
||||||
{ id: 2, name: "JURNALIS" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function EmergencyIssue() {
|
|
||||||
const [startDate, setStartDate] = useState<any>(new Date());
|
|
||||||
const [endDate, setEndDate] = useState<any>(new Date());
|
|
||||||
const [hasMounted, setHasMounted] = useState(false);
|
|
||||||
// const t = useTranslations("AnalyticsDashboard");
|
|
||||||
const levelName = getCookiesDecrypt("ulnae");
|
|
||||||
const poldaState = Cookies.get("state");
|
|
||||||
const provState = Cookies.get("state-prov");
|
|
||||||
|
|
||||||
const [ticket1, setTicket1] = useState("");
|
|
||||||
const [ticket2, setTicket2] = useState("");
|
|
||||||
const [ticket3, setTicket3] = useState("");
|
|
||||||
const [ticket4, setTicket4] = useState("");
|
|
||||||
const [ticket5, setTicket5] = useState("");
|
|
||||||
const [ticket6, setTicket6] = useState("");
|
|
||||||
const [isInternational, setIsInternational] = useState([false, false, false]);
|
|
||||||
|
|
||||||
const baseUrl = "https://analytic.sitani.info/";
|
|
||||||
const url = "https://analytic.sitani.info/trusted/";
|
|
||||||
|
|
||||||
const view1 =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? isInternational[0]
|
|
||||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?"
|
|
||||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?"
|
|
||||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?provinsi-polda=${provState}&`;
|
|
||||||
|
|
||||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function initState() {
|
|
||||||
const response1 = await generateTicket();
|
|
||||||
setTicket1(response1?.data?.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
initState();
|
|
||||||
}, [isInternational]);
|
|
||||||
|
|
||||||
// Hooks
|
|
||||||
useEffect(() => {
|
|
||||||
setHasMounted(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Render
|
|
||||||
if (!hasMounted) return null;
|
|
||||||
|
|
||||||
const handleInternational = (index: number, val: boolean) => {
|
|
||||||
const updatedIsInternational = [...isInternational];
|
|
||||||
|
|
||||||
updatedIsInternational[index] = val;
|
|
||||||
setIsInternational(updatedIsInternational);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<Accordion
|
|
||||||
id="feedback-center"
|
|
||||||
type="single"
|
|
||||||
collapsible
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
<AccordionItem value="item-1" className="bg-white w-full">
|
|
||||||
<AccordionTrigger className="bg-white">
|
|
||||||
ANALISA BERKAITAN DENGAN AKUN PELAPOR{" "}
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="my-5">
|
|
||||||
{ticket1 == "" ? (
|
|
||||||
<iframe
|
|
||||||
src={`${baseUrl + view1 + param}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<iframe
|
|
||||||
src={`${`${url + ticket1}/${view1}${param}`}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
"use client";
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import {
|
|
||||||
Accordion,
|
|
||||||
AccordionContent,
|
|
||||||
AccordionItem,
|
|
||||||
AccordionTrigger,
|
|
||||||
} from "@/components/ui/accordion";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { addDays, format } from "date-fns";
|
|
||||||
import { Calendar as CalendarIcon } from "lucide-react";
|
|
||||||
import { DateRange } from "react-day-picker";
|
|
||||||
import Cookies from "js-cookie";
|
|
||||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
import { generateTicket } from "@/service/tableau/tableau-service";
|
|
||||||
|
|
||||||
const users = [
|
|
||||||
{ id: 1, name: "POLRI" },
|
|
||||||
{ id: 2, name: "JURNALIS" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function FeedbackCenter() {
|
|
||||||
const [startDate, setStartDate] = useState<any>(new Date());
|
|
||||||
const [endDate, setEndDate] = useState<any>(new Date());
|
|
||||||
const [hasMounted, setHasMounted] = useState(false);
|
|
||||||
// const t = useTranslations("AnalyticsDashboard");
|
|
||||||
const levelName = getCookiesDecrypt("ulnae");
|
|
||||||
const poldaState = Cookies.get("state");
|
|
||||||
const provState = Cookies.get("state-prov");
|
|
||||||
|
|
||||||
const [ticket1, setTicket1] = useState("");
|
|
||||||
const [ticket2, setTicket2] = useState("");
|
|
||||||
const [ticket3, setTicket3] = useState("");
|
|
||||||
const [ticket4, setTicket4] = useState("");
|
|
||||||
const [ticket5, setTicket5] = useState("");
|
|
||||||
const [ticket6, setTicket6] = useState("");
|
|
||||||
const [isInternational, setIsInternational] = useState([false, false, false]);
|
|
||||||
|
|
||||||
const baseUrl = "https://analytic.sitani.info/";
|
|
||||||
const url = "https://analytic.sitani.info/trusted/";
|
|
||||||
|
|
||||||
const view1 =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? isInternational[0]
|
|
||||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-tickets?"
|
|
||||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-tickets?"
|
|
||||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-tickets?provinsi-polda=${provState}&`;
|
|
||||||
|
|
||||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function initState() {
|
|
||||||
const response1 = await generateTicket();
|
|
||||||
setTicket1(response1?.data?.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
initState();
|
|
||||||
}, [isInternational]);
|
|
||||||
|
|
||||||
// Hooks
|
|
||||||
useEffect(() => {
|
|
||||||
setHasMounted(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Render
|
|
||||||
if (!hasMounted) return null;
|
|
||||||
|
|
||||||
const handleInternational = (index: number, val: boolean) => {
|
|
||||||
const updatedIsInternational = [...isInternational];
|
|
||||||
|
|
||||||
updatedIsInternational[index] = val;
|
|
||||||
setIsInternational(updatedIsInternational);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<Accordion
|
|
||||||
id="feedback-center"
|
|
||||||
type="single"
|
|
||||||
collapsible
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
<AccordionItem value="item-1" className="bg-white w-full">
|
|
||||||
<AccordionTrigger className="bg-white">
|
|
||||||
TICKET PADA FEEDBACK CENTER{" "}
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="my-5">
|
|
||||||
{ticket1 == "" ? (
|
|
||||||
<iframe
|
|
||||||
src={`${baseUrl + view1 + param}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<iframe
|
|
||||||
src={`${`${url + ticket1}/${view1}${param}`}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,246 +0,0 @@
|
||||||
"use client";
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import {
|
|
||||||
Accordion,
|
|
||||||
AccordionContent,
|
|
||||||
AccordionItem,
|
|
||||||
AccordionTrigger,
|
|
||||||
} from "@/components/ui/accordion";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { addDays, format } from "date-fns";
|
|
||||||
import { Calendar as CalendarIcon } from "lucide-react";
|
|
||||||
import { DateRange } from "react-day-picker";
|
|
||||||
import Cookies from "js-cookie";
|
|
||||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
import { generateTicket } from "@/service/tableau/tableau-service";
|
|
||||||
|
|
||||||
export default function ContentManagement() {
|
|
||||||
const [startDate, setStartDate] = useState<any>(new Date());
|
|
||||||
const [endDate, setEndDate] = useState<any>(new Date());
|
|
||||||
const [hasMounted, setHasMounted] = useState(false);
|
|
||||||
// const t = useTranslations("AnalyticsDashboard");
|
|
||||||
const levelName = getCookiesDecrypt("ulnae");
|
|
||||||
const poldaState = Cookies.get("state");
|
|
||||||
const provState = Cookies.get("state-prov");
|
|
||||||
|
|
||||||
const [ticket1, setTicket1] = useState("");
|
|
||||||
const [ticket2, setTicket2] = useState("");
|
|
||||||
const [ticket3, setTicket3] = useState("");
|
|
||||||
const [ticket4, setTicket4] = useState("");
|
|
||||||
const [ticket5, setTicket5] = useState("");
|
|
||||||
const [ticket6, setTicket6] = useState("");
|
|
||||||
const [isInternational, setIsInternational] = useState([false, false, false]);
|
|
||||||
|
|
||||||
const baseUrl = "https://analytic.sitani.info/";
|
|
||||||
const url = "https://analytic.sitani.info/trusted/";
|
|
||||||
|
|
||||||
const view1 =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? isInternational[0]
|
|
||||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-rank?"
|
|
||||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-rank?"
|
|
||||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-rank?provinsi-polda=${provState}&`;
|
|
||||||
|
|
||||||
const view2 =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? isInternational[1]
|
|
||||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-rank-kat?"
|
|
||||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-rank-kat?"
|
|
||||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-rank-kat?provinsi-polda=${poldaState}&`;
|
|
||||||
|
|
||||||
const view3 =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? isInternational[2]
|
|
||||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-act?"
|
|
||||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-act?"
|
|
||||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-act?provinsi-polda=${poldaState}&`;
|
|
||||||
|
|
||||||
const view4 =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? isInternational[3]
|
|
||||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-act-jnl?"
|
|
||||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-act-jnl?"
|
|
||||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-ktn-act-jnl?provinsi-polda=${poldaState}&`;
|
|
||||||
|
|
||||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function initState() {
|
|
||||||
const response1 = await generateTicket();
|
|
||||||
setTicket1(response1?.data?.data);
|
|
||||||
|
|
||||||
const response2 = await generateTicket();
|
|
||||||
setTicket2(response2?.data?.data);
|
|
||||||
|
|
||||||
const response3 = await generateTicket();
|
|
||||||
setTicket3(response3?.data?.data);
|
|
||||||
|
|
||||||
const response4 = await generateTicket();
|
|
||||||
setTicket4(response4?.data?.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
initState();
|
|
||||||
}, [isInternational]);
|
|
||||||
|
|
||||||
// Hooks
|
|
||||||
useEffect(() => {
|
|
||||||
setHasMounted(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Render
|
|
||||||
if (!hasMounted) return null;
|
|
||||||
|
|
||||||
const handleInternational = (index: number, val: boolean) => {
|
|
||||||
const updatedIsInternational = [...isInternational];
|
|
||||||
|
|
||||||
updatedIsInternational[index] = val;
|
|
||||||
setIsInternational(updatedIsInternational);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<Accordion
|
|
||||||
id="schedule-press-conference"
|
|
||||||
type="single"
|
|
||||||
collapsible
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
<AccordionItem value="item-1" className="bg-white w-full">
|
|
||||||
<AccordionTrigger className="bg-white">
|
|
||||||
PUBLISH JADWAL PRESS CONFERENCE TERBANYAK
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="my-5">
|
|
||||||
{ticket1 == "" ? (
|
|
||||||
<iframe
|
|
||||||
src={`${baseUrl + view1 + param}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<iframe
|
|
||||||
src={`${`${url + ticket1}/${view1}${param}`}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion
|
|
||||||
id="content-press-conference"
|
|
||||||
type="single"
|
|
||||||
collapsible
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
<AccordionItem value="item-1" className="bg-white w-full">
|
|
||||||
<AccordionTrigger className="bg-white">
|
|
||||||
JUMLAH PRODUKSI KONTEN UNTUK KATEGORI PRESS CONFERENCE
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="my-5">
|
|
||||||
{ticket2 == "" ? (
|
|
||||||
<iframe
|
|
||||||
src={`${baseUrl + view2 + param}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<iframe
|
|
||||||
src={`${`${url + ticket2}/${view2}${param}`}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion
|
|
||||||
id="interaction-press-conference"
|
|
||||||
type="single"
|
|
||||||
collapsible
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
<AccordionItem value="item-1" className="bg-white w-full">
|
|
||||||
<AccordionTrigger className="bg-white">
|
|
||||||
TINGKAT INTERAKSI KONTEN UNTUK KATEGORI PRESS CONFERENCE
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="my-5">
|
|
||||||
{ticket3 == "" ? (
|
|
||||||
<iframe
|
|
||||||
src={`${baseUrl + view3 + param}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<iframe
|
|
||||||
src={`${`${url + ticket3}/${view3}${param}`}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion
|
|
||||||
id="press-release"
|
|
||||||
type="single"
|
|
||||||
collapsible
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
<AccordionItem value="item-1" className="bg-white w-full">
|
|
||||||
<AccordionTrigger className="bg-white">
|
|
||||||
AKTIFITAS MEDIA BERKAITAN DENGAN PERS RILIS
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="my-5">
|
|
||||||
{ticket4 == "" ? (
|
|
||||||
<iframe
|
|
||||||
src={`${baseUrl + view4 + param}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<iframe
|
|
||||||
src={`${`${url + ticket4}/${view4}${param}`}`}
|
|
||||||
width="100%"
|
|
||||||
height="750"
|
|
||||||
frameBorder="0"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
||||||
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;
|
|
||||||
|
|
@ -1,275 +0,0 @@
|
||||||
"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;
|
|
||||||
|
|
@ -1,306 +0,0 @@
|
||||||
"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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,325 +0,0 @@
|
||||||
"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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import AccountListTable from "./component/table";
|
|
||||||
|
|
||||||
export default function AdminCampaignList() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<AccountListTable />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
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;
|
|
||||||
|
|
@ -1,215 +0,0 @@
|
||||||
"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;
|
|
||||||
|
|
@ -1,198 +0,0 @@
|
||||||
"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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,227 +0,0 @@
|
||||||
"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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import CampaignListTable from "./component/table";
|
|
||||||
|
|
||||||
export default function AdminCampaignList() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<CampaignListTable />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
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: "fileTypeName",
|
|
||||||
header: "Konten",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("fileTypeName")}</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;
|
|
||||||
|
|
@ -1,403 +0,0 @@
|
||||||
"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,
|
|
||||||
listDataMediaBroadCast,
|
|
||||||
} 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 BroadcastEmailTable = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const [search, setSearch] = React.useState("");
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
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 listDataMediaBroadCast(
|
|
||||||
page - 1,
|
|
||||||
showData,
|
|
||||||
search,
|
|
||||||
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 ">
|
|
||||||
<Link href="/admin/broadcast/campaign-list" className="mr-3">
|
|
||||||
<Button color="primary" size="md" className="text-sm">
|
|
||||||
<UserIcon />
|
|
||||||
Daftar Campaign
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
placeholder="Search"
|
|
||||||
onKeyUp={handleKeyUp}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
|
||||||
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 BroadcastEmailTable;
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
"use client";
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import BroadcastTable from "./email/component/table";
|
|
||||||
import { PlusIcon } from "lucide-react";
|
|
||||||
|
|
||||||
import EscalationTable from "../../shared/communication/escalation/components/escalation-table";
|
|
||||||
import InternalTable from "../../shared/communication/internal/components/internal-table";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { Link } from "@/i18n/routing";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import BroadcastEmailTable from "./email/component/table";
|
|
||||||
import BroadcastWhatsAppTable from "./whatsapp/component/table";
|
|
||||||
|
|
||||||
export default function AdminBroadcast() {
|
|
||||||
const [tab, setTab] = useState("Email Blast");
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
|
||||||
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
|
|
||||||
<Button
|
|
||||||
rounded="md"
|
|
||||||
onClick={() => setTab("Email Blast")}
|
|
||||||
className={` hover:text-white
|
|
||||||
${
|
|
||||||
tab === "Email Blast"
|
|
||||||
? "bg-black text-white "
|
|
||||||
: "bg-white text-black "
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
Email Blast
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
rounded="md"
|
|
||||||
onClick={() => setTab("WhatsApp Blast")}
|
|
||||||
className={` hover:text-white
|
|
||||||
${
|
|
||||||
tab === "WhatsApp Blast"
|
|
||||||
? "bg-black text-white "
|
|
||||||
: "bg-white text-black "
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
WhatsApp Blast
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
{tab === "Email Blast" && <BroadcastEmailTable />}
|
|
||||||
{tab === "WhatsApp Blast" && <BroadcastWhatsAppTable />}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
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;
|
|
||||||
|
|
@ -1,403 +0,0 @@
|
||||||
"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,
|
|
||||||
listDataMediaBroadCast,
|
|
||||||
} 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 BroadcastWhatsAppTable = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const [search, setSearch] = React.useState("");
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
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 listDataMediaBroadCast(
|
|
||||||
page - 1,
|
|
||||||
showData,
|
|
||||||
search,
|
|
||||||
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 ">
|
|
||||||
<Link href="/admin/broadcast/campaign-list" className="mr-3">
|
|
||||||
<Button color="primary" size="md" className="text-sm">
|
|
||||||
<UserIcon />
|
|
||||||
Daftar Campaign
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
placeholder="Search"
|
|
||||||
onKeyUp={handleKeyUp}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
|
||||||
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 BroadcastWhatsAppTable;
|
|
||||||
|
|
@ -1,291 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
|
|
||||||
import {
|
|
||||||
getUserById,
|
|
||||||
saveUserInternal,
|
|
||||||
} from "@/service/management-user/management-user";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
|
||||||
|
|
||||||
import Swal from "sweetalert2";
|
|
||||||
import withReactContent from "sweetalert2-react-content";
|
|
||||||
import { close, error, loading } from "@/config/swal";
|
|
||||||
import { useParams } from "next/navigation";
|
|
||||||
import { identity } from "@fullcalendar/core/internal";
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
fullname: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
username: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
|
|
||||||
identity: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
address: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
email: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
phoneNumber: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
password: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
confirmPassword: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
isValidPassword: z.boolean().refine((val) => val === true, {
|
|
||||||
message: "Check Password",
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function EditUserForm() {
|
|
||||||
const router = useRouter();
|
|
||||||
const params = useParams();
|
|
||||||
const id = params?.id;
|
|
||||||
const MySwal = withReactContent(Swal);
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
defaultValues: {
|
|
||||||
password: "",
|
|
||||||
confirmPassword: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const passwordVal = form.watch("password");
|
|
||||||
const confPasswordVal = form.watch("confirmPassword");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
initData();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const initData = async () => {
|
|
||||||
loading();
|
|
||||||
const response = await getUserById(String(id));
|
|
||||||
const res = response?.data?.data;
|
|
||||||
close();
|
|
||||||
console.log("res", res);
|
|
||||||
form.setValue("fullname", res?.fullname);
|
|
||||||
form.setValue("username", res?.username);
|
|
||||||
form.setValue("phoneNumber", res?.phoneNumber);
|
|
||||||
form.setValue("address", res?.address);
|
|
||||||
form.setValue("email", res?.email);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function save(data: z.infer<typeof FormSchema>) {
|
|
||||||
let req: any = {
|
|
||||||
id: Number(id),
|
|
||||||
firstName: data.fullname,
|
|
||||||
username: data.username,
|
|
||||||
address: data.address,
|
|
||||||
email: data.email,
|
|
||||||
identityNumber: data.identity,
|
|
||||||
password: data.password,
|
|
||||||
passwordConf: data.confirmPassword,
|
|
||||||
isDefault: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
loading();
|
|
||||||
const response = await saveUserInternal(req);
|
|
||||||
|
|
||||||
if (response?.error) {
|
|
||||||
error(response.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
close();
|
|
||||||
MySwal.fire({
|
|
||||||
title: "Sukses",
|
|
||||||
icon: "success",
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
confirmButtonText: "Oke",
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
router.push("/admin/management-user");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
|
||||||
MySwal.fire({
|
|
||||||
title: "Simpan Data?",
|
|
||||||
text: "",
|
|
||||||
icon: "warning",
|
|
||||||
showCancelButton: true,
|
|
||||||
cancelButtonColor: "#d33",
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
confirmButtonText: "Simpan",
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
save(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
className="space-y-6 bg-white p-10 w-full"
|
|
||||||
>
|
|
||||||
<p className="text-xl">Data Pengelola Media Hub</p>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="fullname"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Nama Lengkap</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
placeholder="Masukkan nama lengkap"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="username"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Username</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
placeholder="Masukkan username"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="identity"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Nomor Identitas</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="Masukkan nomor identitas"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="address"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Alamat</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea
|
|
||||||
placeholder="Masukkan alamat"
|
|
||||||
className="resize-none w-1/2"
|
|
||||||
{...field}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="email"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Email</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="email"
|
|
||||||
placeholder="Masukkan email"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="phoneNumber"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>No. Handphone</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="Masukkan nomor handphone"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2 mb-5"
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Link href="/admin/management-user">
|
|
||||||
<Button type="button" color="primary" variant="outline">
|
|
||||||
Back
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,381 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { Check, ChevronsUpDown } from "lucide-react";
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import {
|
|
||||||
Command,
|
|
||||||
CommandEmpty,
|
|
||||||
CommandGroup,
|
|
||||||
CommandInput,
|
|
||||||
CommandItem,
|
|
||||||
CommandList,
|
|
||||||
} from "@/components/ui/command";
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
import {
|
|
||||||
AdministrationLevelList,
|
|
||||||
getListCompetencies,
|
|
||||||
getListEducation,
|
|
||||||
getListSchools,
|
|
||||||
getUserById,
|
|
||||||
saveUserInternal,
|
|
||||||
} from "@/service/management-user/management-user";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/components/ui/select";
|
|
||||||
import Swal from "sweetalert2";
|
|
||||||
import withReactContent from "sweetalert2-react-content";
|
|
||||||
import { close, error, loading } from "@/config/swal";
|
|
||||||
import { useParams } from "next/navigation";
|
|
||||||
import { identity } from "@fullcalendar/core/internal";
|
|
||||||
|
|
||||||
const PasswordChecklist = dynamic(() => import("react-password-checklist"), {
|
|
||||||
ssr: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
interface RoleData {
|
|
||||||
id: number;
|
|
||||||
label: string;
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
levelNumber: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
fullname: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
username: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
|
|
||||||
identity: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
address: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
email: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
phoneNumber: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
password: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
confirmPassword: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
isValidPassword: z.boolean().refine((val) => val === true, {
|
|
||||||
message: "Check Password",
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function EditUserForm() {
|
|
||||||
const router = useRouter();
|
|
||||||
const params = useParams();
|
|
||||||
const id = params?.id;
|
|
||||||
const MySwal = withReactContent(Swal);
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
defaultValues: {
|
|
||||||
password: "",
|
|
||||||
confirmPassword: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const passwordVal = form.watch("password");
|
|
||||||
const confPasswordVal = form.watch("confirmPassword");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
initData();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const initData = async () => {
|
|
||||||
loading();
|
|
||||||
const response = await getUserById(String(id));
|
|
||||||
const res = response?.data?.data;
|
|
||||||
close();
|
|
||||||
console.log("res", res);
|
|
||||||
form.setValue("fullname", res?.fullname);
|
|
||||||
form.setValue("username", res?.username);
|
|
||||||
form.setValue("phoneNumber", res?.phoneNumber);
|
|
||||||
form.setValue("address", res?.address);
|
|
||||||
form.setValue("email", res?.email);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function save(data: z.infer<typeof FormSchema>) {
|
|
||||||
let req: any = {
|
|
||||||
id: Number(id),
|
|
||||||
firstName: data.fullname,
|
|
||||||
username: data.username,
|
|
||||||
address: data.address,
|
|
||||||
email: data.email,
|
|
||||||
identityNumber: data.identity,
|
|
||||||
password: data.password,
|
|
||||||
passwordConf: data.confirmPassword,
|
|
||||||
isDefault: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
loading();
|
|
||||||
const response = await saveUserInternal(req);
|
|
||||||
|
|
||||||
if (response?.error) {
|
|
||||||
error(response.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
close();
|
|
||||||
MySwal.fire({
|
|
||||||
title: "Sukses",
|
|
||||||
icon: "success",
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
confirmButtonText: "Oke",
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
router.push("/admin/management-user");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
|
||||||
MySwal.fire({
|
|
||||||
title: "Simpan Data?",
|
|
||||||
text: "",
|
|
||||||
icon: "warning",
|
|
||||||
showCancelButton: true,
|
|
||||||
cancelButtonColor: "#d33",
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
confirmButtonText: "Simpan",
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
save(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
className="space-y-6 bg-white p-10 w-full"
|
|
||||||
>
|
|
||||||
<p className="text-xl">Data Pengelola Media Hub</p>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="fullname"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Nama Lengkap</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
placeholder="Masukkan nama lengkap"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="username"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Username</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
placeholder="Masukkan username"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="identity"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Nomor Identitas</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="Masukkan nomor identitas"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="address"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Alamat</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea
|
|
||||||
placeholder="Masukkan alamat"
|
|
||||||
className="resize-none w-1/2"
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="email"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Email</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="email"
|
|
||||||
placeholder="Masukkan email"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="phoneNumber"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>No. Handphone</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="Masukkan nomor handphone"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="password"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Password</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="password"
|
|
||||||
placeholder="Masukkan kata sandi"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="confirmPassword"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Konfirmasi Kata Sandi</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="password"
|
|
||||||
placeholder="Masukkan kata sandi"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<PasswordChecklist
|
|
||||||
rules={["minLength", "specialChar", "number", "capital", "match"]}
|
|
||||||
minLength={8}
|
|
||||||
value={passwordVal}
|
|
||||||
valueAgain={confPasswordVal}
|
|
||||||
onChange={(isValid) => {
|
|
||||||
form.setValue("isValidPassword", isValid);
|
|
||||||
}}
|
|
||||||
className="text-sm"
|
|
||||||
messages={{
|
|
||||||
minLength: "Password harus lebih dari 8 karakter",
|
|
||||||
specialChar: "Password harus memiliki spesial karakter",
|
|
||||||
number: "Password harus memiliki angka",
|
|
||||||
capital: "Password harus memiliki huruf kapital",
|
|
||||||
match: "Password sama",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Link href="/admin/management-user">
|
|
||||||
<Button type="button" color="primary" variant="outline">
|
|
||||||
Back
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Button type="submit" color="primary" className="mx-3">
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,756 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { Check, ChevronsUpDown } from "lucide-react";
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import {
|
|
||||||
Command,
|
|
||||||
CommandEmpty,
|
|
||||||
CommandGroup,
|
|
||||||
CommandInput,
|
|
||||||
CommandItem,
|
|
||||||
CommandList,
|
|
||||||
} from "@/components/ui/command";
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
import {
|
|
||||||
AdministrationLevelList,
|
|
||||||
getListCompetencies,
|
|
||||||
getListEducation,
|
|
||||||
getListSchools,
|
|
||||||
saveUserInternal,
|
|
||||||
} from "@/service/management-user/management-user";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/components/ui/select";
|
|
||||||
import Swal from "sweetalert2";
|
|
||||||
import withReactContent from "sweetalert2-react-content";
|
|
||||||
import { close, error, loading } from "@/config/swal";
|
|
||||||
|
|
||||||
const PasswordChecklist = dynamic(() => import("react-password-checklist"), {
|
|
||||||
ssr: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const sns = [
|
|
||||||
{
|
|
||||||
key: 1,
|
|
||||||
id: "comment",
|
|
||||||
typeId: 1,
|
|
||||||
name: "Komentar Konten",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 2,
|
|
||||||
id: "fb",
|
|
||||||
typeId: 2,
|
|
||||||
name: "Facebook",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 3,
|
|
||||||
id: "ig",
|
|
||||||
typeId: 3,
|
|
||||||
name: "Instagram",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 4,
|
|
||||||
id: "twt",
|
|
||||||
typeId: 4,
|
|
||||||
name: "Twitter",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 5,
|
|
||||||
id: "yt",
|
|
||||||
typeId: 5,
|
|
||||||
name: "Youtube",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 6,
|
|
||||||
id: "emergency",
|
|
||||||
typeId: 6,
|
|
||||||
name: "Emergency Issue",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 7,
|
|
||||||
id: "email",
|
|
||||||
typeId: 7,
|
|
||||||
name: "Email",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 8,
|
|
||||||
id: "inbox",
|
|
||||||
typeId: 8,
|
|
||||||
name: "Pesan Masuk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 9,
|
|
||||||
id: "whatsapp",
|
|
||||||
typeId: 9,
|
|
||||||
name: "Whatssapp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 10,
|
|
||||||
id: "tiktok",
|
|
||||||
typeId: 10,
|
|
||||||
name: "Tiktok",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
interface RoleData {
|
|
||||||
id: number;
|
|
||||||
label: string;
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
levelNumber: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
level: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
fullname: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
username: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
role: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
nrp: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
address: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
email: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
phoneNumber: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
password: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
confirmPassword: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
isValidPassword: z.boolean().refine((val) => val === true, {
|
|
||||||
message: "Check Password",
|
|
||||||
}),
|
|
||||||
sns: z.array(z.string()).optional(),
|
|
||||||
education: z.string().optional(),
|
|
||||||
school: z.string().optional(),
|
|
||||||
competency: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function CreateUserForm() {
|
|
||||||
const router = useRouter();
|
|
||||||
const MySwal = withReactContent(Swal);
|
|
||||||
const levelName = getCookiesDecrypt("ulnae");
|
|
||||||
const [roleList, setRoleList] = useState<RoleData[]>([]);
|
|
||||||
|
|
||||||
const [userEducations, setUserEducations] = useState<any>();
|
|
||||||
const [userSchools, setUserSchools] = useState<any>();
|
|
||||||
const [userCompetencies, setUserCompetencies] = useState<any>();
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
defaultValues: {
|
|
||||||
password: "",
|
|
||||||
confirmPassword: "",
|
|
||||||
sns: [],
|
|
||||||
education: "1",
|
|
||||||
school: "4",
|
|
||||||
competency: "2",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const passwordVal = form.watch("password");
|
|
||||||
const confPasswordVal = form.watch("confirmPassword");
|
|
||||||
const selectedRole = form.watch("role");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
initFetch();
|
|
||||||
getDataAdditional();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const initFetch = async () => {
|
|
||||||
const response = await AdministrationLevelList();
|
|
||||||
const res = response?.data?.data;
|
|
||||||
var levelsArr: RoleData[] = [];
|
|
||||||
res.forEach((levels: RoleData) => {
|
|
||||||
levelsArr.push({
|
|
||||||
id: levels.id,
|
|
||||||
label: levels.name,
|
|
||||||
name: levels.name,
|
|
||||||
value: String(levels.id),
|
|
||||||
levelNumber: levels.levelNumber,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
setRoleList(levelsArr);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getDataAdditional() {
|
|
||||||
const resEducations = await getListEducation();
|
|
||||||
setUserEducations(resEducations?.data?.data);
|
|
||||||
const resSchools = await getListSchools();
|
|
||||||
setUserSchools(resSchools?.data?.data);
|
|
||||||
const resCompetencies = await getListCompetencies();
|
|
||||||
setUserCompetencies(resCompetencies?.data?.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
const roles =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
id: "ADM-ID",
|
|
||||||
name: "Admin",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "EXE-ID",
|
|
||||||
name: "Eksekutif",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "APP-ID",
|
|
||||||
name: "Approver",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "CON-ID",
|
|
||||||
name: "Kontributor",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "SPV-ID",
|
|
||||||
name: "Supervisor Feedback Center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "OPT-ID",
|
|
||||||
name: "Operator Feedback Center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "KKUR-ID",
|
|
||||||
name: "Koor Kurator",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "KUR-ID",
|
|
||||||
name: "Kurator",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
{
|
|
||||||
id: "APP-ID",
|
|
||||||
name: "Approver",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "CON-ID",
|
|
||||||
name: "Kontributor",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
async function save(data: z.infer<typeof FormSchema>) {
|
|
||||||
let req: any = {
|
|
||||||
firstName: data.fullname,
|
|
||||||
username: data.username,
|
|
||||||
roleId: data.role,
|
|
||||||
userLevelId: Number(data.level),
|
|
||||||
memberIdentity: data.nrp,
|
|
||||||
address: data.address,
|
|
||||||
email: data.email,
|
|
||||||
phoneNumber: data.phoneNumber,
|
|
||||||
password: data.password,
|
|
||||||
passwordConf: data.confirmPassword,
|
|
||||||
isDefault: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (data.role == "OPT-ID") {
|
|
||||||
req.handledSocialMedia = data?.sns ? data.sns.join(",") : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.role == "KUR-ID") {
|
|
||||||
req.userEducationId = Number(data.education);
|
|
||||||
req.userSchoolsId = Number(data.school);
|
|
||||||
req.userCompetencyId = Number(data.competency);
|
|
||||||
}
|
|
||||||
|
|
||||||
loading();
|
|
||||||
const response = await saveUserInternal(req);
|
|
||||||
|
|
||||||
if (response?.error) {
|
|
||||||
error(response.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
close();
|
|
||||||
MySwal.fire({
|
|
||||||
title: "Sukses",
|
|
||||||
icon: "success",
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
confirmButtonText: "Oke",
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
router.push("/admin/management-user");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
|
||||||
MySwal.fire({
|
|
||||||
title: "Simpan Data?",
|
|
||||||
text: "",
|
|
||||||
icon: "warning",
|
|
||||||
showCancelButton: true,
|
|
||||||
cancelButtonColor: "#d33",
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
confirmButtonText: "Simpan",
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
save(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
className="space-y-6 bg-white p-10 w-full"
|
|
||||||
>
|
|
||||||
<p className="text-xl">Data Pengelola Media Hub</p>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="level"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex flex-col">
|
|
||||||
<FormLabel>Pilih Level</FormLabel>
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<FormControl>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
role="combobox"
|
|
||||||
className={cn(
|
|
||||||
"w-[400px] justify-between",
|
|
||||||
!field.value && "text-muted-foreground"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{field.value
|
|
||||||
? roleList.find((role) => role.value === field.value)
|
|
||||||
?.label
|
|
||||||
: "Pilih level"}
|
|
||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
||||||
</Button>
|
|
||||||
</FormControl>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-[400px] p-0">
|
|
||||||
<Command>
|
|
||||||
<CommandInput />
|
|
||||||
<CommandList>
|
|
||||||
<CommandEmpty>No role found.</CommandEmpty>
|
|
||||||
<CommandGroup>
|
|
||||||
{roleList.map((role) => (
|
|
||||||
<CommandItem
|
|
||||||
value={role.label}
|
|
||||||
key={role.value}
|
|
||||||
onSelect={() => {
|
|
||||||
form.setValue("level", role.value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{role.label}
|
|
||||||
<Check
|
|
||||||
className={cn(
|
|
||||||
"ml-auto",
|
|
||||||
role.value === field.value
|
|
||||||
? "opacity-100"
|
|
||||||
: "opacity-0"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</CommandItem>
|
|
||||||
))}
|
|
||||||
</CommandGroup>
|
|
||||||
</CommandList>
|
|
||||||
</Command>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="fullname"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Nama Lengkap</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
placeholder="Masukkan nama lengkap"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="username"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Username</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
placeholder="Masukkan username"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="role"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="space-y-3">
|
|
||||||
<FormLabel>Role</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<RadioGroup
|
|
||||||
onValueChange={field.onChange}
|
|
||||||
defaultValue={field.value}
|
|
||||||
className="flex flex-wrap gap-3 w-1/2"
|
|
||||||
>
|
|
||||||
{roles.map((role) => (
|
|
||||||
<FormItem
|
|
||||||
key={role.id}
|
|
||||||
className="flex items-center space-x-3 space-y-0"
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<RadioGroupItem value={role.id} />
|
|
||||||
</FormControl>
|
|
||||||
<FormLabel className="font-normal">
|
|
||||||
{role.name}
|
|
||||||
</FormLabel>
|
|
||||||
</FormItem>
|
|
||||||
))}
|
|
||||||
</RadioGroup>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{selectedRole === "OPT-ID" && (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="sns"
|
|
||||||
render={() => (
|
|
||||||
<FormItem>
|
|
||||||
<div className="mb-4">
|
|
||||||
<FormLabel>Social Media Yang Ditangani</FormLabel>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-5 gap-2 w-1/2">
|
|
||||||
{sns.map((item) => (
|
|
||||||
<FormField
|
|
||||||
key={item.id}
|
|
||||||
control={form.control}
|
|
||||||
name="sns"
|
|
||||||
render={({ field }) => {
|
|
||||||
return (
|
|
||||||
<FormItem
|
|
||||||
key={item.typeId}
|
|
||||||
className="flex flex-row items-start space-x-3 space-y-0"
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<Checkbox
|
|
||||||
checked={field.value?.includes(
|
|
||||||
String(item.typeId)
|
|
||||||
)}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
return checked
|
|
||||||
? field.onChange([
|
|
||||||
...(field.value || []),
|
|
||||||
String(item.typeId),
|
|
||||||
])
|
|
||||||
: field.onChange(
|
|
||||||
(field.value || []).filter(
|
|
||||||
(value) =>
|
|
||||||
value !== String(item.typeId)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormLabel className="font-normal">
|
|
||||||
{item.name}
|
|
||||||
</FormLabel>
|
|
||||||
</FormItem>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{selectedRole === "KUR-ID" && (
|
|
||||||
<>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="education"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Pendidikan Terakhir</FormLabel>
|
|
||||||
<Select onValueChange={field.onChange} value={field.value}>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{userEducations?.map((edu: any) => (
|
|
||||||
<SelectItem key={edu.id} value={String(edu.id)}>
|
|
||||||
{edu.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="school"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Universitas / Perguruan Tinggi</FormLabel>
|
|
||||||
<Select onValueChange={field.onChange} value={field.value}>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{userSchools?.map((edu: any) => (
|
|
||||||
<SelectItem key={edu.id} value={String(edu.id)}>
|
|
||||||
{edu.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="competency"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Kompetensi</FormLabel>
|
|
||||||
<Select onValueChange={field.onChange} value={field.value}>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{userCompetencies?.map((edu: any) => (
|
|
||||||
<SelectItem key={edu.id} value={String(edu.id)}>
|
|
||||||
{edu.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="nrp"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Nomor Regitrasi Polri {`(NRP)`}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="Masukkan NRP"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="address"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Alamat</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea
|
|
||||||
placeholder="Masukkan alamat"
|
|
||||||
className="resize-none w-1/2"
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="email"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Email</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="email"
|
|
||||||
placeholder="Masukkan email"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="phoneNumber"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>No. Handphone</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="Masukkan nomor handphone"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="password"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Password</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="password"
|
|
||||||
placeholder="Masukkan kata sandi"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="confirmPassword"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Konfirmasi Kata Sandi</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="password"
|
|
||||||
placeholder="Masukkan kata sandi"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<PasswordChecklist
|
|
||||||
rules={["minLength", "specialChar", "number", "capital", "match"]}
|
|
||||||
minLength={8}
|
|
||||||
value={passwordVal}
|
|
||||||
valueAgain={confPasswordVal}
|
|
||||||
onChange={(isValid) => {
|
|
||||||
form.setValue("isValidPassword", isValid);
|
|
||||||
}}
|
|
||||||
className="text-sm"
|
|
||||||
messages={{
|
|
||||||
minLength: "Password harus lebih dari 8 karakter",
|
|
||||||
specialChar: "Password harus memiliki spesial karakter",
|
|
||||||
number: "Password harus memiliki angka",
|
|
||||||
capital: "Password harus memiliki huruf kapital",
|
|
||||||
match: "Password sama",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Link href="/admin/management-user">
|
|
||||||
<Button type="button" color="primary" variant="outline">
|
|
||||||
Back
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Button type="submit" color="primary" className="mx-3">
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,743 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { Check, ChevronsUpDown } from "lucide-react";
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import {
|
|
||||||
Command,
|
|
||||||
CommandEmpty,
|
|
||||||
CommandGroup,
|
|
||||||
CommandInput,
|
|
||||||
CommandItem,
|
|
||||||
CommandList,
|
|
||||||
} from "@/components/ui/command";
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
import {
|
|
||||||
AdministrationLevelList,
|
|
||||||
getListCompetencies,
|
|
||||||
getListEducation,
|
|
||||||
getListSchools,
|
|
||||||
getUserById,
|
|
||||||
saveUserInternal,
|
|
||||||
} from "@/service/management-user/management-user";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/components/ui/select";
|
|
||||||
import Swal from "sweetalert2";
|
|
||||||
import withReactContent from "sweetalert2-react-content";
|
|
||||||
import { close, error, loading } from "@/config/swal";
|
|
||||||
import { useParams } from "next/navigation";
|
|
||||||
|
|
||||||
const sns = [
|
|
||||||
{
|
|
||||||
key: 1,
|
|
||||||
id: "comment",
|
|
||||||
typeId: 1,
|
|
||||||
name: "Komentar Konten",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 2,
|
|
||||||
id: "fb",
|
|
||||||
typeId: 2,
|
|
||||||
name: "Facebook",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 3,
|
|
||||||
id: "ig",
|
|
||||||
typeId: 3,
|
|
||||||
name: "Instagram",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 4,
|
|
||||||
id: "twt",
|
|
||||||
typeId: 4,
|
|
||||||
name: "Twitter",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 5,
|
|
||||||
id: "yt",
|
|
||||||
typeId: 5,
|
|
||||||
name: "Youtube",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 6,
|
|
||||||
id: "emergency",
|
|
||||||
typeId: 6,
|
|
||||||
name: "Emergency Issue",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 7,
|
|
||||||
id: "email",
|
|
||||||
typeId: 7,
|
|
||||||
name: "Email",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 8,
|
|
||||||
id: "inbox",
|
|
||||||
typeId: 8,
|
|
||||||
name: "Pesan Masuk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 9,
|
|
||||||
id: "whatsapp",
|
|
||||||
typeId: 9,
|
|
||||||
name: "Whatssapp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 10,
|
|
||||||
id: "tiktok",
|
|
||||||
typeId: 10,
|
|
||||||
name: "Tiktok",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
interface RoleData {
|
|
||||||
id: number;
|
|
||||||
label: string;
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
levelNumber: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
level: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
fullname: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
username: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
role: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
nrp: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
address: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
email: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
phoneNumber: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
password: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
confirmPassword: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
isValidPassword: z.boolean().refine((val) => val === true, {
|
|
||||||
message: "Check Password",
|
|
||||||
}),
|
|
||||||
sns: z.array(z.string()).optional(),
|
|
||||||
education: z.string().optional(),
|
|
||||||
school: z.string().optional(),
|
|
||||||
competency: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function DetailUserForm() {
|
|
||||||
const router = useRouter();
|
|
||||||
const params = useParams();
|
|
||||||
const id = params?.id;
|
|
||||||
const MySwal = withReactContent(Swal);
|
|
||||||
const levelName = getCookiesDecrypt("ulnae");
|
|
||||||
const [roleList, setRoleList] = useState<RoleData[]>([]);
|
|
||||||
|
|
||||||
const [userEducations, setUserEducations] = useState<any>();
|
|
||||||
const [userSchools, setUserSchools] = useState<any>();
|
|
||||||
const [userCompetencies, setUserCompetencies] = useState<any>();
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
defaultValues: {
|
|
||||||
password: "",
|
|
||||||
confirmPassword: "",
|
|
||||||
sns: [],
|
|
||||||
education: "1",
|
|
||||||
school: "4",
|
|
||||||
competency: "2",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const passwordVal = form.watch("password");
|
|
||||||
const confPasswordVal = form.watch("confirmPassword");
|
|
||||||
const selectedRole = form.watch("role");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getDataAdditional();
|
|
||||||
initData();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const initData = async () => {
|
|
||||||
loading();
|
|
||||||
const response = await getUserById(String(id));
|
|
||||||
const res = response?.data?.data;
|
|
||||||
close();
|
|
||||||
console.log("res", res);
|
|
||||||
if (Number(res.roleId) > 4) {
|
|
||||||
form.setValue("fullname", res?.fullname);
|
|
||||||
form.setValue("username", res?.username);
|
|
||||||
form.setValue("phoneNumber", res?.phoneNumber);
|
|
||||||
form.setValue("nrp", res?.memberIdentity);
|
|
||||||
form.setValue("address", res?.address);
|
|
||||||
form.setValue("email", res?.email);
|
|
||||||
form.setValue("role", res?.role?.code);
|
|
||||||
form.setValue("level", String(res?.userLevelId));
|
|
||||||
} else {
|
|
||||||
initFetch();
|
|
||||||
console.log("sadad", res?.role?.code);
|
|
||||||
form.setValue("fullname", res?.fullname);
|
|
||||||
form.setValue("username", res?.username);
|
|
||||||
form.setValue("phoneNumber", res?.phoneNumber);
|
|
||||||
form.setValue("nrp", res?.memberIdentity);
|
|
||||||
form.setValue("address", res?.address);
|
|
||||||
form.setValue("email", res?.email);
|
|
||||||
form.setValue("role", res?.role?.code);
|
|
||||||
form.setValue("level", String(res?.userLevelId));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const initFetch = async () => {
|
|
||||||
const response = await AdministrationLevelList();
|
|
||||||
const res = response?.data?.data;
|
|
||||||
var levelsArr: RoleData[] = [];
|
|
||||||
res.forEach((levels: RoleData) => {
|
|
||||||
levelsArr.push({
|
|
||||||
id: levels.id,
|
|
||||||
label: levels.name,
|
|
||||||
name: levels.name,
|
|
||||||
value: String(levels.id),
|
|
||||||
levelNumber: levels.levelNumber,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
setRoleList(levelsArr);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getDataAdditional() {
|
|
||||||
const resEducations = await getListEducation();
|
|
||||||
setUserEducations(resEducations?.data?.data);
|
|
||||||
const resSchools = await getListSchools();
|
|
||||||
setUserSchools(resSchools?.data?.data);
|
|
||||||
const resCompetencies = await getListCompetencies();
|
|
||||||
setUserCompetencies(resCompetencies?.data?.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
const roles =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
id: "ADM-ID",
|
|
||||||
name: "Admin",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "APP-ID",
|
|
||||||
name: "Approver",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "CON-ID",
|
|
||||||
name: "Kontributor",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "SPV-ID",
|
|
||||||
name: "Supervisor Feedback Center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "OPT-ID",
|
|
||||||
name: "Operator Feedback Center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "KKUR-ID",
|
|
||||||
name: "Koor Kurator",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "KUR-ID",
|
|
||||||
name: "Kurator",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
{
|
|
||||||
id: "APP-ID",
|
|
||||||
name: "Approver",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "CON-ID",
|
|
||||||
name: "Kontributor",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
async function save(data: z.infer<typeof FormSchema>) {
|
|
||||||
let req: any = {
|
|
||||||
id: id,
|
|
||||||
firstName: data.fullname,
|
|
||||||
username: data.username,
|
|
||||||
roleId: data.role,
|
|
||||||
userLevelId: Number(data.level),
|
|
||||||
memberIdentity: data.nrp,
|
|
||||||
address: data.address,
|
|
||||||
email: data.email,
|
|
||||||
password: data.password,
|
|
||||||
passwordConf: data.confirmPassword,
|
|
||||||
isDefault: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (data.role == "OPT-ID") {
|
|
||||||
req.handledSocialMedia = data?.sns ? data.sns.join(",") : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.role == "KUR-ID") {
|
|
||||||
req.userEducationId = Number(data.education);
|
|
||||||
req.userSchoolsId = Number(data.school);
|
|
||||||
req.userCompetencyId = Number(data.competency);
|
|
||||||
}
|
|
||||||
|
|
||||||
loading();
|
|
||||||
const response = await saveUserInternal(req);
|
|
||||||
|
|
||||||
if (response?.error) {
|
|
||||||
error(response.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
close();
|
|
||||||
MySwal.fire({
|
|
||||||
title: "Sukses",
|
|
||||||
icon: "success",
|
|
||||||
showCancelButton: true,
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
confirmButtonText: "Simpan",
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
router.push("/admin/management-user");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
|
||||||
MySwal.fire({
|
|
||||||
title: "Simpan Data?",
|
|
||||||
text: "",
|
|
||||||
icon: "warning",
|
|
||||||
showCancelButton: true,
|
|
||||||
cancelButtonColor: "#d33",
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
confirmButtonText: "Simpan",
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
save(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
className="space-y-6 bg-white p-10 w-full"
|
|
||||||
>
|
|
||||||
<p className="text-xl">Data Pengelola Media Hub</p>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="level"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex flex-col">
|
|
||||||
<FormLabel>Pilih Level</FormLabel>
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild disabled>
|
|
||||||
<FormControl>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
role="combobox"
|
|
||||||
className={cn(
|
|
||||||
"w-[400px] justify-between",
|
|
||||||
!field.value && "text-muted-foreground"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{field.value
|
|
||||||
? roleList.find((role) => role.value === field.value)
|
|
||||||
?.label
|
|
||||||
: "Pilih level"}
|
|
||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
||||||
</Button>
|
|
||||||
</FormControl>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-[400px] p-0">
|
|
||||||
<Command>
|
|
||||||
<CommandInput />
|
|
||||||
<CommandList>
|
|
||||||
<CommandEmpty>No role found.</CommandEmpty>
|
|
||||||
<CommandGroup>
|
|
||||||
{roleList.map((role) => (
|
|
||||||
<CommandItem
|
|
||||||
value={role.label}
|
|
||||||
key={role.value}
|
|
||||||
onSelect={() => {
|
|
||||||
form.setValue("level", role.value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{role.label}
|
|
||||||
<Check
|
|
||||||
className={cn(
|
|
||||||
"ml-auto",
|
|
||||||
role.value === field.value
|
|
||||||
? "opacity-100"
|
|
||||||
: "opacity-0"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</CommandItem>
|
|
||||||
))}
|
|
||||||
</CommandGroup>
|
|
||||||
</CommandList>
|
|
||||||
</Command>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="fullname"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Nama Lengkap</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
placeholder="Masukkan nama lengkap"
|
|
||||||
{...field}
|
|
||||||
readOnly
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="username"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Username</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
placeholder="Masukkan username"
|
|
||||||
{...field}
|
|
||||||
readOnly
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="role"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="space-y-3">
|
|
||||||
<FormLabel>Role</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<RadioGroup
|
|
||||||
onValueChange={field.onChange}
|
|
||||||
value={field.value}
|
|
||||||
className="flex flex-wrap gap-3 w-1/2"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
{roles.map((role) => (
|
|
||||||
<FormItem
|
|
||||||
key={role.id}
|
|
||||||
className="flex items-center space-x-3 space-y-0"
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<RadioGroupItem value={role.id} />
|
|
||||||
</FormControl>
|
|
||||||
<FormLabel className="font-normal">
|
|
||||||
{role.name}
|
|
||||||
</FormLabel>
|
|
||||||
</FormItem>
|
|
||||||
))}
|
|
||||||
</RadioGroup>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{selectedRole === "OPT-ID" && (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="sns"
|
|
||||||
render={() => (
|
|
||||||
<FormItem>
|
|
||||||
<div className="mb-4">
|
|
||||||
<FormLabel>Social Media Yang Ditangani</FormLabel>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-5 gap-2 w-1/2">
|
|
||||||
{sns.map((item) => (
|
|
||||||
<FormField
|
|
||||||
key={item.id}
|
|
||||||
control={form.control}
|
|
||||||
name="sns"
|
|
||||||
render={({ field }) => {
|
|
||||||
return (
|
|
||||||
<FormItem
|
|
||||||
key={item.typeId}
|
|
||||||
className="flex flex-row items-start space-x-3 space-y-0"
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<Checkbox
|
|
||||||
disabled
|
|
||||||
checked={field.value?.includes(
|
|
||||||
String(item.typeId)
|
|
||||||
)}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
return checked
|
|
||||||
? field.onChange([
|
|
||||||
...(field.value || []),
|
|
||||||
String(item.typeId),
|
|
||||||
])
|
|
||||||
: field.onChange(
|
|
||||||
(field.value || []).filter(
|
|
||||||
(value) =>
|
|
||||||
value !== String(item.typeId)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormLabel className="font-normal">
|
|
||||||
{item.name}
|
|
||||||
</FormLabel>
|
|
||||||
</FormItem>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{selectedRole === "KUR-ID" && (
|
|
||||||
<>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="education"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Pendidikan Terakhir</FormLabel>
|
|
||||||
<Select
|
|
||||||
onValueChange={field.onChange}
|
|
||||||
value={field.value}
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{userEducations?.map((edu: any) => (
|
|
||||||
<SelectItem key={edu.id} value={String(edu.id)}>
|
|
||||||
{edu.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="school"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Universitas / Perguruan Tinggi</FormLabel>
|
|
||||||
<Select
|
|
||||||
onValueChange={field.onChange}
|
|
||||||
value={field.value}
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{userSchools?.map((edu: any) => (
|
|
||||||
<SelectItem key={edu.id} value={String(edu.id)}>
|
|
||||||
{edu.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="competency"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Kompetensi</FormLabel>
|
|
||||||
<Select
|
|
||||||
onValueChange={field.onChange}
|
|
||||||
value={field.value}
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{userCompetencies?.map((edu: any) => (
|
|
||||||
<SelectItem key={edu.id} value={String(edu.id)}>
|
|
||||||
{edu.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="nrp"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Nomor Regitrasi Polri {`(NRP)`}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="Masukkan NRP"
|
|
||||||
readOnly
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="address"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Alamat</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea
|
|
||||||
placeholder="Masukkan alamat"
|
|
||||||
readOnly
|
|
||||||
className="resize-none w-1/2"
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="email"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Email</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="email"
|
|
||||||
readOnly
|
|
||||||
placeholder="Masukkan email"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="phoneNumber"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>No. Handphone</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="Masukkan nomor handphone"
|
|
||||||
{...field}
|
|
||||||
readOnly
|
|
||||||
className="w-1/2 mb-2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Link href="/admin/management-user">
|
|
||||||
<Button type="button" color="primary" variant="outline">
|
|
||||||
Back
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,784 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { Check, ChevronsUpDown } from "lucide-react";
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import {
|
|
||||||
Command,
|
|
||||||
CommandEmpty,
|
|
||||||
CommandGroup,
|
|
||||||
CommandInput,
|
|
||||||
CommandItem,
|
|
||||||
CommandList,
|
|
||||||
} from "@/components/ui/command";
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
import {
|
|
||||||
AdministrationLevelList,
|
|
||||||
getListCompetencies,
|
|
||||||
getListEducation,
|
|
||||||
getListSchools,
|
|
||||||
getUserById,
|
|
||||||
saveUserInternal,
|
|
||||||
} from "@/service/management-user/management-user";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/components/ui/select";
|
|
||||||
import Swal from "sweetalert2";
|
|
||||||
import withReactContent from "sweetalert2-react-content";
|
|
||||||
import { close, error, loading } from "@/config/swal";
|
|
||||||
import { useParams } from "next/navigation";
|
|
||||||
|
|
||||||
const PasswordChecklist = dynamic(() => import("react-password-checklist"), {
|
|
||||||
ssr: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const sns = [
|
|
||||||
{
|
|
||||||
key: 1,
|
|
||||||
id: "comment",
|
|
||||||
typeId: 1,
|
|
||||||
name: "Komentar Konten",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 2,
|
|
||||||
id: "fb",
|
|
||||||
typeId: 2,
|
|
||||||
name: "Facebook",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 3,
|
|
||||||
id: "ig",
|
|
||||||
typeId: 3,
|
|
||||||
name: "Instagram",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 4,
|
|
||||||
id: "twt",
|
|
||||||
typeId: 4,
|
|
||||||
name: "Twitter",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 5,
|
|
||||||
id: "yt",
|
|
||||||
typeId: 5,
|
|
||||||
name: "Youtube",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 6,
|
|
||||||
id: "emergency",
|
|
||||||
typeId: 6,
|
|
||||||
name: "Emergency Issue",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 7,
|
|
||||||
id: "email",
|
|
||||||
typeId: 7,
|
|
||||||
name: "Email",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 8,
|
|
||||||
id: "inbox",
|
|
||||||
typeId: 8,
|
|
||||||
name: "Pesan Masuk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 9,
|
|
||||||
id: "whatsapp",
|
|
||||||
typeId: 9,
|
|
||||||
name: "Whatssapp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 10,
|
|
||||||
id: "tiktok",
|
|
||||||
typeId: 10,
|
|
||||||
name: "Tiktok",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
interface RoleData {
|
|
||||||
id: number;
|
|
||||||
label: string;
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
levelNumber: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
level: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
fullname: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
username: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
role: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
nrp: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
address: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
email: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
phoneNumber: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
password: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
confirmPassword: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
isValidPassword: z.boolean().refine((val) => val === true, {
|
|
||||||
message: "Check Password",
|
|
||||||
}),
|
|
||||||
sns: z.array(z.string()).optional(),
|
|
||||||
education: z.string().optional(),
|
|
||||||
school: z.string().optional(),
|
|
||||||
competency: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function EditUserForm() {
|
|
||||||
const router = useRouter();
|
|
||||||
const params = useParams();
|
|
||||||
const id = params?.id;
|
|
||||||
const MySwal = withReactContent(Swal);
|
|
||||||
const levelName = getCookiesDecrypt("ulnae");
|
|
||||||
const [roleList, setRoleList] = useState<RoleData[]>([]);
|
|
||||||
|
|
||||||
const [userEducations, setUserEducations] = useState<any>();
|
|
||||||
const [userSchools, setUserSchools] = useState<any>();
|
|
||||||
const [userCompetencies, setUserCompetencies] = useState<any>();
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
defaultValues: {
|
|
||||||
password: "",
|
|
||||||
confirmPassword: "",
|
|
||||||
sns: [],
|
|
||||||
education: "1",
|
|
||||||
school: "4",
|
|
||||||
competency: "2",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const passwordVal = form.watch("password");
|
|
||||||
const confPasswordVal = form.watch("confirmPassword");
|
|
||||||
const selectedRole = form.watch("role");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getDataAdditional();
|
|
||||||
initData();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const initData = async () => {
|
|
||||||
loading();
|
|
||||||
const response = await getUserById(String(id));
|
|
||||||
const res = response?.data?.data;
|
|
||||||
close();
|
|
||||||
console.log("res", res);
|
|
||||||
if (Number(res.roleId) > 4) {
|
|
||||||
form.setValue("fullname", res?.fullname);
|
|
||||||
form.setValue("username", res?.username);
|
|
||||||
form.setValue("phoneNumber", res?.phoneNumber);
|
|
||||||
form.setValue("nrp", res?.memberIdentity);
|
|
||||||
form.setValue("address", res?.address);
|
|
||||||
form.setValue("email", res?.email);
|
|
||||||
form.setValue("role", res?.role?.code);
|
|
||||||
form.setValue("level", String(res?.userLevelId));
|
|
||||||
} else {
|
|
||||||
initFetch();
|
|
||||||
console.log("sadad", res?.role?.code);
|
|
||||||
form.setValue("fullname", res?.fullname);
|
|
||||||
form.setValue("username", res?.username);
|
|
||||||
form.setValue("phoneNumber", res?.phoneNumber);
|
|
||||||
form.setValue("nrp", res?.memberIdentity);
|
|
||||||
form.setValue("address", res?.address);
|
|
||||||
form.setValue("email", res?.email);
|
|
||||||
form.setValue("role", res?.role?.code);
|
|
||||||
form.setValue("level", String(res?.userLevelId));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const initFetch = async () => {
|
|
||||||
const response = await AdministrationLevelList();
|
|
||||||
const res = response?.data?.data;
|
|
||||||
var levelsArr: RoleData[] = [];
|
|
||||||
res.forEach((levels: RoleData) => {
|
|
||||||
levelsArr.push({
|
|
||||||
id: levels.id,
|
|
||||||
label: levels.name,
|
|
||||||
name: levels.name,
|
|
||||||
value: String(levels.id),
|
|
||||||
levelNumber: levels.levelNumber,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
setRoleList(levelsArr);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getDataAdditional() {
|
|
||||||
const resEducations = await getListEducation();
|
|
||||||
setUserEducations(resEducations?.data?.data);
|
|
||||||
const resSchools = await getListSchools();
|
|
||||||
setUserSchools(resSchools?.data?.data);
|
|
||||||
const resCompetencies = await getListCompetencies();
|
|
||||||
setUserCompetencies(resCompetencies?.data?.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
const roles =
|
|
||||||
levelName == "MABES POLRI"
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
id: "ADM-ID",
|
|
||||||
name: "Admin",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "APP-ID",
|
|
||||||
name: "Approver",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "CON-ID",
|
|
||||||
name: "Kontributor",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "SPV-ID",
|
|
||||||
name: "Supervisor Feedback Center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "OPT-ID",
|
|
||||||
name: "Operator Feedback Center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "KKUR-ID",
|
|
||||||
name: "Koor Kurator",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "KUR-ID",
|
|
||||||
name: "Kurator",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
{
|
|
||||||
id: "APP-ID",
|
|
||||||
name: "Approver",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "CON-ID",
|
|
||||||
name: "Kontributor",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
async function save(data: z.infer<typeof FormSchema>) {
|
|
||||||
let req: any = {
|
|
||||||
id: id,
|
|
||||||
firstName: data.fullname,
|
|
||||||
username: data.username,
|
|
||||||
roleId: data.role,
|
|
||||||
userLevelId: Number(data.level),
|
|
||||||
memberIdentity: data.nrp,
|
|
||||||
address: data.address,
|
|
||||||
email: data.email,
|
|
||||||
password: data.password,
|
|
||||||
passwordConf: data.confirmPassword,
|
|
||||||
isDefault: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (data.role == "OPT-ID") {
|
|
||||||
req.handledSocialMedia = data?.sns ? data.sns.join(",") : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.role == "KUR-ID") {
|
|
||||||
req.userEducationId = Number(data.education);
|
|
||||||
req.userSchoolsId = Number(data.school);
|
|
||||||
req.userCompetencyId = Number(data.competency);
|
|
||||||
}
|
|
||||||
|
|
||||||
loading();
|
|
||||||
const response = await saveUserInternal(req);
|
|
||||||
|
|
||||||
if (response?.error) {
|
|
||||||
error(response.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
close();
|
|
||||||
MySwal.fire({
|
|
||||||
title: "Sukses",
|
|
||||||
icon: "success",
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
confirmButtonText: "Oke",
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
router.push("/admin/management-user");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
|
||||||
MySwal.fire({
|
|
||||||
title: "Simpan Data?",
|
|
||||||
text: "",
|
|
||||||
icon: "warning",
|
|
||||||
showCancelButton: true,
|
|
||||||
cancelButtonColor: "#d33",
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
confirmButtonText: "Simpan",
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
save(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
className="space-y-6 bg-white p-10 w-full"
|
|
||||||
>
|
|
||||||
<p className="text-xl">Data Pengelola Media Hub</p>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="level"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex flex-col">
|
|
||||||
<FormLabel>Pilih Level</FormLabel>
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<FormControl>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
role="combobox"
|
|
||||||
className={cn(
|
|
||||||
"w-[400px] justify-between",
|
|
||||||
!field.value && "text-muted-foreground"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{field.value
|
|
||||||
? roleList.find((role) => role.value === field.value)
|
|
||||||
?.label
|
|
||||||
: "Pilih level"}
|
|
||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
||||||
</Button>
|
|
||||||
</FormControl>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-[400px] p-0">
|
|
||||||
<Command>
|
|
||||||
<CommandInput />
|
|
||||||
<CommandList>
|
|
||||||
<CommandEmpty>No role found.</CommandEmpty>
|
|
||||||
<CommandGroup>
|
|
||||||
{roleList.map((role) => (
|
|
||||||
<CommandItem
|
|
||||||
value={role.label}
|
|
||||||
key={role.value}
|
|
||||||
onSelect={() => {
|
|
||||||
form.setValue("level", role.value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{role.label}
|
|
||||||
<Check
|
|
||||||
className={cn(
|
|
||||||
"ml-auto",
|
|
||||||
role.value === field.value
|
|
||||||
? "opacity-100"
|
|
||||||
: "opacity-0"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</CommandItem>
|
|
||||||
))}
|
|
||||||
</CommandGroup>
|
|
||||||
</CommandList>
|
|
||||||
</Command>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="fullname"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Nama Lengkap</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
placeholder="Masukkan nama lengkap"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="username"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Username</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
placeholder="Masukkan username"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="role"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="space-y-3">
|
|
||||||
<FormLabel>Role</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<RadioGroup
|
|
||||||
onValueChange={field.onChange}
|
|
||||||
value={field.value}
|
|
||||||
className="flex flex-wrap gap-3 w-1/2"
|
|
||||||
>
|
|
||||||
{roles.map((role) => (
|
|
||||||
<FormItem
|
|
||||||
key={role.id}
|
|
||||||
className="flex items-center space-x-3 space-y-0"
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<RadioGroupItem value={role.id} />
|
|
||||||
</FormControl>
|
|
||||||
<FormLabel className="font-normal">
|
|
||||||
{role.name}
|
|
||||||
</FormLabel>
|
|
||||||
</FormItem>
|
|
||||||
))}
|
|
||||||
</RadioGroup>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{selectedRole === "OPT-ID" && (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="sns"
|
|
||||||
render={() => (
|
|
||||||
<FormItem>
|
|
||||||
<div className="mb-4">
|
|
||||||
<FormLabel>Social Media Yang Ditangani</FormLabel>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-5 gap-2 w-1/2">
|
|
||||||
{sns.map((item) => (
|
|
||||||
<FormField
|
|
||||||
key={item.id}
|
|
||||||
control={form.control}
|
|
||||||
name="sns"
|
|
||||||
render={({ field }) => {
|
|
||||||
return (
|
|
||||||
<FormItem
|
|
||||||
key={item.typeId}
|
|
||||||
className="flex flex-row items-start space-x-3 space-y-0"
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<Checkbox
|
|
||||||
checked={field.value?.includes(
|
|
||||||
String(item.typeId)
|
|
||||||
)}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
return checked
|
|
||||||
? field.onChange([
|
|
||||||
...(field.value || []),
|
|
||||||
String(item.typeId),
|
|
||||||
])
|
|
||||||
: field.onChange(
|
|
||||||
(field.value || []).filter(
|
|
||||||
(value) =>
|
|
||||||
value !== String(item.typeId)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormLabel className="font-normal">
|
|
||||||
{item.name}
|
|
||||||
</FormLabel>
|
|
||||||
</FormItem>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{selectedRole === "KUR-ID" && (
|
|
||||||
<>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="education"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Pendidikan Terakhir</FormLabel>
|
|
||||||
<Select onValueChange={field.onChange} value={field.value}>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{userEducations?.map((edu: any) => (
|
|
||||||
<SelectItem key={edu.id} value={String(edu.id)}>
|
|
||||||
{edu.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="school"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Universitas / Perguruan Tinggi</FormLabel>
|
|
||||||
<Select onValueChange={field.onChange} value={field.value}>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{userSchools?.map((edu: any) => (
|
|
||||||
<SelectItem key={edu.id} value={String(edu.id)}>
|
|
||||||
{edu.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="competency"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Kompetensi</FormLabel>
|
|
||||||
<Select onValueChange={field.onChange} value={field.value}>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{userCompetencies?.map((edu: any) => (
|
|
||||||
<SelectItem key={edu.id} value={String(edu.id)}>
|
|
||||||
{edu.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="nrp"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Nomor Regitrasi Polri {`(NRP)`}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="Masukkan NRP"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="address"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Alamat</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea
|
|
||||||
placeholder="Masukkan alamat"
|
|
||||||
className="resize-none w-1/2"
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="email"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Email</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="email"
|
|
||||||
placeholder="Masukkan email"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="phoneNumber"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>No. Handphone</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="Masukkan nomor handphone"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="password"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Password</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="password"
|
|
||||||
placeholder="Masukkan kata sandi"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="confirmPassword"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Konfirmasi Kata Sandi</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="password"
|
|
||||||
placeholder="Masukkan kata sandi"
|
|
||||||
{...field}
|
|
||||||
className="w-1/2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<PasswordChecklist
|
|
||||||
rules={["minLength", "specialChar", "number", "capital", "match"]}
|
|
||||||
minLength={8}
|
|
||||||
value={passwordVal}
|
|
||||||
valueAgain={confPasswordVal}
|
|
||||||
onChange={(isValid) => {
|
|
||||||
form.setValue("isValidPassword", isValid);
|
|
||||||
}}
|
|
||||||
className="text-sm"
|
|
||||||
messages={{
|
|
||||||
minLength: "Password harus lebih dari 8 karakter",
|
|
||||||
specialChar: "Password harus memiliki spesial karakter",
|
|
||||||
number: "Password harus memiliki angka",
|
|
||||||
capital: "Password harus memiliki huruf kapital",
|
|
||||||
match: "Password sama",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Link href="/admin/management-user">
|
|
||||||
<Button type="button" color="primary" variant="outline">
|
|
||||||
Back
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Button type="submit" color="primary" className="mx-3">
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
"use client";
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import UserExternalTable from "@/components/table/management-user/management-user-external-table";
|
|
||||||
import UserInternalTable from "@/components/table/management-user/management-user-internal-table";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import ManagementUserVisualization from "@/components/visualization/management-user-viz";
|
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
|
||||||
import { PlusIcon } from "lucide-react";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import Cookies from "js-cookie";
|
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
|
||||||
|
|
||||||
export default function ManagementUser() {
|
|
||||||
const [isInternal, setIsInternal] = useState(true);
|
|
||||||
const [levelNumber, setLevelNumber] = useState<number | null>(null);
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const encryptedLevel = Cookies.get("ulne");
|
|
||||||
if (encryptedLevel) {
|
|
||||||
const decryptedLevel = getCookiesDecrypt("ulne");
|
|
||||||
setLevelNumber(Number(decryptedLevel));
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
router.push("?page=1");
|
|
||||||
}, [isInternal]);
|
|
||||||
|
|
||||||
const showExternalButton = levelNumber !== 2 && levelNumber !== 3;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<section id="viz">
|
|
||||||
<ManagementUserVisualization />
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="flex flex-col gap-2 bg-white rounded-lg p-3 mt-5">
|
|
||||||
<div className="flex justify-between py-3">
|
|
||||||
<p className="text-lg">
|
|
||||||
Data User {isInternal ? "Internal" : "Eksternal"}
|
|
||||||
</p>
|
|
||||||
{isInternal && (
|
|
||||||
<Link href="/admin/management-user/internal/create">
|
|
||||||
<Button color="primary" size="md">
|
|
||||||
<PlusIcon />
|
|
||||||
Add User
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
|
|
||||||
<Button
|
|
||||||
rounded="md"
|
|
||||||
onClick={() => setIsInternal(true)}
|
|
||||||
className={`hover:text-white ${
|
|
||||||
!isInternal ? "bg-white text-black" : "bg-black text-white"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
User Internal
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{showExternalButton && (
|
|
||||||
<Button
|
|
||||||
rounded="md"
|
|
||||||
onClick={() => setIsInternal(false)}
|
|
||||||
className={`hover:text-white ${
|
|
||||||
!isInternal ? "bg-black text-white" : "bg-white text-black"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
User Eksternal
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isInternal ? <UserInternalTable /> : <UserExternalTable />}
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
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: "date",
|
|
||||||
header: "Tanggal",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span>
|
|
||||||
{row.original.startDate.split(" ")[0]} -{" "}
|
|
||||||
{row.original.endDate.split(" ")[0]}
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "title",
|
|
||||||
header: "Judul Berita",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("title")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "pageUrl",
|
|
||||||
header: "Link Berita",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<a
|
|
||||||
className="cursor-pointer normal-case"
|
|
||||||
href={row.original.mediaUpload.pageUrl}
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{row.original.mediaUpload.pageUrl}
|
|
||||||
</a>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "actions",
|
|
||||||
accessorKey: "action",
|
|
||||||
header: "Aksi",
|
|
||||||
enableHiding: false,
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
|
||||||
>
|
|
||||||
<span className="sr-only">Open menu</span>
|
|
||||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
|
||||||
<Link href={`/admin/media-tracking/detail/${row.original.id}`}>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<Eye className="w-4 h-4 me-1.5" />
|
|
||||||
View
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -1,390 +0,0 @@
|
||||||
"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 {
|
|
||||||
ChevronDown,
|
|
||||||
ChevronLeft,
|
|
||||||
ChevronRight,
|
|
||||||
Eye,
|
|
||||||
MoreVertical,
|
|
||||||
Search,
|
|
||||||
SquarePen,
|
|
||||||
Trash2,
|
|
||||||
TrendingDown,
|
|
||||||
TrendingUp,
|
|
||||||
UserIcon,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuCheckboxItem,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuRadioGroup,
|
|
||||||
DropdownMenuRadioItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
||||||
import TablePagination from "@/components/table/table-pagination";
|
|
||||||
import columns from "./column";
|
|
||||||
import { listEnableCategory } from "@/service/content/content";
|
|
||||||
import { close, loading } from "@/config/swal";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
DialogTrigger,
|
|
||||||
} from "@/components/ui/dialog";
|
|
||||||
import { link } from "fs";
|
|
||||||
import { listDataAll } from "@/service/landing/landing";
|
|
||||||
import SearchImageComponent from "@/components/form/media-tracking/search-image-card";
|
|
||||||
import SearchVideoComponent from "@/components/form/media-tracking/search-video-card";
|
|
||||||
import SearchDocumentComponent from "@/components/form/media-tracking/search-document-card";
|
|
||||||
import SearchAudioComponent from "@/components/form/media-tracking/search-audio-card";
|
|
||||||
import TrackingMediaModal from "./modal";
|
|
||||||
import { getMediaTracking } from "@/service/media-tracking/media-tracking";
|
|
||||||
import { group } from "console";
|
|
||||||
import router from "next/router";
|
|
||||||
import { title } from "process";
|
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
|
|
||||||
const NewsTable = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const asPath = usePathname();
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const [showData, setShowData] = React.useState("10");
|
|
||||||
const [categories, setCategories] = React.useState<any>();
|
|
||||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const [showTable, setShowTable] = React.useState(false);
|
|
||||||
const [onSearch, setOnSearch] = React.useState("");
|
|
||||||
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<any>([]);
|
|
||||||
const [searchTitle, setSearchTitle] = React.useState<string>("");
|
|
||||||
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
|
||||||
const [page, setPage] = React.useState(1);
|
|
||||||
const [imageData, setImageData] = React.useState<any>();
|
|
||||||
const [totalData, setTotalData] = React.useState<number>(1);
|
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
|
||||||
const [contentAll, setContentAll] = React.useState([]);
|
|
||||||
const [formatFilter, setFormatFilter] = React.useState<any>([]);
|
|
||||||
const [totalContent, setTotalContent] = React.useState();
|
|
||||||
const [search, setSearch] = React.useState<string>("");
|
|
||||||
const group = searchParams?.get("group");
|
|
||||||
const title = searchParams?.get("title");
|
|
||||||
const categorie = searchParams?.get("category");
|
|
||||||
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;
|
|
||||||
|
|
||||||
// async function doneTyping() {
|
|
||||||
// fetchData();
|
|
||||||
// }
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const pageFromUrl = searchParams?.get("page");
|
|
||||||
if (pageFromUrl) {
|
|
||||||
setPage(Number(pageFromUrl));
|
|
||||||
}
|
|
||||||
}, [searchParams]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
getCategories();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
getDataTable();
|
|
||||||
}, [page, showData, search]);
|
|
||||||
|
|
||||||
const getDataTable = async () => {
|
|
||||||
const res = await getMediaTracking(page - 1, search, showData);
|
|
||||||
|
|
||||||
const data = res?.data?.data;
|
|
||||||
const newData = data?.content;
|
|
||||||
|
|
||||||
newData.forEach((item: any, index: number) => {
|
|
||||||
item.no = (page - 1) * Number(showData) + index + 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
setDataTable(newData);
|
|
||||||
setTotalData(data?.totalElements);
|
|
||||||
setTotalPage(data?.totalPages);
|
|
||||||
setTotalContent(data.totalElements);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getCategories() {
|
|
||||||
const category = await listEnableCategory("");
|
|
||||||
const resCategory = category?.data?.data?.content;
|
|
||||||
setCategories(resCategory);
|
|
||||||
}
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (categorie) {
|
|
||||||
setCategoryFilter(
|
|
||||||
categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
"Kategori",
|
|
||||||
categorie,
|
|
||||||
categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [categorie]);
|
|
||||||
|
|
||||||
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 cleanCheckbox = () => {
|
|
||||||
setCategoryFilter([]);
|
|
||||||
setFormatFilter([]);
|
|
||||||
router.push(`?category=&title=`);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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, "");
|
|
||||||
// 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 handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setSearch(e.target.value);
|
|
||||||
table.getColumn("judul")?.setFilterValue(e.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
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="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3 border ">
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<p className="text-xl font-medium text-default-900">
|
|
||||||
Tracking Berita hari ini!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row lg:flex-row justify-between sm:items-center md:items-center lg:items-center px-1">
|
|
||||||
<TrackingMediaModal triggerFetch={() => getDataTable()} />
|
|
||||||
<div className=" flex flex-row items-center gap-3">
|
|
||||||
<div className="flex items-center py-2">
|
|
||||||
<div className="mx-3">
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button size="md" variant="outline">
|
|
||||||
1 - {showData} Data
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent className="w-56 text-sm">
|
|
||||||
<DropdownMenuRadioGroup
|
|
||||||
value={showData}
|
|
||||||
onValueChange={setShowData}
|
|
||||||
>
|
|
||||||
<DropdownMenuRadioItem value="10">
|
|
||||||
1 - 10 Data
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
<DropdownMenuRadioItem value="50">
|
|
||||||
1 - 50 Data
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
<DropdownMenuRadioItem value="100">
|
|
||||||
1 - 100 Data
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
<DropdownMenuRadioItem value="250">
|
|
||||||
1 - 250 Data
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
</DropdownMenuRadioGroup>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center py-4">
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button variant="outline" className="ml-auto" size="md">
|
|
||||||
Columns <ChevronDown />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="end">
|
|
||||||
{table
|
|
||||||
.getAllColumns()
|
|
||||||
.filter((column) => column.getCanHide())
|
|
||||||
.map((column) => {
|
|
||||||
return (
|
|
||||||
<DropdownMenuCheckboxItem
|
|
||||||
key={column.id}
|
|
||||||
className="capitalize"
|
|
||||||
checked={column.getIsVisible()}
|
|
||||||
onCheckedChange={(value) =>
|
|
||||||
column.toggleVisibility(!!value)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{column.id}
|
|
||||||
</DropdownMenuCheckboxItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
|
||||||
</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} 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 className="flex justify-end mt-4">
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
size="md"
|
|
||||||
className="text-sm mr-3"
|
|
||||||
onClick={() => setShowTable(false)}
|
|
||||||
>
|
|
||||||
Tracking Berita Baru
|
|
||||||
</Button>
|
|
||||||
</div> */}
|
|
||||||
{/* </>
|
|
||||||
)} */}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default NewsTable;
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import NewsTable from "../../component/table";
|
|
||||||
import NewsDetailTable from "../component/table";
|
|
||||||
|
|
||||||
export default function DetailNews() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<NewsDetailTable />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
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: "source",
|
|
||||||
header: "Media Online",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">{row.getValue("source")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "title",
|
|
||||||
header: "Judul Berita",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">{row.getValue("title")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "link",
|
|
||||||
header: "Link Berita",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">{row.getValue("link")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -1,260 +0,0 @@
|
||||||
"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 { useParams, 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";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
DialogTrigger,
|
|
||||||
} from "@/components/ui/dialog";
|
|
||||||
import { link } from "fs";
|
|
||||||
import { getMediaTrackingResult } from "@/service/media-tracking/media-tracking";
|
|
||||||
|
|
||||||
const NewsDetailTable = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const param = useParams();
|
|
||||||
const id = param?.id;
|
|
||||||
const [search, setSearch] = React.useState("");
|
|
||||||
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 [showTable, setShowTable] = React.useState(false);
|
|
||||||
const [link, setLink] = React.useState("");
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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 getMediaTrackingResult({ id: id, page: page - 1 });
|
|
||||||
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 border">
|
|
||||||
<Table className="overflow-hidden mt-4">
|
|
||||||
<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} 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 NewsDetailTable;
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
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: "Nama Media Online",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("title")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "link",
|
|
||||||
header: "Link",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -1,302 +0,0 @@
|
||||||
"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 MediaOnlineTable = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const [search, setSearch] = React.useState("");
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
// search,
|
|
||||||
// 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">Media Online</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-between ">
|
|
||||||
<Link href="/admin/media-tracking/media-online/create">
|
|
||||||
<Button color="primary" size="md" className="text-sm mr-3">
|
|
||||||
Tambah Media Online
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
placeholder="Search"
|
|
||||||
// onKeyUp={handleKeyUp}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
|
||||||
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 MediaOnlineTable;
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import FormTaskTa from "@/components/form/task-ta/task-ta-form";
|
|
||||||
import FormAskExpert from "@/components/form/shared/ask-expert-form";
|
|
||||||
import FormDoItYourself from "@/components/form/shared/do-it-yourself-form";
|
|
||||||
import FormMediaOnline from "@/components/form/media-tracking/media-tracking-form";
|
|
||||||
|
|
||||||
const MediaOnlineCreatePage = () => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<div className="space-y-4">
|
|
||||||
<FormMediaOnline />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MediaOnlineCreatePage;
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import MediaOnlineTable from "./component/table";
|
|
||||||
|
|
||||||
export default function AdminMediaOnline() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<MediaOnlineTable />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import NewsTable from "./component/table";
|
|
||||||
|
|
||||||
export default function AdminNews() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<NewsTable />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
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: "date",
|
|
||||||
header: "Tanggal",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "link",
|
|
||||||
header: "Link Berita",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "actions",
|
|
||||||
accessorKey: "action",
|
|
||||||
header: "Aksi",
|
|
||||||
enableHiding: false,
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
|
||||||
>
|
|
||||||
<span className="sr-only">Open menu</span>
|
|
||||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
|
||||||
<Link
|
|
||||||
href={`/admin/media-tracking/tb-news/detail/${row.original.id}`}
|
|
||||||
>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<Eye className="w-4 h-4 me-1.5" />
|
|
||||||
View
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -1,333 +0,0 @@
|
||||||
"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 { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
||||||
import TablePagination from "@/components/table/table-pagination";
|
|
||||||
import columns from "./column";
|
|
||||||
import { listEnableCategory } from "@/service/content/content";
|
|
||||||
import { close, loading } from "@/config/swal";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
DialogTrigger,
|
|
||||||
} from "@/components/ui/dialog";
|
|
||||||
import { link } from "fs";
|
|
||||||
import { listDataAll } from "@/service/landing/landing";
|
|
||||||
import SearchImageComponent from "@/components/form/media-tracking/search-image-card";
|
|
||||||
import SearchVideoComponent from "@/components/form/media-tracking/search-video-card";
|
|
||||||
import SearchDocumentComponent from "@/components/form/media-tracking/search-document-card";
|
|
||||||
import SearchAudioComponent from "@/components/form/media-tracking/search-audio-card";
|
|
||||||
|
|
||||||
const NewsTable = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const asPath = usePathname();
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const [search, setSearch] = React.useState("");
|
|
||||||
const [showData, setShowData] = React.useState("10");
|
|
||||||
const [categories, setCategories] = React.useState<any>();
|
|
||||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const [showTable, setShowTable] = React.useState(false);
|
|
||||||
const [onSearch, setOnSearch] = React.useState("");
|
|
||||||
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<any>([]);
|
|
||||||
const [searchTitle, setSearchTitle] = React.useState<string>("");
|
|
||||||
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
|
||||||
const [page, setPage] = React.useState(1);
|
|
||||||
const [imageData, setImageData] = React.useState<any>();
|
|
||||||
const [totalData, setTotalData] = React.useState<number>(1);
|
|
||||||
const [totalPage, setTotalPage] = React.useState<number>(1);
|
|
||||||
const [contentAll, setContentAll] = React.useState([]);
|
|
||||||
const [formatFilter, setFormatFilter] = React.useState<any>([]);
|
|
||||||
const [totalContent, setTotalContent] = React.useState();
|
|
||||||
const group = searchParams?.get("group");
|
|
||||||
const title = searchParams?.get("title");
|
|
||||||
const categorie = searchParams?.get("category");
|
|
||||||
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;
|
|
||||||
|
|
||||||
// async function doneTyping() {
|
|
||||||
// fetchData();
|
|
||||||
// }
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const pageFromUrl = searchParams?.get("page");
|
|
||||||
if (pageFromUrl) {
|
|
||||||
setPage(Number(pageFromUrl));
|
|
||||||
}
|
|
||||||
}, [searchParams]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
getCategories();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
async function getCategories() {
|
|
||||||
const category = await listEnableCategory("");
|
|
||||||
const resCategory = category?.data?.data?.content;
|
|
||||||
setCategories(resCategory);
|
|
||||||
}
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (categorie) {
|
|
||||||
setCategoryFilter(
|
|
||||||
categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
"Kategori",
|
|
||||||
categorie,
|
|
||||||
categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [categorie]);
|
|
||||||
|
|
||||||
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 cleanCheckbox = () => {
|
|
||||||
setCategoryFilter([]);
|
|
||||||
setFormatFilter([]);
|
|
||||||
router.push(`?category=&title=`);
|
|
||||||
};
|
|
||||||
|
|
||||||
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, "");
|
|
||||||
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 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="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3 border ">
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<p className="text-xl font-medium text-default-900">
|
|
||||||
Tracking Berita hari ini!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Dialog>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button className="bg-blue-600" size="md">
|
|
||||||
Tracking Berita
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent className="overflow-y-auto h-[500px] min-w-max mx-5">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Form Tracking Berita</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<div className="grid gap-4 py-4 px-5">
|
|
||||||
<div className="space-y-2 flex flex-col">
|
|
||||||
<Label htmlFor="link" className="text-sm font-medium">
|
|
||||||
Masukkan Link <span className="text-red-500">*</span>
|
|
||||||
</Label>
|
|
||||||
<input
|
|
||||||
value={searchTitle}
|
|
||||||
onChange={(e) => setSearchTitle(e.target.value)}
|
|
||||||
onKeyUp={handleKeyUp}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
type="text"
|
|
||||||
placeholder="Search..."
|
|
||||||
className="pl-4 pr-4 py-1 w-full h-10 text-[15px] border focus:outline-none dark:text-white"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2 w-full">
|
|
||||||
<SearchImageComponent categoryFilter={categoryFilter} />
|
|
||||||
<SearchVideoComponent categoryFilter={categoryFilter} />
|
|
||||||
<SearchDocumentComponent categoryFilter={categoryFilter} />
|
|
||||||
<SearchAudioComponent categoryFilter={categoryFilter} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DialogFooter className="flex justify-end gap-2">
|
|
||||||
<Button onClick={cleanCheckbox} variant="outline">
|
|
||||||
Riset Filter
|
|
||||||
</Button>
|
|
||||||
<Button className="bg-blue-600 text-white">Tracking Berita</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
<Table className="overflow-hidden mt-4">
|
|
||||||
<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} 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 className="flex justify-end mt-4">
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
size="md"
|
|
||||||
className="text-sm mr-3"
|
|
||||||
onClick={() => setShowTable(false)}
|
|
||||||
>
|
|
||||||
Tracking Berita Baru
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
{/* </>
|
|
||||||
)} */}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default NewsTable;
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import NewsTable from "../../component/table";
|
|
||||||
import NewsDetailTable from "../component/table";
|
|
||||||
|
|
||||||
export default function DetailNews() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<NewsDetailTable />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
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: "mediaOnline",
|
|
||||||
header: "Media Online",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "link",
|
|
||||||
header: "Link Berita",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -1,263 +0,0 @@
|
||||||
"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";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
DialogTrigger,
|
|
||||||
} from "@/components/ui/dialog";
|
|
||||||
import { link } from "fs";
|
|
||||||
|
|
||||||
const NewsDetailTable = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const [search, setSearch] = React.useState("");
|
|
||||||
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 [showTable, setShowTable] = React.useState(false);
|
|
||||||
const [link, setLink] = React.useState("");
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
// search,
|
|
||||||
// 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 border">
|
|
||||||
<Table className="overflow-hidden mt-4">
|
|
||||||
<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} 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 NewsDetailTable;
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import NewsTable from "./component/table";
|
|
||||||
|
|
||||||
export default function AdminNews() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<NewsTable />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import PerformancePoldaViz from "@/components/visualization/performance-polda";
|
|
||||||
|
|
||||||
export default function PerformancePolda() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<p className="font-semibold">PERFORMANCE KUMULATIF PER POLDA</p>
|
|
||||||
<PerformancePoldaViz />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import PerformancePolresViz from "@/components/visualization/performance-polres";
|
|
||||||
|
|
||||||
export default function PerformancePolda() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<p className="font-semibold">PERFORMANCE KUMULATIF PER POLRES</p>
|
|
||||||
<PerformancePolresViz />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import PerformancePolresViz from "@/components/visualization/performance-polres";
|
|
||||||
import PerformanceSatkerViz from "@/components/visualization/performance-satker";
|
|
||||||
|
|
||||||
export default function PerformanceSatker() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<p className="font-semibold">PERFORMANCE KUMULATIF PER SATKER</p>
|
|
||||||
<PerformanceSatkerViz />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
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";
|
|
||||||
import { setBanner } from "@/service/settings/settings";
|
|
||||||
import { error } from "@/config/swal";
|
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
|
||||||
|
|
||||||
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 }) => {
|
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
const handleBanner = async (id: number) => {
|
|
||||||
const response = setBanner(id, true);
|
|
||||||
toast({
|
|
||||||
title: "Success",
|
|
||||||
});
|
|
||||||
};
|
|
||||||
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">
|
|
||||||
<a onClick={() => handleBanner(row.original.id)}>
|
|
||||||
Jadikan Banner
|
|
||||||
</a>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
import { Switch } from "@/components/ui/switch";
|
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
|
||||||
import { useRouter } from "@/i18n/routing";
|
|
||||||
import { setBanner } from "@/service/settings/settings";
|
|
||||||
|
|
||||||
export default function StatusToogle(props: {
|
|
||||||
id: number;
|
|
||||||
initChecked: boolean;
|
|
||||||
}) {
|
|
||||||
const { id, initChecked } = props;
|
|
||||||
const { toast } = useToast();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const disableBanner = async () => {
|
|
||||||
const response = await setBanner(id, false);
|
|
||||||
|
|
||||||
if (response?.error) {
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: response?.message,
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: "Success ",
|
|
||||||
});
|
|
||||||
router.push("/admin/settings/banner?dataChange=true");
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Switch
|
|
||||||
id="status-toogle"
|
|
||||||
checked={initChecked}
|
|
||||||
onCheckedChange={() => disableBanner()}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,393 +0,0 @@
|
||||||
"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 ContentListTable = () => {
|
|
||||||
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="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 text-sm">
|
|
||||||
<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}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ContentListTable;
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
"use client";
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import ContentListTable from "./component/table";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
import BannerListTable from "./component/banner-table";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
|
|
||||||
export default function AdminBanner() {
|
|
||||||
const [selectedTab, setSelectedTab] = useState("content");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
{selectedTab === "content" ? "List Media" : " List Banner"}
|
|
||||||
|
|
||||||
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
|
|
||||||
<Button
|
|
||||||
rounded="md"
|
|
||||||
onClick={() => setSelectedTab("content")}
|
|
||||||
className={` hover:text-white
|
|
||||||
${
|
|
||||||
selectedTab === "content"
|
|
||||||
? "bg-black text-white "
|
|
||||||
: "bg-white text-black "
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
Kontent
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
rounded="md"
|
|
||||||
onClick={() => setSelectedTab("banner")}
|
|
||||||
className={` hover:text-white
|
|
||||||
${
|
|
||||||
selectedTab === "banner"
|
|
||||||
? "bg-black text-white "
|
|
||||||
: "bg-white text-black "
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
Banner
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{selectedTab === "content" ? <ContentListTable /> : <BannerListTable />}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
||||||
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: "mediaTypesString",
|
|
||||||
header: "Tipe Konten",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">{row.getValue("mediaTypesString")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "publishedLocation",
|
|
||||||
header: "Wilayah",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">{row.getValue("publishedLocation")}</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 (
|
|
||||||
<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
|
|
||||||
id={row.original.id}
|
|
||||||
isDetail={true}
|
|
||||||
thumbnailLink={row.original.thumbnailLink}
|
|
||||||
/>
|
|
||||||
<EditCategoryModal
|
|
||||||
id={row.original.id}
|
|
||||||
thumbnailLink={row.original.thumbnailLink}
|
|
||||||
/>
|
|
||||||
<a
|
|
||||||
onClick={() => categoryDelete(row.original.id)}
|
|
||||||
className="hover:underline cursor-pointer hover:text-destructive"
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</a>
|
|
||||||
</MenubarContent>
|
|
||||||
</MenubarMenu>
|
|
||||||
</Menubar>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -1,600 +0,0 @@
|
||||||
"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 { Fragment, 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";
|
|
||||||
import { UnitMapping } from "./unit-mapping";
|
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
|
||||||
import { stringify } from "querystring";
|
|
||||||
import { useDropzone } from "react-dropzone";
|
|
||||||
import { CloudUpload } from "lucide-react";
|
|
||||||
import Image from "next/image";
|
|
||||||
import { Upload } from "tus-js-client";
|
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
|
||||||
import Cookies from "js-cookie";
|
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
|
|
||||||
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.array(z.string()).refine((value) => value.some((item) => item), {
|
|
||||||
message: "Required",
|
|
||||||
}),
|
|
||||||
|
|
||||||
file: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const listContent = [
|
|
||||||
{
|
|
||||||
id: "1",
|
|
||||||
name: "Foto",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "2",
|
|
||||||
name: "Audio Visual",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "3",
|
|
||||||
name: "Teks",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "4",
|
|
||||||
name: "Audio",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const publishToList = [
|
|
||||||
{
|
|
||||||
id: "mabes",
|
|
||||||
name: "Nasional",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "polda",
|
|
||||||
name: "Polda",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "satker",
|
|
||||||
name: "Satker",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "internasional",
|
|
||||||
name: "Internasional",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function CreateCategoryModal() {
|
|
||||||
const router = useRouter();
|
|
||||||
const { toast } = useToast();
|
|
||||||
const roleId = getCookiesDecrypt("urie");
|
|
||||||
const levelNumber = getCookiesDecrypt("ulne");
|
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
|
||||||
const poldaState = Cookies.get("state");
|
|
||||||
const t = useTranslations("Menu");
|
|
||||||
const [files, setFiles] = useState<File[]>([]);
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
const [satkerData, setSatkerData] = useState<string[]>([]);
|
|
||||||
const [unitData, setUnitData] = useState<string[]>([]);
|
|
||||||
const [userList, setUserList] = useState<
|
|
||||||
{ id: string; name: string; isInternal: boolean }[]
|
|
||||||
>([]);
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
defaultValues: { contentType: [], selectedUser: [], publishTo: [] },
|
|
||||||
});
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
const target = form.watch("publishTo");
|
|
||||||
const isAllTargetChecked = publishToList.every((item) =>
|
|
||||||
target?.includes(item.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
const { getRootProps, getInputProps } = useDropzone({
|
|
||||||
onDrop: (acceptedFiles) => {
|
|
||||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
|
||||||
},
|
|
||||||
maxFiles: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getRoles();
|
|
||||||
if (Number(levelNumber) === 2) {
|
|
||||||
form.setValue("publishTo", ["polda"]);
|
|
||||||
setUnitData([String(userLevelId)]);
|
|
||||||
}
|
|
||||||
if (Number(levelNumber) === 3) {
|
|
||||||
form.setValue("publishTo", ["satker"]);
|
|
||||||
setSatkerData([String(userLevelId)]);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
|
||||||
if (data.publishTo.includes("polda") || data.publishTo.includes("satker")) {
|
|
||||||
if (
|
|
||||||
(data.publishTo.includes("polda") && unitData?.length < 1) ||
|
|
||||||
(data.publishTo.includes("satker") && satkerData?.length < 1)
|
|
||||||
) {
|
|
||||||
const poldaValidation = data.publishTo.includes("polda");
|
|
||||||
const satkerValidation = data.publishTo.includes("satker");
|
|
||||||
toast({
|
|
||||||
title:
|
|
||||||
poldaValidation && satkerValidation
|
|
||||||
? "Pilih Polda dan Satker tujuan"
|
|
||||||
: `Pilih ${poldaValidation ? "Polda" : "Satker"} tujuan`,
|
|
||||||
variant: "destructive",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
save(data);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
save(data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const save = async (data: z.infer<typeof FormSchema>) => {
|
|
||||||
const formMedia = new FormData();
|
|
||||||
|
|
||||||
loading();
|
|
||||||
|
|
||||||
const unit = unitData?.join(",");
|
|
||||||
const satker = satkerData?.join(",");
|
|
||||||
const join =
|
|
||||||
unitData?.length > 0 && satkerData?.length > 0
|
|
||||||
? unit + "," + satker
|
|
||||||
: unitData?.length > 0
|
|
||||||
? unit
|
|
||||||
: satkerData?.length > 0
|
|
||||||
? satker
|
|
||||||
: "";
|
|
||||||
|
|
||||||
formMedia.append("name", data.title);
|
|
||||||
formMedia.append("description", data.description);
|
|
||||||
formMedia.append("mediaTypes", data.contentType.join(","));
|
|
||||||
formMedia.append("publishedFor", data.selectedUser.join(","));
|
|
||||||
formMedia.append("file", files[0]);
|
|
||||||
formMedia.append("publishedLocation", data.publishTo.sort().join(","));
|
|
||||||
formMedia.append("publishedLocationLevel", join);
|
|
||||||
formMedia.append(
|
|
||||||
"isInt",
|
|
||||||
data.publishTo.includes("internasional") ? "true" : "false"
|
|
||||||
);
|
|
||||||
|
|
||||||
const response = await postCategory(formMedia);
|
|
||||||
close();
|
|
||||||
if (response?.error) {
|
|
||||||
toast({ title: stringify(response.message), variant: "destructive" });
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
toast({
|
|
||||||
title: "Succes",
|
|
||||||
description: "Kategori berhasil dibuat",
|
|
||||||
});
|
|
||||||
router.push("/admin/settings/category?dataChange=true");
|
|
||||||
setIsOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRemoveFile = (file: File) => {
|
|
||||||
const uploadedFiles = files;
|
|
||||||
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
|
|
||||||
setFiles([...filtered]);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button color="primary" size="md">
|
|
||||||
{t("add-category")}
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent
|
|
||||||
size="md"
|
|
||||||
className="sm:h-[300px] md:h-[300px] lg:h-[500px] overflow-y-auto"
|
|
||||||
>
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle> {t("add-category")}</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>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{Number(levelNumber) === 1 ? (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="publishTo"
|
|
||||||
render={() => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Wilayah Publish</FormLabel>
|
|
||||||
|
|
||||||
<div className="flex flex-row items-center gap-2">
|
|
||||||
<div className="flex gap-3 items-center">
|
|
||||||
<Checkbox
|
|
||||||
id="all"
|
|
||||||
checked={isAllTargetChecked}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
if (checked) {
|
|
||||||
form.setValue(
|
|
||||||
"publishTo",
|
|
||||||
publishToList.map((item) => item.id)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
form.setValue("publishTo", []);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<label htmlFor="all" className="text-sm">
|
|
||||||
Semua
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{publishToList.map((item) => (
|
|
||||||
<>
|
|
||||||
<FormField
|
|
||||||
key={item.id}
|
|
||||||
control={form.control}
|
|
||||||
name="publishTo"
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{item.id === "polda" &&
|
|
||||||
form.getValues("publishTo")?.includes(item.id) && (
|
|
||||||
<UnitMapping
|
|
||||||
unit="Polda"
|
|
||||||
isDetail={false}
|
|
||||||
sendDataToParent={(data) => setUnitData(data)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{item.id === "satker" &&
|
|
||||||
form.getValues("publishTo")?.includes(item.id) && (
|
|
||||||
<UnitMapping
|
|
||||||
isDetail={false}
|
|
||||||
unit="Satker"
|
|
||||||
sendDataToParent={(data) => setSatkerData(data)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="publishTo"
|
|
||||||
render={() => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Wilayah Publish</FormLabel>
|
|
||||||
|
|
||||||
<div className="flex flex-row items-center gap-2">
|
|
||||||
<div className="flex gap-3 items-center">
|
|
||||||
<Checkbox disabled checked />
|
|
||||||
<label htmlFor="all" className="text-sm">
|
|
||||||
{poldaState}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<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>Thumbnail Category</FormLabel>
|
|
||||||
{files.length < 1 && (
|
|
||||||
<Fragment>
|
|
||||||
<div {...getRootProps({ className: "dropzone" })}>
|
|
||||||
<input {...getInputProps()} />
|
|
||||||
<div className=" w-full text-center border-dashed border border-default-200 dark:border-default-300 rounded-md py-[52px] flex items-center flex-col">
|
|
||||||
<CloudUpload className="text-default-300 w-10 h-10" />
|
|
||||||
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
|
||||||
Tarik file disini atau klik untuk upload.
|
|
||||||
</h4>
|
|
||||||
<div className=" text-xs text-muted-foreground">
|
|
||||||
( Upload file dengan format .jpg, .jpeg, atau .png.
|
|
||||||
Ukuran maksimal 100mb.)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{files.length > 0 && (
|
|
||||||
<div className="flex flex-row gap-2">
|
|
||||||
<img
|
|
||||||
src={URL.createObjectURL(files[0])}
|
|
||||||
className="w-[30%]"
|
|
||||||
alt="thumbnail"
|
|
||||||
/>
|
|
||||||
<a
|
|
||||||
onClick={() => handleRemoveFile(files[0])}
|
|
||||||
className="cursor-pointer"
|
|
||||||
>
|
|
||||||
<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" {...field} />
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<DialogFooter>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
color="primary"
|
|
||||||
size="md"
|
|
||||||
className="text-xs"
|
|
||||||
>
|
|
||||||
Tambah Kategori
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,660 +0,0 @@
|
||||||
"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 {
|
|
||||||
getCategoryDetail,
|
|
||||||
getUserRoles,
|
|
||||||
postCategory,
|
|
||||||
} from "@/service/settings/settings";
|
|
||||||
import { Fragment, 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";
|
|
||||||
import { UnitMapping } from "./unit-mapping";
|
|
||||||
import { useDropzone } from "react-dropzone";
|
|
||||||
import { CloudUpload } from "lucide-react";
|
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
|
||||||
import Cookies from "js-cookie";
|
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
|
||||||
|
|
||||||
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.array(z.string()).refine((value) => value.some((item) => item), {
|
|
||||||
message: "Required",
|
|
||||||
}),
|
|
||||||
|
|
||||||
id: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
file: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const listContent = [
|
|
||||||
{
|
|
||||||
id: "1",
|
|
||||||
name: "Foto",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "2",
|
|
||||||
name: "Audio Visual",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "3",
|
|
||||||
name: "Teks",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "4",
|
|
||||||
name: "Audio",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const publishToList = [
|
|
||||||
{
|
|
||||||
id: "mabes",
|
|
||||||
name: "Nasional",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "polda",
|
|
||||||
name: "Polda",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "satker",
|
|
||||||
name: "Satker",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "internasional",
|
|
||||||
name: "Internasional",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function EditCategoryModal(props: {
|
|
||||||
id: string;
|
|
||||||
thumbnailLink: string;
|
|
||||||
isDetail?: boolean;
|
|
||||||
}) {
|
|
||||||
const { id, isDetail, thumbnailLink } = props;
|
|
||||||
const [files, setFiles] = useState<File[]>([]);
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
const { toast } = useToast();
|
|
||||||
const roleId = getCookiesDecrypt("urie");
|
|
||||||
const levelNumber = getCookiesDecrypt("ulne");
|
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
|
||||||
const poldaState = Cookies.get("state");
|
|
||||||
const router = useRouter();
|
|
||||||
const [satkerData, setSatkerData] = useState<string[]>([]);
|
|
||||||
const [unitData, setUnitData] = useState<string[]>([]);
|
|
||||||
const [userList, setUserList] = useState<
|
|
||||||
{ id: string; name: string; isInternal: boolean }[]
|
|
||||||
>([]);
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
defaultValues: { contentType: [], selectedUser: [], publishTo: [] },
|
|
||||||
});
|
|
||||||
|
|
||||||
const { getRootProps, getInputProps } = useDropzone({
|
|
||||||
onDrop: (acceptedFiles) => {
|
|
||||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
|
||||||
},
|
|
||||||
maxFiles: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const initFetch = async () => {
|
|
||||||
const req = await getCategoryDetail(id);
|
|
||||||
const data = req?.data?.data;
|
|
||||||
console.log("dataC", data);
|
|
||||||
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",
|
|
||||||
removeAndReturn(data?.publishedFor, [2, 3, 4])
|
|
||||||
);
|
|
||||||
form.setValue("publishTo", data?.publishedLocation?.split(","));
|
|
||||||
form.setValue("file", thumbnailLink);
|
|
||||||
|
|
||||||
setUnitData(filterString(data?.publishedLocationLevel, "under"));
|
|
||||||
setSatkerData(filterString(data?.publishedLocationLevel, "above"));
|
|
||||||
};
|
|
||||||
|
|
||||||
initFetch();
|
|
||||||
}, [id]);
|
|
||||||
|
|
||||||
function removeAndReturn(inputString: string, toRemove: number[]) {
|
|
||||||
const numbers = inputString.split(",").map(Number);
|
|
||||||
|
|
||||||
const filteredNumbers = numbers.filter((num) => !toRemove.includes(num));
|
|
||||||
|
|
||||||
return filteredNumbers.map(String);
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterString(inputString: string, type: string) {
|
|
||||||
const numbers = inputString?.split(",").map(Number);
|
|
||||||
if (type === "above") {
|
|
||||||
const above700 = numbers?.filter((num) => num > 700);
|
|
||||||
|
|
||||||
return above700?.map(String);
|
|
||||||
} else {
|
|
||||||
const under700 = numbers?.filter((num) => num < 700);
|
|
||||||
|
|
||||||
return under700?.map(String);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
const target = form.watch("publishTo");
|
|
||||||
const isAllTargetChecked = publishToList.every((item) =>
|
|
||||||
target?.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeDuplicates(inputString: string): string {
|
|
||||||
const numbers = inputString.split(",");
|
|
||||||
const uniqueNumbers = Array.from(new Set(numbers));
|
|
||||||
return uniqueNumbers.join(",");
|
|
||||||
}
|
|
||||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
|
||||||
const formMedia = new FormData();
|
|
||||||
|
|
||||||
loading();
|
|
||||||
const unit = unitData?.join(",");
|
|
||||||
const satker = satkerData?.join(",");
|
|
||||||
const join =
|
|
||||||
unitData?.length > 0 && satkerData?.length > 0
|
|
||||||
? unit + "," + satker
|
|
||||||
: unitData?.length > 0
|
|
||||||
? unit
|
|
||||||
: satkerData?.length > 0
|
|
||||||
? satker
|
|
||||||
: "";
|
|
||||||
|
|
||||||
formMedia.append("id", data.id);
|
|
||||||
formMedia.append("name", data.title);
|
|
||||||
formMedia.append("description", data.description);
|
|
||||||
formMedia.append("mediaTypes", data.contentType.join(","));
|
|
||||||
formMedia.append("publishedFor", data.selectedUser.join(","));
|
|
||||||
formMedia.append("publishedLocation", data.publishTo.sort().join(","));
|
|
||||||
formMedia.append("publishedLocationLevel", removeDuplicates(join));
|
|
||||||
if (files?.length > 0) {
|
|
||||||
formMedia.append("file", files[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await postCategory(formMedia);
|
|
||||||
close();
|
|
||||||
if (response?.error == true) {
|
|
||||||
error(response.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
toast({
|
|
||||||
title: "Succes",
|
|
||||||
description: "Kategori berhasil diubah",
|
|
||||||
});
|
|
||||||
router.push("/admin/settings/category?dataChange=true");
|
|
||||||
setIsOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRemoveFile = (file: File) => {
|
|
||||||
const uploadedFiles = files;
|
|
||||||
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
|
|
||||||
setFiles([...filtered]);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
|
||||||
<DialogTrigger>
|
|
||||||
<a
|
|
||||||
onClick={() => setIsOpen(true)}
|
|
||||||
className="hover:underline cursor-pointer"
|
|
||||||
>
|
|
||||||
{isDetail ? "Detail" : "Edit"}
|
|
||||||
</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}
|
|
||||||
disabled={isDetail}
|
|
||||||
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
|
|
||||||
disabled={isDetail}
|
|
||||||
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"
|
|
||||||
disabled={isDetail}
|
|
||||||
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
|
|
||||||
disabled={isDetail}
|
|
||||||
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>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{Number(levelNumber) === 1 ? (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="publishTo"
|
|
||||||
render={() => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Wilayah Publish</FormLabel>
|
|
||||||
|
|
||||||
<div className="flex flex-row items-center gap-2">
|
|
||||||
<div className="flex gap-3 items-center">
|
|
||||||
<Checkbox
|
|
||||||
id="all"
|
|
||||||
checked={isAllTargetChecked}
|
|
||||||
disabled={isDetail}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
if (checked) {
|
|
||||||
form.setValue(
|
|
||||||
"publishTo",
|
|
||||||
publishToList.map((item) => item.id)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
form.setValue("publishTo", []);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<label htmlFor="all" className="text-sm">
|
|
||||||
Semua
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{publishToList.map((item) => (
|
|
||||||
<>
|
|
||||||
<FormField
|
|
||||||
key={item.id}
|
|
||||||
control={form.control}
|
|
||||||
name="publishTo"
|
|
||||||
render={({ field }) => {
|
|
||||||
return (
|
|
||||||
<FormItem
|
|
||||||
key={item.id}
|
|
||||||
className="flex flex-row items-start "
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<FormControl>
|
|
||||||
<Checkbox
|
|
||||||
disabled={isDetail}
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{item.id === "polda" &&
|
|
||||||
form.getValues("publishTo")?.includes(item.id) && (
|
|
||||||
<UnitMapping
|
|
||||||
unit="Polda"
|
|
||||||
sendDataToParent={(data) => setUnitData(data)}
|
|
||||||
isDetail={isDetail ? true : false}
|
|
||||||
initData={unitData}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{item.id === "satker" &&
|
|
||||||
form.getValues("publishTo")?.includes(item.id) && (
|
|
||||||
<UnitMapping
|
|
||||||
unit="Satker"
|
|
||||||
sendDataToParent={(data) => setSatkerData(data)}
|
|
||||||
isDetail={isDetail ? true : false}
|
|
||||||
initData={satkerData}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="publishTo"
|
|
||||||
render={() => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Wilayah Publish</FormLabel>
|
|
||||||
|
|
||||||
<div className="flex flex-row items-center gap-2">
|
|
||||||
<div className="flex gap-3 items-center">
|
|
||||||
<Checkbox disabled checked />
|
|
||||||
<label htmlFor="all" className="text-sm">
|
|
||||||
{poldaState}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="title"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Nama Kategori</FormLabel>
|
|
||||||
<Input
|
|
||||||
value={field.value}
|
|
||||||
readOnly={isDetail}
|
|
||||||
placeholder="Masukkan Nama Kategori"
|
|
||||||
onChange={field.onChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{isDetail ? (
|
|
||||||
<div className="flex flex-row gap-2">
|
|
||||||
<img src={thumbnailLink} className="w-[30%]" alt="thumbnail" />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="file"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Thumbnail Category</FormLabel>
|
|
||||||
{files.length < 1 && field.value === "" && (
|
|
||||||
<Fragment>
|
|
||||||
<div {...getRootProps({ className: "dropzone" })}>
|
|
||||||
<input {...getInputProps()} />
|
|
||||||
<div className=" w-full text-center border-dashed border border-default-200 dark:border-default-300 rounded-md py-[52px] flex items-center flex-col">
|
|
||||||
<CloudUpload className="text-default-300 w-10 h-10" />
|
|
||||||
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
|
||||||
Tarik file disini atau klik untuk upload.
|
|
||||||
</h4>
|
|
||||||
<div className=" text-xs text-muted-foreground">
|
|
||||||
( Upload file dengan format .jpg, .jpeg, atau
|
|
||||||
.png. Ukuran maksimal 100mb.)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
{field.value !== "" && (
|
|
||||||
<div className="flex flex-row gap-2">
|
|
||||||
<img
|
|
||||||
src={field.value}
|
|
||||||
className="w-[30%]"
|
|
||||||
alt="thumbnail"
|
|
||||||
/>
|
|
||||||
<a
|
|
||||||
onClick={() => form.setValue("file", "")}
|
|
||||||
className="cursor-pointer"
|
|
||||||
>
|
|
||||||
<Icon icon="fa-solid:times" color="red" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{files.length > 0 && (
|
|
||||||
<div className="flex flex-row gap-2">
|
|
||||||
<img
|
|
||||||
src={URL.createObjectURL(files[0])}
|
|
||||||
className="w-[30%]"
|
|
||||||
alt="thumbnail"
|
|
||||||
/>
|
|
||||||
<a
|
|
||||||
onClick={() => handleRemoveFile(files[0])}
|
|
||||||
className="cursor-pointer"
|
|
||||||
>
|
|
||||||
<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
|
|
||||||
readOnly={isDetail}
|
|
||||||
placeholder="Deskripsi"
|
|
||||||
// className="resize-none"
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{!isDetail && (
|
|
||||||
<DialogFooter>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
color="primary"
|
|
||||||
size="md"
|
|
||||||
className="text-xs"
|
|
||||||
>
|
|
||||||
Edit Kategori
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
)}
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
"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))}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,323 +0,0 @@
|
||||||
"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";
|
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
|
|
||||||
const AdminCategoryTable = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const t = useTranslations("Menu");
|
|
||||||
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 [region, setRegion] = React.useState<any>();
|
|
||||||
const [regionFilter, setRegionFilter] = React.useState<string[]>([]);
|
|
||||||
const [statusFilter, setStatusFilter] = React.useState<string[]>([]);
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const manualRegions = [
|
|
||||||
{ id: "semua", label: "Semua" },
|
|
||||||
{ id: "mabes", label: "Mabes" },
|
|
||||||
{ id: "polda", label: "Polda" },
|
|
||||||
{ id: "satker", label: "Satker" },
|
|
||||||
{ id: "internasional", label: "Internasional" },
|
|
||||||
];
|
|
||||||
|
|
||||||
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, regionFilter]);
|
|
||||||
|
|
||||||
async function fetchData() {
|
|
||||||
try {
|
|
||||||
loading();
|
|
||||||
|
|
||||||
const regionQuery = regionFilter.length
|
|
||||||
? `&publishedLocation=${regionFilter.join(",")}`
|
|
||||||
: "";
|
|
||||||
|
|
||||||
const statusQuery = statusFilter.length
|
|
||||||
? `&isPublish=${statusFilter.join(",")}`
|
|
||||||
: "";
|
|
||||||
|
|
||||||
const response = await getCategories(
|
|
||||||
page - 1,
|
|
||||||
`${regionQuery}${statusQuery}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const data = response?.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 categories:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleChange = (type: string, id: string, checked: boolean) => {
|
|
||||||
if (type === "region") {
|
|
||||||
if (id === "semua") {
|
|
||||||
if (checked) {
|
|
||||||
// Pilih semua (kecuali 'semua' itu sendiri)
|
|
||||||
setRegionFilter(["mabes", "polda", "satker", "internasional"]);
|
|
||||||
} else {
|
|
||||||
setRegionFilter([]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let updated = checked
|
|
||||||
? [...regionFilter, id]
|
|
||||||
: regionFilter.filter((val) => val !== id);
|
|
||||||
|
|
||||||
// Jika semua sudah tercentang, maka otomatis centang "semua"
|
|
||||||
const allIds = ["mabes", "polda", "satker", "internasional"];
|
|
||||||
const allSelected = allIds.every((val) => updated.includes(val));
|
|
||||||
|
|
||||||
setRegionFilter(allSelected ? allIds : updated);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (checked) {
|
|
||||||
setStatusFilter([...statusFilter, id]);
|
|
||||||
} else {
|
|
||||||
setStatusFilter(statusFilter.filter((val: any) => val !== id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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">{t("category")}</p>
|
|
||||||
<CreateCategoryModal />
|
|
||||||
</div>
|
|
||||||
<div className="flex items-end 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">
|
|
||||||
<p>Wilayah</p>
|
|
||||||
{manualRegions.map((region) => (
|
|
||||||
<div key={region.id} className="flex items-center space-x-2">
|
|
||||||
<Checkbox
|
|
||||||
id={region.id}
|
|
||||||
checked={
|
|
||||||
region.id === "semua"
|
|
||||||
? ["mabes", "polda", "satker", "internasional"].every(
|
|
||||||
(val) => regionFilter.includes(val)
|
|
||||||
)
|
|
||||||
: regionFilter.includes(region.id)
|
|
||||||
}
|
|
||||||
onCheckedChange={(e) =>
|
|
||||||
handleChange("region", region.id, Boolean(e))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor={region.id}
|
|
||||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
||||||
>
|
|
||||||
{region.label}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<p className="mt-3">Status</p>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Checkbox
|
|
||||||
id="aktif"
|
|
||||||
checked={statusFilter.includes("true")}
|
|
||||||
onCheckedChange={(e) =>
|
|
||||||
handleChange("status", "true", Boolean(e))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor="aktif"
|
|
||||||
className="text-xs font-medium leading-none"
|
|
||||||
>
|
|
||||||
Aktif
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Checkbox
|
|
||||||
id="nonaktif"
|
|
||||||
checked={statusFilter.includes("false")}
|
|
||||||
onCheckedChange={(e) =>
|
|
||||||
handleChange("status", "false", Boolean(e))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor="nonaktif"
|
|
||||||
className="text-xs font-medium leading-none"
|
|
||||||
>
|
|
||||||
Non Aktif
|
|
||||||
</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 AdminCategoryTable;
|
|
||||||
|
|
@ -1,253 +0,0 @@
|
||||||
"use client";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormDescription,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
DialogTrigger,
|
|
||||||
} from "@/components/ui/dialog";
|
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { z } from "zod";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { getUserLevelForAssignments } from "@/service/task";
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
items: z.array(z.string()).refine((value) => value.some((item) => item), {
|
|
||||||
message: "Required",
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
interface UnitType {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
subDestination: { id: number; name: string }[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function UnitMapping(props: {
|
|
||||||
unit: string;
|
|
||||||
sendDataToParent: (data: string[]) => void;
|
|
||||||
isDetail: boolean;
|
|
||||||
initData?: string[];
|
|
||||||
}) {
|
|
||||||
const { unit, sendDataToParent, isDetail } = props;
|
|
||||||
const [unitList, setUnitList] = useState<UnitType[]>([]);
|
|
||||||
const [satkerList, setSatkerList] = useState<{ id: number; name: string }[]>(
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
defaultValues: {
|
|
||||||
items: props.initData ? props.initData : [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function initState() {
|
|
||||||
const response = await getUserLevelForAssignments();
|
|
||||||
setupUnit(response?.data?.data.list);
|
|
||||||
}
|
|
||||||
|
|
||||||
initState();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const unitType = form.watch("items");
|
|
||||||
|
|
||||||
const isAllUnitChecked = unitList.every((item) =>
|
|
||||||
unitType?.includes(String(item.id))
|
|
||||||
);
|
|
||||||
const isAllSatkerChecked = satkerList.every((item) =>
|
|
||||||
unitType?.includes(String(item.id))
|
|
||||||
);
|
|
||||||
|
|
||||||
const setupUnit = (data: UnitType[]) => {
|
|
||||||
const temp = data.filter((a) => a.name.includes("POLDA"));
|
|
||||||
const temp2 = data.filter((a) => a.name.includes("SATKER"));
|
|
||||||
setUnitList(temp);
|
|
||||||
setSatkerList(temp2[0].subDestination);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
sendDataToParent(form.getValues("items"));
|
|
||||||
}, [unitType]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<a
|
|
||||||
onClick={() => setIsOpen(true)}
|
|
||||||
className="text-primary cursor-pointer text-xs mr-3"
|
|
||||||
>
|
|
||||||
Pilih {unit}
|
|
||||||
</a>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent size="md">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>{unit}</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
{unit === "Polda" ? (
|
|
||||||
<Form {...form}>
|
|
||||||
<form className="flex flex-col gap-2">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<Checkbox
|
|
||||||
id={`all-${unit}`}
|
|
||||||
checked={isAllUnitChecked}
|
|
||||||
disabled={isDetail}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
if (checked) {
|
|
||||||
form.setValue(
|
|
||||||
"items",
|
|
||||||
unitList.map((item) => String(item.id))
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
form.setValue("items", []);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<label htmlFor="all" className="text-sm text-black uppercase">
|
|
||||||
SEMUA {unit}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="items"
|
|
||||||
render={() => (
|
|
||||||
<FormItem
|
|
||||||
className={`grid grid-cols-${unit === "Polda" ? "2" : "3"}`}
|
|
||||||
>
|
|
||||||
{unitList?.map((item: any) => (
|
|
||||||
<FormField
|
|
||||||
key={item.id}
|
|
||||||
control={form.control}
|
|
||||||
name="items"
|
|
||||||
render={({ field }) => {
|
|
||||||
return (
|
|
||||||
<FormItem
|
|
||||||
key={String(item.id)}
|
|
||||||
className="flex flex-row items-center space-x-3 space-y-0"
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<Checkbox
|
|
||||||
disabled={isDetail}
|
|
||||||
checked={field.value?.includes(
|
|
||||||
String(item.id)
|
|
||||||
)}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
return checked
|
|
||||||
? field.onChange([
|
|
||||||
...field.value,
|
|
||||||
String(item.id),
|
|
||||||
])
|
|
||||||
: field.onChange(
|
|
||||||
field.value?.filter(
|
|
||||||
(value) => value !== String(item.id)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<p className="text-sm text-black">{item.name}</p>
|
|
||||||
</FormItem>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
) : (
|
|
||||||
<Form {...form}>
|
|
||||||
<form className="flex flex-col gap-2">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<Checkbox
|
|
||||||
id={`all-${unit}`}
|
|
||||||
checked={isAllSatkerChecked}
|
|
||||||
disabled={isDetail}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
if (checked) {
|
|
||||||
form.setValue(
|
|
||||||
"items",
|
|
||||||
satkerList.map((item) => String(item.id))
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
form.setValue("items", []);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<label htmlFor="all" className="text-sm text-black uppercase">
|
|
||||||
SEMUA {unit}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="items"
|
|
||||||
render={() => (
|
|
||||||
<FormItem
|
|
||||||
className={`grid grid-cols-${unit === "Polda" ? "2" : "3"}`}
|
|
||||||
>
|
|
||||||
{satkerList?.map((item: any) => (
|
|
||||||
<FormField
|
|
||||||
key={item.id}
|
|
||||||
control={form.control}
|
|
||||||
name="items"
|
|
||||||
render={({ field }) => {
|
|
||||||
return (
|
|
||||||
<FormItem
|
|
||||||
key={String(item.id)}
|
|
||||||
className="flex flex-row items-center space-x-3 space-y-0"
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<Checkbox
|
|
||||||
disabled={isDetail}
|
|
||||||
checked={field.value?.includes(
|
|
||||||
String(item.id)
|
|
||||||
)}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
return checked
|
|
||||||
? field.onChange([
|
|
||||||
...field.value,
|
|
||||||
String(item.id),
|
|
||||||
])
|
|
||||||
: field.onChange(
|
|
||||||
field.value?.filter(
|
|
||||||
(value) => value !== String(item.id)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<p className="text-sm text-black">{item.name}</p>
|
|
||||||
</FormItem>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
)}
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
"use client";
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import AdminCategoryTable from "./component/table";
|
|
||||||
|
|
||||||
export default function AdminCategory() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<AdminCategoryTable />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
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 { error } from "@/config/swal";
|
|
||||||
import { deleteCategory, deleteDataFAQ } 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";
|
|
||||||
import { htmlToString } from "@/utils/globals";
|
|
||||||
import EditFAQModal from "./edit";
|
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
|
||||||
{
|
|
||||||
accessorKey: "no",
|
|
||||||
header: "No",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
accessorKey: "question",
|
|
||||||
header: "Pertanyaan",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">
|
|
||||||
{htmlToString(row.getValue("question"))}
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "answer",
|
|
||||||
header: "Answer",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">
|
|
||||||
{htmlToString(row.getValue("answer"))}
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
id: "actions",
|
|
||||||
accessorKey: "action",
|
|
||||||
header: "Actions",
|
|
||||||
enableHiding: false,
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const router = useRouter();
|
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
const faqDelete = async (id: string) => {
|
|
||||||
const response = await deleteDataFAQ(id);
|
|
||||||
console.log(response);
|
|
||||||
if (response?.error) {
|
|
||||||
error(response.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
toast({
|
|
||||||
title: "Sukses",
|
|
||||||
description: "Berhasil Delete",
|
|
||||||
});
|
|
||||||
router.push("/admin/settings/faq?dataChange=true");
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<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">
|
|
||||||
<EditFAQModal id={row.original.id} isDetail={true} />
|
|
||||||
|
|
||||||
<EditFAQModal id={row.original.id} isDetail={false} />
|
|
||||||
|
|
||||||
<a
|
|
||||||
onClick={() => faqDelete(row.original.id)}
|
|
||||||
className="hover:underline cursor-pointer hover:text-destructive"
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</a>
|
|
||||||
</MenubarContent>
|
|
||||||
</MenubarMenu>
|
|
||||||
</Menubar>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -1,300 +0,0 @@
|
||||||
"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,
|
|
||||||
postDataFAQ,
|
|
||||||
} from "@/service/settings/settings";
|
|
||||||
import { Fragment, 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";
|
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
|
||||||
import { stringify } from "querystring";
|
|
||||||
import { useDropzone } from "react-dropzone";
|
|
||||||
import { CloudUpload } from "lucide-react";
|
|
||||||
import Image from "next/image";
|
|
||||||
import { Upload } from "tus-js-client";
|
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
answer: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
question: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
publishTo: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
|
|
||||||
// publishTo: z.array(z.string()).refine((value) => value.some((item) => item), {
|
|
||||||
// message: "Required",
|
|
||||||
// }),
|
|
||||||
});
|
|
||||||
|
|
||||||
const publishToList = [
|
|
||||||
{
|
|
||||||
id: "mabes",
|
|
||||||
name: "Nasional",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "polda",
|
|
||||||
name: "Polda",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "satker",
|
|
||||||
name: "Satker",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "internasional",
|
|
||||||
name: "Internasional",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function CreateFAQModal() {
|
|
||||||
const router = useRouter();
|
|
||||||
const { toast } = useToast();
|
|
||||||
const t = useTranslations("Menu");
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
const [satkerData, setSatkerData] = useState<string[]>([]);
|
|
||||||
const [unitData, setUnitData] = useState<string[]>([]);
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
defaultValues: { publishTo: "wilayah" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const target = form.watch("publishTo");
|
|
||||||
const isAllTargetChecked = publishToList.every((item) =>
|
|
||||||
target?.includes(item.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
|
||||||
const request = {
|
|
||||||
question: data.question,
|
|
||||||
answer: data.answer,
|
|
||||||
isInternational: data.publishTo === "wilayah" ? false : true,
|
|
||||||
isActive: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await postDataFAQ(request);
|
|
||||||
close();
|
|
||||||
if (response?.error) {
|
|
||||||
toast({ title: stringify(response.message), variant: "destructive" });
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
toast({
|
|
||||||
title: "Succes",
|
|
||||||
description: "FAQ berhasil dibuat",
|
|
||||||
});
|
|
||||||
router.push("/admin/settings/faq?dataChange=true");
|
|
||||||
setIsOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button color="primary" size="md">
|
|
||||||
{t("add")} FAQ
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent size="md">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>{t("add")} FAQ</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
className="space-y-3 bg-white rounded-sm"
|
|
||||||
>
|
|
||||||
<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 space-x-1"
|
|
||||||
>
|
|
||||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
|
||||||
<FormControl>
|
|
||||||
<RadioGroupItem value="wilayah" />
|
|
||||||
</FormControl>
|
|
||||||
<FormLabel className="font-normal">Wilayah</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="publishTo"
|
|
||||||
render={() => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Wilayah Publish</FormLabel>
|
|
||||||
|
|
||||||
<div className="flex flex-row items-center gap-2">
|
|
||||||
<div className="flex gap-3 items-center">
|
|
||||||
<Checkbox
|
|
||||||
id="all"
|
|
||||||
checked={isAllTargetChecked}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
if (checked) {
|
|
||||||
form.setValue(
|
|
||||||
"publishTo",
|
|
||||||
publishToList.map((item) => item.id)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
form.setValue("publishTo", []);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<label htmlFor="all" className="text-sm">
|
|
||||||
Semua
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{publishToList.map((item) => (
|
|
||||||
<>
|
|
||||||
<FormField
|
|
||||||
key={item.id}
|
|
||||||
control={form.control}
|
|
||||||
name="publishTo"
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{item.id === "polda" &&
|
|
||||||
form.getValues("publishTo")?.includes(item.id) && (
|
|
||||||
<UnitMapping
|
|
||||||
unit="Polda"
|
|
||||||
isDetail={false}
|
|
||||||
sendDataToParent={(data) => setUnitData(data)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{item.id === "satker" &&
|
|
||||||
form.getValues("publishTo")?.includes(item.id) && (
|
|
||||||
<UnitMapping
|
|
||||||
isDetail={false}
|
|
||||||
unit="Satker"
|
|
||||||
sendDataToParent={(data) => setSatkerData(data)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/> */}
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="question"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Pertanyaan</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea placeholder="Masukkan pertanyaan" {...field} />
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="answer"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Jawaban</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea placeholder="Masukkan jawaban" {...field} />
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<DialogFooter>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
color="primary"
|
|
||||||
size="md"
|
|
||||||
className="text-xs"
|
|
||||||
>
|
|
||||||
Tambah FAQ
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,325 +0,0 @@
|
||||||
"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 {
|
|
||||||
detailDataFAQ,
|
|
||||||
getUserRoles,
|
|
||||||
postCategory,
|
|
||||||
postDataFAQ,
|
|
||||||
} from "@/service/settings/settings";
|
|
||||||
import { Fragment, 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";
|
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
|
||||||
import { stringify } from "querystring";
|
|
||||||
import { useDropzone } from "react-dropzone";
|
|
||||||
import { CloudUpload } from "lucide-react";
|
|
||||||
import Image from "next/image";
|
|
||||||
import { Upload } from "tus-js-client";
|
|
||||||
import { id } from "date-fns/locale";
|
|
||||||
import { htmlToString } from "@/utils/globals";
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
answer: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
question: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
publishTo: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
|
|
||||||
// publishTo: z.array(z.string()).refine((value) => value.some((item) => item), {
|
|
||||||
// message: "Required",
|
|
||||||
// }),
|
|
||||||
});
|
|
||||||
|
|
||||||
const publishToList = [
|
|
||||||
{
|
|
||||||
id: "mabes",
|
|
||||||
name: "Nasional",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "polda",
|
|
||||||
name: "Polda",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "satker",
|
|
||||||
name: "Satker",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "internasional",
|
|
||||||
name: "Internasional",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function EditFAQModal(props: { id: string; isDetail: boolean }) {
|
|
||||||
const { id, isDetail } = props;
|
|
||||||
const router = useRouter();
|
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
initState();
|
|
||||||
}, [id]);
|
|
||||||
|
|
||||||
const initState = async () => {
|
|
||||||
const res = await detailDataFAQ(id);
|
|
||||||
const data = res?.data?.data;
|
|
||||||
form.setValue("question", htmlToString(data.question));
|
|
||||||
form.setValue("answer", htmlToString(data.answer));
|
|
||||||
form.setValue(
|
|
||||||
"publishTo",
|
|
||||||
data.isInternational ? "international" : "wilayah"
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
|
||||||
const request = {
|
|
||||||
id: Number(id),
|
|
||||||
question: data.question,
|
|
||||||
answer: data.answer,
|
|
||||||
isInternational: data.publishTo === "wilayah" ? false : true,
|
|
||||||
isActive: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await postDataFAQ(request);
|
|
||||||
close();
|
|
||||||
if (response?.error) {
|
|
||||||
toast({ title: stringify(response.message), variant: "destructive" });
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
toast({
|
|
||||||
title: "Succes",
|
|
||||||
description: "FAQ berhasil diubah",
|
|
||||||
});
|
|
||||||
router.push("/admin/settings/faq?dataChange=true");
|
|
||||||
setIsOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<a className="hover:underline cursor-pointer">
|
|
||||||
{isDetail ? "Detail" : "Edit"}
|
|
||||||
</a>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent size="md">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>{isDetail ? "Detail" : "Edit"} FAQ</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
className="space-y-3 bg-white rounded-sm"
|
|
||||||
>
|
|
||||||
<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 space-x-1"
|
|
||||||
>
|
|
||||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
|
||||||
<FormControl>
|
|
||||||
<RadioGroupItem value="wilayah" />
|
|
||||||
</FormControl>
|
|
||||||
<FormLabel className="font-normal">Wilayah</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="publishTo"
|
|
||||||
render={() => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Wilayah Publish</FormLabel>
|
|
||||||
|
|
||||||
<div className="flex flex-row items-center gap-2">
|
|
||||||
<div className="flex gap-3 items-center">
|
|
||||||
<Checkbox
|
|
||||||
id="all"
|
|
||||||
disabled={isDetail}
|
|
||||||
checked={isAllTargetChecked}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
if (checked) {
|
|
||||||
form.setValue(
|
|
||||||
"publishTo",
|
|
||||||
publishToList.map((item) => item.id)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
form.setValue("publishTo", []);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<label htmlFor="all" className="text-sm">
|
|
||||||
Semua
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{publishToList.map((item) => (
|
|
||||||
<>
|
|
||||||
<FormField
|
|
||||||
key={item.id}
|
|
||||||
control={form.control}
|
|
||||||
name="publishTo"
|
|
||||||
render={({ field }) => {
|
|
||||||
return (
|
|
||||||
<FormItem
|
|
||||||
key={item.id}
|
|
||||||
className="flex flex-row items-start "
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<FormControl>
|
|
||||||
<Checkbox
|
|
||||||
disabled={isDetail}
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{item.id === "polda" &&
|
|
||||||
form.getValues("publishTo")?.includes(item.id) && (
|
|
||||||
<UnitMapping
|
|
||||||
unit="Polda"
|
|
||||||
isDetail={isDetail}
|
|
||||||
sendDataToParent={(data) => setUnitData(data)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{item.id === "satker" &&
|
|
||||||
form.getValues("publishTo")?.includes(item.id) && (
|
|
||||||
<UnitMapping
|
|
||||||
isDetail={isDetail}
|
|
||||||
unit="Satker"
|
|
||||||
sendDataToParent={(data) => setSatkerData(data)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/> */}
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="question"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Pertanyaan</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea
|
|
||||||
readOnly={isDetail}
|
|
||||||
placeholder="Masukkan pertanyaan"
|
|
||||||
value={field.value}
|
|
||||||
onChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="answer"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Jawaban</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea
|
|
||||||
readOnly={isDetail}
|
|
||||||
placeholder="Masukkan jawaban"
|
|
||||||
value={field.value}
|
|
||||||
onChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{!isDetail && (
|
|
||||||
<DialogFooter>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
color="primary"
|
|
||||||
size="md"
|
|
||||||
className="text-xs"
|
|
||||||
>
|
|
||||||
Edit FAQ
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
)}
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
||||||
"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, getListFAQ } from "@/service/settings/settings";
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
DialogTrigger,
|
|
||||||
} from "@/components/ui/dialog";
|
|
||||||
import CreateFAQModal from "./create";
|
|
||||||
|
|
||||||
const AdminFAQTable = () => {
|
|
||||||
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: 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(() => {
|
|
||||||
if (dataChange) {
|
|
||||||
router.push("/admin/settings/faq");
|
|
||||||
}
|
|
||||||
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 getListFAQ();
|
|
||||||
const data = response?.data?.data;
|
|
||||||
console.log("respone", response);
|
|
||||||
data.forEach((item: any, index: number) => {
|
|
||||||
item.no = (page - 1) * 10 + index + 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
setDataTable(data);
|
|
||||||
setTotalData(data?.length);
|
|
||||||
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">FAQ</p>
|
|
||||||
<CreateFAQModal />
|
|
||||||
</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 AdminFAQTable;
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import AdminFAQTable from "./component/table";
|
|
||||||
|
|
||||||
export default function FAQSetting() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<AdminFAQTable />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
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 { error } from "@/config/swal";
|
|
||||||
import { deleteCategory, deleteDataFAQ } 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";
|
|
||||||
import { htmlToString } from "@/utils/globals";
|
|
||||||
import EditFAQModal from "./edit";
|
|
||||||
import EditFeedbackModal from "./edit";
|
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
|
||||||
{
|
|
||||||
accessorKey: "no",
|
|
||||||
header: "No",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
accessorKey: "question",
|
|
||||||
header: "Poin Penilaian",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">
|
|
||||||
{htmlToString(row.getValue("question"))}
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "isInternational",
|
|
||||||
header: "Wilayah Publish",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">
|
|
||||||
{row.getValue("isInternational") ? "Internasional" : "Wilayah"}
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
id: "actions",
|
|
||||||
accessorKey: "action",
|
|
||||||
header: "Actions",
|
|
||||||
enableHiding: false,
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const router = useRouter();
|
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
const faqDelete = async (id: string) => {
|
|
||||||
const response = await deleteDataFAQ(id);
|
|
||||||
console.log(response);
|
|
||||||
if (response?.error) {
|
|
||||||
error(response.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
toast({
|
|
||||||
title: "Sukses",
|
|
||||||
description: "Berhasil Delete",
|
|
||||||
});
|
|
||||||
router.push("/admin/settings/feedback?dataChange=true");
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<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">
|
|
||||||
<EditFeedbackModal
|
|
||||||
id={row.original.id}
|
|
||||||
isDetail={true}
|
|
||||||
data={row.original}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<EditFeedbackModal
|
|
||||||
id={row.original.id}
|
|
||||||
isDetail={false}
|
|
||||||
data={row.original}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<a
|
|
||||||
onClick={() => faqDelete(row.original.id)}
|
|
||||||
className="hover:underline cursor-pointer hover:text-destructive"
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</a>
|
|
||||||
</MenubarContent>
|
|
||||||
</MenubarMenu>
|
|
||||||
</Menubar>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -1,276 +0,0 @@
|
||||||
"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,
|
|
||||||
postDataFAQ,
|
|
||||||
postDataFeedback,
|
|
||||||
} from "@/service/settings/settings";
|
|
||||||
import { Fragment, 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";
|
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
|
||||||
import { stringify } from "querystring";
|
|
||||||
import { useDropzone } from "react-dropzone";
|
|
||||||
import { CloudUpload } from "lucide-react";
|
|
||||||
import Image from "next/image";
|
|
||||||
import { Upload } from "tus-js-client";
|
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
question: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
publishTo: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
|
|
||||||
// publishTo: z.array(z.string()).refine((value) => value.some((item) => item), {
|
|
||||||
// message: "Required",
|
|
||||||
// }),
|
|
||||||
});
|
|
||||||
|
|
||||||
const publishToList = [
|
|
||||||
{
|
|
||||||
id: "mabes",
|
|
||||||
name: "Nasional",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "polda",
|
|
||||||
name: "Polda",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "satker",
|
|
||||||
name: "Satker",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "internasional",
|
|
||||||
name: "Internasional",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function CreateFAQModal() {
|
|
||||||
const router = useRouter();
|
|
||||||
const { toast } = useToast();
|
|
||||||
const t = useTranslations("Menu");
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
defaultValues: { publishTo: "wilayah" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
|
||||||
const request = {
|
|
||||||
question: data.question,
|
|
||||||
isInternational: data.publishTo === "wilayah" ? false : true,
|
|
||||||
isActive: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await postDataFeedback(request);
|
|
||||||
close();
|
|
||||||
if (response?.error) {
|
|
||||||
toast({ title: stringify(response.message), variant: "destructive" });
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
toast({
|
|
||||||
title: "Succes",
|
|
||||||
description: "Feedback berhasil dibuat",
|
|
||||||
});
|
|
||||||
router.push("/admin/settings/feedback?dataChange=true");
|
|
||||||
setIsOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button color="primary" size="md">
|
|
||||||
{t("add")} Feedback
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent size="md">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>{t("add")} Feedback</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
className="space-y-3 bg-white rounded-sm"
|
|
||||||
>
|
|
||||||
<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 space-x-1"
|
|
||||||
>
|
|
||||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
|
||||||
<FormControl>
|
|
||||||
<RadioGroupItem value="wilayah" />
|
|
||||||
</FormControl>
|
|
||||||
<FormLabel className="font-normal">Wilayah</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="publishTo"
|
|
||||||
render={() => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Wilayah Publish</FormLabel>
|
|
||||||
|
|
||||||
<div className="flex flex-row items-center gap-2">
|
|
||||||
<div className="flex gap-3 items-center">
|
|
||||||
<Checkbox
|
|
||||||
id="all"
|
|
||||||
checked={isAllTargetChecked}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
if (checked) {
|
|
||||||
form.setValue(
|
|
||||||
"publishTo",
|
|
||||||
publishToList.map((item) => item.id)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
form.setValue("publishTo", []);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<label htmlFor="all" className="text-sm">
|
|
||||||
Semua
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{publishToList.map((item) => (
|
|
||||||
<>
|
|
||||||
<FormField
|
|
||||||
key={item.id}
|
|
||||||
control={form.control}
|
|
||||||
name="publishTo"
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{item.id === "polda" &&
|
|
||||||
form.getValues("publishTo")?.includes(item.id) && (
|
|
||||||
<UnitMapping
|
|
||||||
unit="Polda"
|
|
||||||
isDetail={false}
|
|
||||||
sendDataToParent={(data) => setUnitData(data)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{item.id === "satker" &&
|
|
||||||
form.getValues("publishTo")?.includes(item.id) && (
|
|
||||||
<UnitMapping
|
|
||||||
isDetail={false}
|
|
||||||
unit="Satker"
|
|
||||||
sendDataToParent={(data) => setSatkerData(data)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/> */}
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="question"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Pertanyaan</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea placeholder="Masukkan pertanyaan" {...field} />
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<DialogFooter>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
color="primary"
|
|
||||||
size="md"
|
|
||||||
className="text-xs"
|
|
||||||
>
|
|
||||||
Tambah Feeback
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,311 +0,0 @@
|
||||||
"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 {
|
|
||||||
detailDataFAQ,
|
|
||||||
detailDataFeedback,
|
|
||||||
getUserRoles,
|
|
||||||
postCategory,
|
|
||||||
postDataFAQ,
|
|
||||||
postDataFeedback,
|
|
||||||
} from "@/service/settings/settings";
|
|
||||||
import { Fragment, 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";
|
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
|
||||||
import { stringify } from "querystring";
|
|
||||||
import { useDropzone } from "react-dropzone";
|
|
||||||
import { CloudUpload } from "lucide-react";
|
|
||||||
import Image from "next/image";
|
|
||||||
import { Upload } from "tus-js-client";
|
|
||||||
import { id } from "date-fns/locale";
|
|
||||||
import { htmlToString } from "@/utils/globals";
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
question: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
publishTo: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
|
|
||||||
// publishTo: z.array(z.string()).refine((value) => value.some((item) => item), {
|
|
||||||
// message: "Required",
|
|
||||||
// }),
|
|
||||||
});
|
|
||||||
|
|
||||||
const publishToList = [
|
|
||||||
{
|
|
||||||
id: "mabes",
|
|
||||||
name: "Nasional",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "polda",
|
|
||||||
name: "Polda",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "satker",
|
|
||||||
name: "Satker",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "internasional",
|
|
||||||
name: "Internasional",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function EditFeedbackModal(props: {
|
|
||||||
id: string;
|
|
||||||
isDetail: boolean;
|
|
||||||
data: {
|
|
||||||
id: number;
|
|
||||||
question: string;
|
|
||||||
isInternational: boolean;
|
|
||||||
isActive: boolean;
|
|
||||||
};
|
|
||||||
}) {
|
|
||||||
const { id, isDetail, data } = props;
|
|
||||||
const router = useRouter();
|
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
initState();
|
|
||||||
}, [id]);
|
|
||||||
|
|
||||||
const initState = async () => {
|
|
||||||
form.setValue("question", htmlToString(data.question));
|
|
||||||
form.setValue(
|
|
||||||
"publishTo",
|
|
||||||
data.isInternational ? "international" : "wilayah"
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
|
||||||
const request = {
|
|
||||||
id: Number(id),
|
|
||||||
question: data.question,
|
|
||||||
isInternational: data.publishTo === "wilayah" ? false : true,
|
|
||||||
isActive: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await postDataFeedback(request);
|
|
||||||
close();
|
|
||||||
if (response?.error) {
|
|
||||||
toast({ title: stringify(response.message), variant: "destructive" });
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
toast({
|
|
||||||
title: "Succes",
|
|
||||||
description: "Feedback berhasil diubah",
|
|
||||||
});
|
|
||||||
router.push("/admin/settings/feedback?dataChange=true");
|
|
||||||
setIsOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<a className="hover:underline cursor-pointer">
|
|
||||||
{isDetail ? "Detail" : "Edit"}
|
|
||||||
</a>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent size="md">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>{isDetail ? "Detail" : "Edit"} Feedback</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
className="space-y-3 bg-white rounded-sm"
|
|
||||||
>
|
|
||||||
<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 space-x-1"
|
|
||||||
>
|
|
||||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
|
||||||
<FormControl>
|
|
||||||
<RadioGroupItem value="wilayah" />
|
|
||||||
</FormControl>
|
|
||||||
<FormLabel className="font-normal">Wilayah</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="publishTo"
|
|
||||||
render={() => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Wilayah Publish</FormLabel>
|
|
||||||
|
|
||||||
<div className="flex flex-row items-center gap-2">
|
|
||||||
<div className="flex gap-3 items-center">
|
|
||||||
<Checkbox
|
|
||||||
id="all"
|
|
||||||
disabled={isDetail}
|
|
||||||
checked={isAllTargetChecked}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
if (checked) {
|
|
||||||
form.setValue(
|
|
||||||
"publishTo",
|
|
||||||
publishToList.map((item) => item.id)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
form.setValue("publishTo", []);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<label htmlFor="all" className="text-sm">
|
|
||||||
Semua
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{publishToList.map((item) => (
|
|
||||||
<>
|
|
||||||
<FormField
|
|
||||||
key={item.id}
|
|
||||||
control={form.control}
|
|
||||||
name="publishTo"
|
|
||||||
render={({ field }) => {
|
|
||||||
return (
|
|
||||||
<FormItem
|
|
||||||
key={item.id}
|
|
||||||
className="flex flex-row items-start "
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<FormControl>
|
|
||||||
<Checkbox
|
|
||||||
disabled={isDetail}
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{item.id === "polda" &&
|
|
||||||
form.getValues("publishTo")?.includes(item.id) && (
|
|
||||||
<UnitMapping
|
|
||||||
unit="Polda"
|
|
||||||
isDetail={isDetail}
|
|
||||||
sendDataToParent={(data) => setUnitData(data)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{item.id === "satker" &&
|
|
||||||
form.getValues("publishTo")?.includes(item.id) && (
|
|
||||||
<UnitMapping
|
|
||||||
isDetail={isDetail}
|
|
||||||
unit="Satker"
|
|
||||||
sendDataToParent={(data) => setSatkerData(data)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/> */}
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="question"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Pertanyaan</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea
|
|
||||||
readOnly={isDetail}
|
|
||||||
placeholder="Masukkan pertanyaan"
|
|
||||||
value={field.value}
|
|
||||||
onChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{!isDetail && (
|
|
||||||
<DialogFooter>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
color="primary"
|
|
||||||
size="md"
|
|
||||||
className="text-xs"
|
|
||||||
>
|
|
||||||
Edit Feedback
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
)}
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,183 +0,0 @@
|
||||||
"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,
|
|
||||||
getListFAQ,
|
|
||||||
getListFeedback,
|
|
||||||
} from "@/service/settings/settings";
|
|
||||||
|
|
||||||
import CreateFAQModal from "./create";
|
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
|
|
||||||
const AdminFeedbackTable = () => {
|
|
||||||
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: 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(() => {
|
|
||||||
if (dataChange) {
|
|
||||||
router.push("/admin/settings/feedback");
|
|
||||||
}
|
|
||||||
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 getListFeedback();
|
|
||||||
const data = response?.data?.data;
|
|
||||||
console.log("respone", response);
|
|
||||||
data.forEach((item: any, index: number) => {
|
|
||||||
item.no = (page - 1) * 10 + index + 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
setDataTable(data);
|
|
||||||
setTotalData(data?.length);
|
|
||||||
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">Feedback</p>
|
|
||||||
<CreateFAQModal />
|
|
||||||
</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 AdminFeedbackTable;
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import AdminFeedbackTable from "./component/table";
|
|
||||||
|
|
||||||
export default function FAQSetting() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<AdminFeedbackTable />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,136 +0,0 @@
|
||||||
"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 { close, error, loading } from "@/config/swal";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import JoditEditor from "jodit-react";
|
|
||||||
import { useEffect, useRef } from "react";
|
|
||||||
import { getPrivacy, savePrivacy } from "@/service/settings/settings";
|
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
|
|
||||||
const CustomEditor = dynamic(
|
|
||||||
() => {
|
|
||||||
return import("@/components/editor/custom-editor");
|
|
||||||
},
|
|
||||||
{ ssr: false }
|
|
||||||
);
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
title: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
description: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function AdminPrivacyPolicy() {
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
});
|
|
||||||
const editor = useRef(null);
|
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getPrivacyData();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
async function getPrivacyData() {
|
|
||||||
const response = await getPrivacy("1");
|
|
||||||
console.log(response?.data?.data);
|
|
||||||
form.setValue("title", response?.data?.data?.title);
|
|
||||||
form.setValue("description", response?.data?.data?.htmlContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
|
||||||
const req = {
|
|
||||||
id: 1,
|
|
||||||
title: data.title,
|
|
||||||
htmlContent: data.description,
|
|
||||||
isActive: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await savePrivacy(req);
|
|
||||||
if (response?.error) {
|
|
||||||
error(response?.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
toast({
|
|
||||||
title: "Berhasil Simpan",
|
|
||||||
});
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
className="space-y-3 bg-white rounded-sm p-4"
|
|
||||||
>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="title"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Judul</FormLabel>
|
|
||||||
<Input
|
|
||||||
value={field.value}
|
|
||||||
placeholder="Masukkan Judul"
|
|
||||||
onChange={field.onChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="description"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Konten</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
{/* <JoditEditor
|
|
||||||
ref={editor}
|
|
||||||
value={field.value}
|
|
||||||
config={{
|
|
||||||
height: 400, // Tinggi editor dalam piksel
|
|
||||||
}}
|
|
||||||
className="dark:text-black"
|
|
||||||
onChange={field.onChange}
|
|
||||||
|
|
||||||
/> */}
|
|
||||||
<CustomEditor
|
|
||||||
onChange={field.onChange}
|
|
||||||
initialData={field.value}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<Button color="primary" type="submit">
|
|
||||||
Simpan
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
||||||
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 { error } from "@/config/swal";
|
|
||||||
import { deleteCategory, deleteDataFAQ } 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";
|
|
||||||
import { htmlToString } from "@/utils/globals";
|
|
||||||
import EditFAQModal from "./edit";
|
|
||||||
import EditFeedbackModal from "./edit";
|
|
||||||
import EditTagModal from "./edit";
|
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
|
||||||
{
|
|
||||||
accessorKey: "no",
|
|
||||||
header: "No",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
accessorKey: "tagName",
|
|
||||||
header: "Nama Tag",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">{row.getValue("tagName")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "categoryName",
|
|
||||||
header: "Kategori",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">{row.getValue("categoryName")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "contentCount",
|
|
||||||
header: "Jumlah Content",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">
|
|
||||||
{row.getValue("contentCount") ? row.getValue("contentCount") : "-"}
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
id: "actions",
|
|
||||||
accessorKey: "action",
|
|
||||||
header: "Actions",
|
|
||||||
enableHiding: false,
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const router = useRouter();
|
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
const tagDelete = async (id: string) => {
|
|
||||||
// const response = await deleteDataFAQ(id);
|
|
||||||
// console.log(response);
|
|
||||||
// if (response?.error) {
|
|
||||||
// error(response.message);
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
toast({
|
|
||||||
title: "Sukses",
|
|
||||||
description: "Berhasil Delete",
|
|
||||||
});
|
|
||||||
router.push("/admin/settings/tag?dataChange=true");
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<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">
|
|
||||||
<EditTagModal
|
|
||||||
id={row.original.id}
|
|
||||||
isDetail={true}
|
|
||||||
data={row.original}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<EditTagModal
|
|
||||||
id={row.original.id}
|
|
||||||
isDetail={false}
|
|
||||||
data={row.original}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<a
|
|
||||||
onClick={() => tagDelete(row.original.id)}
|
|
||||||
className="hover:underline cursor-pointer hover:text-destructive"
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</a>
|
|
||||||
</MenubarContent>
|
|
||||||
</MenubarMenu>
|
|
||||||
</Menubar>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -1,213 +0,0 @@
|
||||||
"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,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import { useRouter } from "@/i18n/routing";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
|
|
||||||
import { Fragment, useEffect, useState } from "react";
|
|
||||||
|
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
|
||||||
import { getCategories, getCategoriesAll } from "@/service/settings/settings";
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
import { Check, ChevronsUpDown } from "lucide-react";
|
|
||||||
import {
|
|
||||||
Command,
|
|
||||||
CommandEmpty,
|
|
||||||
CommandGroup,
|
|
||||||
CommandInput,
|
|
||||||
CommandItem,
|
|
||||||
CommandList,
|
|
||||||
} from "@/components/ui/command";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
name: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
category: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function CreateTagModal() {
|
|
||||||
const router = useRouter();
|
|
||||||
const { toast } = useToast();
|
|
||||||
const t = useTranslations("Menu");
|
|
||||||
const [categoryList, setCategoryList] = useState<
|
|
||||||
{ id: number; label: string; value: string }[]
|
|
||||||
>([]);
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
});
|
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
|
||||||
const request = {
|
|
||||||
tagName: data.name,
|
|
||||||
categoryId: Number(data.category),
|
|
||||||
isActive: true,
|
|
||||||
};
|
|
||||||
console.log("reqqq", request);
|
|
||||||
// const response = await postDataFeedback(request);
|
|
||||||
// close();
|
|
||||||
// if (response?.error) {
|
|
||||||
// toast({ title: stringify(response.message), variant: "destructive" });
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
toast({
|
|
||||||
title: "Succes",
|
|
||||||
description: "Tag berhasil dibuat",
|
|
||||||
});
|
|
||||||
router.push("/admin/settings/tag?dataChange=true");
|
|
||||||
setIsOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getCategoryParent();
|
|
||||||
}, []);
|
|
||||||
async function getCategoryParent() {
|
|
||||||
const response = await getCategoriesAll();
|
|
||||||
const res = response?.data?.data.content;
|
|
||||||
console.log("res", res);
|
|
||||||
var levelsArr: { id: number; label: string; value: string }[] = [];
|
|
||||||
res.forEach((levels: { id: number; name: string }) => {
|
|
||||||
levelsArr.push({
|
|
||||||
id: levels.id,
|
|
||||||
label: levels.name,
|
|
||||||
value: String(levels.id),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
setCategoryList(levelsArr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button color="primary" size="md">
|
|
||||||
{t("add-tags")}
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent size="md">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle> {t("add-tags")}</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
className="space-y-3 bg-white rounded-sm"
|
|
||||||
>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="category"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex flex-col">
|
|
||||||
<FormLabel>Pilih Category</FormLabel>
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<FormControl>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
role="combobox"
|
|
||||||
className={cn(
|
|
||||||
"w-[400px] justify-between",
|
|
||||||
!field.value && "text-muted-foreground"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{field.value
|
|
||||||
? categoryList.find(
|
|
||||||
(categ) => categ.value === field.value
|
|
||||||
)?.label
|
|
||||||
: "Pilih level"}
|
|
||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
||||||
</Button>
|
|
||||||
</FormControl>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-[400px] p-0">
|
|
||||||
<Command>
|
|
||||||
<CommandInput />
|
|
||||||
<CommandList>
|
|
||||||
<CommandEmpty>No role found.</CommandEmpty>
|
|
||||||
<CommandGroup>
|
|
||||||
{categoryList.map((role) => (
|
|
||||||
<CommandItem
|
|
||||||
value={role.label}
|
|
||||||
key={role.value}
|
|
||||||
onSelect={() => {
|
|
||||||
form.setValue("category", role.value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{role.label}
|
|
||||||
<Check
|
|
||||||
className={cn(
|
|
||||||
"ml-auto",
|
|
||||||
role.value === field.value
|
|
||||||
? "opacity-100"
|
|
||||||
: "opacity-0"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</CommandItem>
|
|
||||||
))}
|
|
||||||
</CommandGroup>
|
|
||||||
</CommandList>
|
|
||||||
</Command>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="name"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Nama Tag</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder="Masukkan nama tag" {...field} />
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<DialogFooter>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
color="primary"
|
|
||||||
size="md"
|
|
||||||
className="text-xs"
|
|
||||||
>
|
|
||||||
Tambah Tag
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,273 +0,0 @@
|
||||||
"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 {
|
|
||||||
detailDataFAQ,
|
|
||||||
detailDataFeedback,
|
|
||||||
getCategoriesAll,
|
|
||||||
getUserRoles,
|
|
||||||
postCategory,
|
|
||||||
postDataFAQ,
|
|
||||||
postDataFeedback,
|
|
||||||
} from "@/service/settings/settings";
|
|
||||||
import { Fragment, useEffect, useState } from "react";
|
|
||||||
|
|
||||||
import { close, error, loading } from "@/config/swal";
|
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
|
||||||
import { stringify } from "querystring";
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
import { Check, ChevronsUpDown } from "lucide-react";
|
|
||||||
import {
|
|
||||||
Command,
|
|
||||||
CommandEmpty,
|
|
||||||
CommandGroup,
|
|
||||||
CommandInput,
|
|
||||||
CommandItem,
|
|
||||||
CommandList,
|
|
||||||
} from "@/components/ui/command";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
name: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
category: z.string({
|
|
||||||
required_error: "Required",
|
|
||||||
}),
|
|
||||||
|
|
||||||
// publishTo: z.array(z.string()).refine((value) => value.some((item) => item), {
|
|
||||||
// message: "Required",
|
|
||||||
// }),
|
|
||||||
});
|
|
||||||
|
|
||||||
const publishToList = [
|
|
||||||
{
|
|
||||||
id: "mabes",
|
|
||||||
name: "Nasional",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "polda",
|
|
||||||
name: "Polda",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "satker",
|
|
||||||
name: "Satker",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "internasional",
|
|
||||||
name: "Internasional",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function EditTagModal(props: {
|
|
||||||
id: string;
|
|
||||||
isDetail: boolean;
|
|
||||||
data: {
|
|
||||||
id: number;
|
|
||||||
tagName: string;
|
|
||||||
categoryId: number;
|
|
||||||
subCategoryId: number;
|
|
||||||
};
|
|
||||||
}) {
|
|
||||||
const { id, isDetail, data } = props;
|
|
||||||
const router = useRouter();
|
|
||||||
const { toast } = useToast();
|
|
||||||
const [categoryList, setCategoryList] = useState<
|
|
||||||
{ id: number; label: string; value: string }[]
|
|
||||||
>([]);
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
resolver: zodResolver(FormSchema),
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
initState();
|
|
||||||
}, [id]);
|
|
||||||
|
|
||||||
const initState = async () => {
|
|
||||||
form.setValue("name", data.tagName);
|
|
||||||
form.setValue("category", String(data.categoryId));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
|
||||||
const request = {
|
|
||||||
id: Number(id),
|
|
||||||
tagName: data.name,
|
|
||||||
categoryId: Number(data.category),
|
|
||||||
isActive: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await postDataFeedback(request);
|
|
||||||
close();
|
|
||||||
if (response?.error) {
|
|
||||||
toast({ title: stringify(response.message), variant: "destructive" });
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
toast({
|
|
||||||
title: "Succes",
|
|
||||||
description: "Tag berhasil diubah",
|
|
||||||
});
|
|
||||||
router.push("/admin/settings/tag?dataChange=true");
|
|
||||||
setIsOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getCategoryParent();
|
|
||||||
}, []);
|
|
||||||
async function getCategoryParent() {
|
|
||||||
const response = await getCategoriesAll();
|
|
||||||
const res = response?.data?.data.content;
|
|
||||||
console.log("res", res);
|
|
||||||
var levelsArr: { id: number; label: string; value: string }[] = [];
|
|
||||||
res.forEach((levels: { id: number; name: string }) => {
|
|
||||||
levelsArr.push({
|
|
||||||
id: levels.id,
|
|
||||||
label: levels.name,
|
|
||||||
value: String(levels.id),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
setCategoryList(levelsArr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<a className="hover:underline cursor-pointer">
|
|
||||||
{isDetail ? "Detail" : "Edit"}
|
|
||||||
</a>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent size="md">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>{isDetail ? "Detail" : "Edit"} Tag</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
className="space-y-3 bg-white rounded-sm"
|
|
||||||
>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="category"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex flex-col">
|
|
||||||
<FormLabel>Pilih Category</FormLabel>
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild disabled={isDetail}>
|
|
||||||
<FormControl>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
role="combobox"
|
|
||||||
className={cn(
|
|
||||||
"w-[400px] justify-between",
|
|
||||||
!field.value && "text-muted-foreground"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{field.value
|
|
||||||
? categoryList.find(
|
|
||||||
(categ) => categ.value === field.value
|
|
||||||
)?.label
|
|
||||||
: "Pilih level"}
|
|
||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
||||||
</Button>
|
|
||||||
</FormControl>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-[400px] p-0">
|
|
||||||
<Command>
|
|
||||||
<CommandInput />
|
|
||||||
<CommandList>
|
|
||||||
<CommandEmpty>No role found.</CommandEmpty>
|
|
||||||
<CommandGroup>
|
|
||||||
{categoryList.map((role) => (
|
|
||||||
<CommandItem
|
|
||||||
value={role.label}
|
|
||||||
key={role.value}
|
|
||||||
onSelect={() => {
|
|
||||||
form.setValue("category", role.value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{role.label}
|
|
||||||
<Check
|
|
||||||
className={cn(
|
|
||||||
"ml-auto",
|
|
||||||
role.value === field.value
|
|
||||||
? "opacity-100"
|
|
||||||
: "opacity-0"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</CommandItem>
|
|
||||||
))}
|
|
||||||
</CommandGroup>
|
|
||||||
</CommandList>
|
|
||||||
</Command>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="name"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Nama Tag</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
readOnly={isDetail}
|
|
||||||
placeholder="Masukkan nama tag"
|
|
||||||
value={field.value}
|
|
||||||
onChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{!isDetail && (
|
|
||||||
<DialogFooter>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
color="primary"
|
|
||||||
size="md"
|
|
||||||
className="text-xs"
|
|
||||||
>
|
|
||||||
Edit Tag
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
)}
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,183 +0,0 @@
|
||||||
"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,
|
|
||||||
getListFAQ,
|
|
||||||
getListFeedback,
|
|
||||||
getTags,
|
|
||||||
} from "@/service/settings/settings";
|
|
||||||
|
|
||||||
import CreateFAQModal from "./create";
|
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
|
|
||||||
const AdminTagTable = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const t = useTranslations("Menu");
|
|
||||||
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: 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(() => {
|
|
||||||
if (dataChange) {
|
|
||||||
router.push("/admin/settings/tag");
|
|
||||||
}
|
|
||||||
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 getTags();
|
|
||||||
const data = response?.data?.data;
|
|
||||||
data.forEach((item: any, index: number) => {
|
|
||||||
item.no = (page - 1) * 10 + index + 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
setDataTable(data);
|
|
||||||
setTotalData(data?.length);
|
|
||||||
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">{t("tags")}</p>
|
|
||||||
<CreateFAQModal />
|
|
||||||
</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 AdminTagTable;
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import AdminTagTable from "./component/table";
|
|
||||||
|
|
||||||
export default function TagCategory() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<AdminTagTable />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
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 { format } from "date-fns";
|
|
||||||
import header from "@/components/partials/header";
|
|
||||||
import { date } from "zod";
|
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
|
||||||
{
|
|
||||||
accessorKey: "no",
|
|
||||||
header: "No",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "createdAt",
|
|
||||||
header: "Tanggal",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const createdAt = row.getValue("createdAt") as
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| undefined;
|
|
||||||
|
|
||||||
const formattedDate =
|
|
||||||
createdAt && !isNaN(new Date(createdAt).getTime())
|
|
||||||
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
|
|
||||||
: "-";
|
|
||||||
return <span className="whitespace-nowrap">{formattedDate}</span>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "createdByCategory",
|
|
||||||
header: "Jenis Akun",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">{row.getValue("createdByCategory")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "createdByUsername",
|
|
||||||
header: "UserName",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">{row.getValue("createdByUsername")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "accessFrequency",
|
|
||||||
header: "Akses Mediahub",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">{row.getValue("accessFrequency")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "uiExperienceDesign",
|
|
||||||
header: "Tampilan Desain Web",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">{row.getValue("uiExperienceDesign")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "uiExperienceNavigation",
|
|
||||||
header: "Kemudahan Navigasi",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">
|
|
||||||
{row.getValue("uiExperienceNavigation")}
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "uiExperienceSpeed",
|
|
||||||
header: "Kecepatan Akses",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="normal-case">{row.getValue("uiExperienceSpeed")}</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">
|
|
||||||
<Link href={`/admin/survey/detail/${row.original.id}`}>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<Eye className="w-4 h-4 me-1.5 mt-1" />
|
|
||||||
View
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -1,347 +0,0 @@
|
||||||
"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";
|
|
||||||
import search from "../../../app/chat/components/search";
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectGroup,
|
|
||||||
SelectItem,
|
|
||||||
SelectLabel,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/components/ui/select";
|
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
|
||||||
import {
|
|
||||||
Bar,
|
|
||||||
BarChart,
|
|
||||||
CartesianGrid,
|
|
||||||
Label,
|
|
||||||
ResponsiveContainer,
|
|
||||||
Tooltip,
|
|
||||||
XAxis,
|
|
||||||
YAxis,
|
|
||||||
} from "recharts";
|
|
||||||
import { getSurveyData } from "@/service/survey/survey";
|
|
||||||
|
|
||||||
const data = [
|
|
||||||
{
|
|
||||||
question: "Seberapa Sering User Mengakses\nMediaHub Polri",
|
|
||||||
total: 100,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question: "Tampilan dan Desain pada Website\nMediaHub Polri",
|
|
||||||
total: 90,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question: "Kemudahan Navigasi pada Website\nMediaHub Polri",
|
|
||||||
total: 80,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question: "Kecepatan Akses pada Website\nMediaHub Polri",
|
|
||||||
total: 70,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question:
|
|
||||||
"Seberapa Akurat dan Terpercaya\nInformasi pada Website MediaHub Polri",
|
|
||||||
total: 60,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question:
|
|
||||||
"Seberapa Lengkap Informasi dan Berita\npada Website MediaHub Polri",
|
|
||||||
total: 55,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question: "Seberapa membantu dalam\nmendapatkan Informasi terkait Polri",
|
|
||||||
total: 52,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const SurveyListTable = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
|
||||||
const [totalData, setTotalData] = React.useState<number>(1);
|
|
||||||
const [search, setSearch] = React.useState<string>("");
|
|
||||||
const [showData, setShowData] = React.useState("polri");
|
|
||||||
const [startDate, setStartDate] = React.useState("");
|
|
||||||
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 getSurveyData();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setSearch(e.target.value); // Perbarui state search
|
|
||||||
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">Survey</div>
|
|
||||||
<div className="flex flex-row gap-2 items-center justify-between">
|
|
||||||
<div className="w-full md:w-[200px] lg:w-[300px] px-2">
|
|
||||||
<InputGroup merged>
|
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
|
||||||
<Search className=" h-4 w-4 dark:text-white" />
|
|
||||||
</InputGroupText>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
placeholder="Search Judul..."
|
|
||||||
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
|
||||||
value={search}
|
|
||||||
onChange={handleSearch}
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-row gap-2 items-center">
|
|
||||||
<div className="mx-2 my-1">
|
|
||||||
<Input
|
|
||||||
type="date"
|
|
||||||
value={startDate}
|
|
||||||
onChange={(e) => setStartDate(e.target.value)}
|
|
||||||
className="max-w-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mx-3">
|
|
||||||
<Select>
|
|
||||||
<SelectTrigger className="w-[150px]">
|
|
||||||
<SelectValue placeholder="Select a filter" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectGroup>
|
|
||||||
<SelectLabel>Filter</SelectLabel>
|
|
||||||
<SelectItem value="polri">Polri</SelectItem>
|
|
||||||
<SelectItem value="umum">Umum</SelectItem>
|
|
||||||
<SelectItem value="jurnalist">Journalist</SelectItem>
|
|
||||||
<SelectItem value="ksp">Ksp</SelectItem>
|
|
||||||
</SelectGroup>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="p-3">
|
|
||||||
<h2 className="text-center font-semibold mb-4">
|
|
||||||
Survei Kepuasan Pengguna MediaHub Polri
|
|
||||||
</h2>
|
|
||||||
<ResponsiveContainer width="100%" height={500}>
|
|
||||||
<BarChart
|
|
||||||
layout="vertical"
|
|
||||||
data={data}
|
|
||||||
margin={{ top: 20, right: 30, left: 30, bottom: 80 }}
|
|
||||||
>
|
|
||||||
<CartesianGrid strokeDasharray="5 5" />
|
|
||||||
<XAxis type="number">
|
|
||||||
<Label
|
|
||||||
value="Total Survei"
|
|
||||||
offset={-30}
|
|
||||||
position="insideBottom"
|
|
||||||
style={{ textAnchor: "middle", fontStyle: "italic" }}
|
|
||||||
/>
|
|
||||||
</XAxis>
|
|
||||||
<YAxis
|
|
||||||
type="category"
|
|
||||||
dataKey="question"
|
|
||||||
width={240} // Lebarkan agar teks muat
|
|
||||||
tick={{
|
|
||||||
fontSize: 12,
|
|
||||||
}}
|
|
||||||
tickFormatter={(value: string) =>
|
|
||||||
value.length > 40 ? value.slice(0, 37) + "..." : value
|
|
||||||
} // Atur potong teks panjang, atau hapus kalau mau tampil semua
|
|
||||||
>
|
|
||||||
<Label
|
|
||||||
value="Pertanyaan"
|
|
||||||
angle={-90}
|
|
||||||
position="insideLeft"
|
|
||||||
style={{ textAnchor: "middle", fontStyle: "italic" }}
|
|
||||||
dx={-20}
|
|
||||||
/>
|
|
||||||
</YAxis>
|
|
||||||
<Tooltip />
|
|
||||||
<Bar dataKey="total" fill="#3163d4" />
|
|
||||||
</BarChart>
|
|
||||||
</ResponsiveContainer>
|
|
||||||
</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 SurveyListTable;
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import FormBlogDetail from "@/components/form/blog/blog--detail-form";
|
|
||||||
import FormSurvey from "@/components/landing-page/survey";
|
|
||||||
import FormSurveyDetail from "@/components/form/survey/survey-detail";
|
|
||||||
|
|
||||||
const SurveyDetailPage = async () => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<div className="space-y-4">
|
|
||||||
<FormSurveyDetail />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SurveyDetailPage;
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
"use client";
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { Link } from "@/i18n/routing";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import SurveyListTable from "./component/table";
|
|
||||||
|
|
||||||
export default function AdminSurvey() {
|
|
||||||
const [tab, setTab] = useState("Email Blast");
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
|
||||||
<SurveyListTable />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,243 +0,0 @@
|
||||||
"use client";
|
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import FullCalendar from "@fullcalendar/react"; // must go before plugins
|
|
||||||
import dayGridPlugin from "@fullcalendar/daygrid";
|
|
||||||
import timeGridPlugin from "@fullcalendar/timegrid";
|
|
||||||
import interactionPlugin, { Draggable } from "@fullcalendar/interaction";
|
|
||||||
import listPlugin from "@fullcalendar/list";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import ExternalDraggingevent from "./dragging-events";
|
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
|
||||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
|
||||||
import { Plus } from "lucide-react";
|
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
|
||||||
import { CalendarEvent, CalendarCategory } from "./data"
|
|
||||||
import {
|
|
||||||
EventContentArg,
|
|
||||||
} from '@fullcalendar/core'
|
|
||||||
import EventModal from "./event-modal";
|
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
const wait = () => new Promise((resolve) => setTimeout(resolve, 1000));
|
|
||||||
interface CalendarViewProps {
|
|
||||||
events: CalendarEvent[];
|
|
||||||
categories: CalendarCategory[];
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const CalendarView = ({ events, categories }: CalendarViewProps) => {
|
|
||||||
const [selectedCategory, setSelectedCategory] = useState<string[] | null>(null);
|
|
||||||
const [selectedEventDate, setSelectedEventDate] = useState<Date | null>(null);
|
|
||||||
const [selectedEvent, setSelectedEvent] = useState<CalendarEvent | null>(null);
|
|
||||||
const [draggableInitialized, setDraggableInitialized] = useState<boolean>(false);
|
|
||||||
const t = useTranslations("CalendarApp")
|
|
||||||
// event canvas state
|
|
||||||
const [sheetOpen, setSheetOpen] = useState<boolean>(false);
|
|
||||||
const [date, setDate] = React.useState<Date>(new Date());
|
|
||||||
|
|
||||||
const [dragEvents] = useState([
|
|
||||||
{ title: "New Event Planning", id: "101", tag: "business" },
|
|
||||||
{ title: "Meeting", id: "102", tag: "meeting" },
|
|
||||||
{ title: "Generating Reports", id: "103", tag: "holiday" },
|
|
||||||
{ title: "Create New theme", id: "104", tag: "etc" },
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setSelectedCategory(categories?.map((c) => c.value));
|
|
||||||
}, [events, categories]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const draggableEl = document.getElementById("external-events");
|
|
||||||
|
|
||||||
const initDraggable = () => {
|
|
||||||
if (draggableEl) {
|
|
||||||
new Draggable(draggableEl, {
|
|
||||||
itemSelector: ".fc-event",
|
|
||||||
eventData: function (eventEl) {
|
|
||||||
let title = eventEl.getAttribute("title");
|
|
||||||
let id = eventEl.getAttribute("data");
|
|
||||||
let event = dragEvents.find((e) => e.id === id);
|
|
||||||
let tag = event ? event.tag : "";
|
|
||||||
return {
|
|
||||||
title: title,
|
|
||||||
id: id,
|
|
||||||
extendedProps: {
|
|
||||||
calendar: tag,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dragEvents.length > 0) {
|
|
||||||
initDraggable();
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
draggableEl?.removeEventListener("mousedown", initDraggable);
|
|
||||||
};
|
|
||||||
}, [dragEvents]);
|
|
||||||
// event click
|
|
||||||
const handleEventClick = (arg: any) => {
|
|
||||||
setSelectedEventDate(null);
|
|
||||||
setSheetOpen(true);
|
|
||||||
setSelectedEvent(arg);
|
|
||||||
wait().then(() => (document.body.style.pointerEvents = "auto"));
|
|
||||||
};
|
|
||||||
// handle close modal
|
|
||||||
const handleCloseModal = () => {
|
|
||||||
setSheetOpen(false);
|
|
||||||
setSelectedEvent(null);
|
|
||||||
setSelectedEventDate(null);
|
|
||||||
};
|
|
||||||
const handleDateClick = (arg: any) => {
|
|
||||||
setSheetOpen(true);
|
|
||||||
setSelectedEventDate(arg);
|
|
||||||
setSelectedEvent(null);
|
|
||||||
wait().then(() => (document.body.style.pointerEvents = "auto"));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCategorySelection = (category: string) => {
|
|
||||||
if (selectedCategory && selectedCategory.includes(category)) {
|
|
||||||
setSelectedCategory(selectedCategory.filter((c) => c !== category));
|
|
||||||
} else {
|
|
||||||
setSelectedCategory([...selectedCategory || [], category]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClassName = (arg: EventContentArg) => {
|
|
||||||
|
|
||||||
if (arg.event.extendedProps.calendar === "holiday") {
|
|
||||||
return "destructive";
|
|
||||||
}
|
|
||||||
else if (arg.event.extendedProps.calendar === "business") {
|
|
||||||
return "primary";
|
|
||||||
} else if (arg.event.extendedProps.calendar === "personal") {
|
|
||||||
return "success";
|
|
||||||
} else if (arg.event.extendedProps.calendar === "family") {
|
|
||||||
return "info";
|
|
||||||
} else if (arg.event.extendedProps.calendar === "etc") {
|
|
||||||
return "info";
|
|
||||||
} else if (arg.event.extendedProps.calendar === "meeting") {
|
|
||||||
return "warning";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return "primary";
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const filteredEvents = events?.filter((event) =>
|
|
||||||
selectedCategory?.includes(event.extendedProps.calendar)
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="grid grid-cols-12 gap-6 divide-x divide-border">
|
|
||||||
<Card className="col-span-12 lg:col-span-4 2xl:col-span-3 pb-5">
|
|
||||||
<CardContent className="p-0">
|
|
||||||
<CardHeader className="border-none mb-2 pt-5">
|
|
||||||
<Button
|
|
||||||
onClick={handleDateClick}
|
|
||||||
className="dark:bg-background dark:text-foreground"
|
|
||||||
>
|
|
||||||
<Plus className="w-4 h-4 me-1" />
|
|
||||||
{t("addEvent")}
|
|
||||||
</Button>
|
|
||||||
</CardHeader>
|
|
||||||
<div className="px-3">
|
|
||||||
<Calendar
|
|
||||||
mode="single"
|
|
||||||
selected={date}
|
|
||||||
onSelect={(s) => {
|
|
||||||
handleDateClick(s);
|
|
||||||
}}
|
|
||||||
className="rounded-md border w-full p-0 border-none"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="external-events" className=" space-y-1.5 mt-6 px-4">
|
|
||||||
<p className="text-sm font-medium text-default-700 mb-3">
|
|
||||||
{t("shortDesc")}
|
|
||||||
</p>
|
|
||||||
{dragEvents.map((event) => (
|
|
||||||
<ExternalDraggingevent key={event.id} event={event} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="py-4 text-default-800 font-semibold text-xs uppercase mt-4 mb-2 px-4">
|
|
||||||
{t("filter")}
|
|
||||||
</div>
|
|
||||||
<ul className="space-y-3 px-4">
|
|
||||||
<li className=" flex gap-3">
|
|
||||||
<Checkbox
|
|
||||||
checked={selectedCategory?.length === categories?.length}
|
|
||||||
onClick={() => {
|
|
||||||
if (selectedCategory?.length === categories?.length) {
|
|
||||||
setSelectedCategory([]);
|
|
||||||
} else {
|
|
||||||
setSelectedCategory(categories.map((c) => c.value));
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Label>All</Label>
|
|
||||||
</li>
|
|
||||||
{categories?.map((category) => (
|
|
||||||
<li className="flex gap-3 " key={category.value}>
|
|
||||||
<Checkbox
|
|
||||||
className={category.className}
|
|
||||||
id={category.label}
|
|
||||||
checked={selectedCategory?.includes(category.value)}
|
|
||||||
onClick={() => handleCategorySelection(category.value)}
|
|
||||||
/>
|
|
||||||
<Label htmlFor={category.label}>{category.label}</Label>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="col-span-12 lg:col-span-8 2xl:col-span-9 pt-5">
|
|
||||||
<CardContent className="dashcode-app-calendar">
|
|
||||||
<FullCalendar
|
|
||||||
plugins={[
|
|
||||||
dayGridPlugin,
|
|
||||||
timeGridPlugin,
|
|
||||||
interactionPlugin,
|
|
||||||
listPlugin,
|
|
||||||
]}
|
|
||||||
headerToolbar={{
|
|
||||||
left: "prev,next today",
|
|
||||||
center: "title",
|
|
||||||
right: "dayGridMonth,timeGridWeek,timeGridDay,listWeek",
|
|
||||||
}}
|
|
||||||
events={filteredEvents}
|
|
||||||
editable={true}
|
|
||||||
rerenderDelay={10}
|
|
||||||
eventDurationEditable={false}
|
|
||||||
selectable={true}
|
|
||||||
selectMirror={true}
|
|
||||||
droppable={true}
|
|
||||||
dayMaxEvents={2}
|
|
||||||
weekends={true}
|
|
||||||
eventClassNames={handleClassName}
|
|
||||||
dateClick={handleDateClick}
|
|
||||||
eventClick={handleEventClick}
|
|
||||||
initialView="dayGridMonth"
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
<EventModal
|
|
||||||
open={sheetOpen}
|
|
||||||
onClose={handleCloseModal}
|
|
||||||
categories={categories}
|
|
||||||
event={selectedEvent}
|
|
||||||
selectedDate={selectedEventDate}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CalendarView;
|
|
||||||
|
|
@ -1,155 +0,0 @@
|
||||||
import { faker } from "@faker-js/faker";
|
|
||||||
|
|
||||||
const date = new Date();
|
|
||||||
const prevDay = new Date().getDate() - 1;
|
|
||||||
const nextDay = new Date(new Date().getTime() + 24 * 60 * 60 * 1000);
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
const nextMonth = date.getMonth() === 11 ? new Date(date.getFullYear() + 1, 0, 1) : new Date(date.getFullYear(), date.getMonth() + 1, 1)
|
|
||||||
// prettier-ignore
|
|
||||||
const prevMonth = date.getMonth() === 11 ? new Date(date.getFullYear() - 1, 0, 1) : new Date(date.getFullYear(), date.getMonth() - 1, 1)
|
|
||||||
export const calendarEvents = [
|
|
||||||
{
|
|
||||||
id: faker.string.uuid() ,
|
|
||||||
title: "All Day Event",
|
|
||||||
start: date,
|
|
||||||
end: nextDay,
|
|
||||||
allDay: false,
|
|
||||||
//className: "warning",
|
|
||||||
extendedProps: {
|
|
||||||
calendar: "business",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: faker.string.uuid(),
|
|
||||||
title: "Meeting With Client",
|
|
||||||
start: new Date(date.getFullYear(), date.getMonth() + 1, -11),
|
|
||||||
end: new Date(date.getFullYear(), date.getMonth() + 1, -10),
|
|
||||||
allDay: true,
|
|
||||||
//className: "success",
|
|
||||||
extendedProps: {
|
|
||||||
calendar: "personal",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: faker.string.uuid(),
|
|
||||||
title: "Lunch",
|
|
||||||
allDay: true,
|
|
||||||
start: new Date(date.getFullYear(), date.getMonth() + 1, -9),
|
|
||||||
end: new Date(date.getFullYear(), date.getMonth() + 1, -7),
|
|
||||||
// className: "info",
|
|
||||||
extendedProps: {
|
|
||||||
calendar: "family",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: faker.string.uuid(),
|
|
||||||
title: "Birthday Party",
|
|
||||||
start: new Date(date.getFullYear(), date.getMonth() + 1, -11),
|
|
||||||
end: new Date(date.getFullYear(), date.getMonth() + 1, -10),
|
|
||||||
allDay: true,
|
|
||||||
//className: "primary",
|
|
||||||
extendedProps: {
|
|
||||||
calendar: "meeting",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: faker.string.uuid(),
|
|
||||||
title: "Birthday Party",
|
|
||||||
start: new Date(date.getFullYear(), date.getMonth() + 1, -13),
|
|
||||||
end: new Date(date.getFullYear(), date.getMonth() + 1, -12),
|
|
||||||
allDay: true,
|
|
||||||
// className: "danger",
|
|
||||||
extendedProps: {
|
|
||||||
calendar: "holiday",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: faker.string.uuid(),
|
|
||||||
title: "Monthly Meeting",
|
|
||||||
start: nextMonth,
|
|
||||||
end: nextMonth,
|
|
||||||
allDay: true,
|
|
||||||
//className: "primary",
|
|
||||||
extendedProps: {
|
|
||||||
calendar: "business",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const calendarCategories = [
|
|
||||||
{
|
|
||||||
label: "Business",
|
|
||||||
value: "business",
|
|
||||||
activeClass: "ring-primary-500 bg-primary-500",
|
|
||||||
className: "group-hover:border-blue-500",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Personal",
|
|
||||||
value: "personal",
|
|
||||||
activeClass: "ring-success-500 bg-success-500",
|
|
||||||
className: " group-hover:border-green-500",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Holiday",
|
|
||||||
value: "holiday",
|
|
||||||
activeClass: "ring-danger-500 bg-danger-500",
|
|
||||||
className: " group-hover:border-red-500",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Family",
|
|
||||||
value: "family",
|
|
||||||
activeClass: "ring-info-500 bg-info-500",
|
|
||||||
className: " group-hover:border-cyan-500",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Meeting",
|
|
||||||
value: "meeting",
|
|
||||||
activeClass: "ring-warning-500 bg-warning-500",
|
|
||||||
className: " group-hover:border-yellow-500",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Etc",
|
|
||||||
value: "etc",
|
|
||||||
activeClass: "ring-info-500 bg-info-500",
|
|
||||||
className: " group-hover:border-cyan-500",
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const categories = [
|
|
||||||
{
|
|
||||||
label: "Business",
|
|
||||||
value: "business",
|
|
||||||
className: "data-[state=checked]:bg-primary data-[state=checked]:ring-primary",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Personal",
|
|
||||||
value: "personal",
|
|
||||||
|
|
||||||
className: "data-[state=checked]:bg-success data-[state=checked]:ring-success",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Holiday",
|
|
||||||
value: "holiday",
|
|
||||||
className: "data-[state=checked]:bg-destructive data-[state=checked]:ring-destructive ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Family",
|
|
||||||
value: "family",
|
|
||||||
className: "data-[state=checked]:bg-info data-[state=checked]:ring-info ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Meeting",
|
|
||||||
value: "meeting",
|
|
||||||
className: "data-[state=checked]:bg-warning data-[state=checked]:ring-warning",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Etc",
|
|
||||||
value: "etc",
|
|
||||||
className: "data-[state=checked]:bg-info data-[state=checked]:ring-info",
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export type CalendarEvent = (typeof calendarEvents)[number]
|
|
||||||
export type CalendarCategory = (typeof calendarCategories)[number]
|
|
||||||
export type Category = (typeof categories)[number]
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
const ExternalDraggingevent = ({ event }: any) => {
|
|
||||||
const { title, id, tag } = event;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
title={title}
|
|
||||||
data-id={id}
|
|
||||||
className="fc-event px-4 py-1.5 bg-default-100 dark:bg-default-300 rounded text-sm flex items-center gap-2 shadow-sm cursor-move" >
|
|
||||||
<span
|
|
||||||
className={cn("h-2 w-2 rounded-full block", {
|
|
||||||
"bg-primary": tag === "business",
|
|
||||||
"bg-warning": tag === "meeting",
|
|
||||||
"bg-destructive": tag === "holiday",
|
|
||||||
"bg-info": tag === "etc",
|
|
||||||
})}
|
|
||||||
></span>
|
|
||||||
<span className="text-sm font-medium text-default-900">{title}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ExternalDraggingevent;
|
|
||||||
|
|
@ -1,285 +0,0 @@
|
||||||
"use client";
|
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import { useForm, Controller } from "react-hook-form";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { format } from "date-fns";
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/components/ui/select";
|
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { z } from "zod";
|
|
||||||
import { Loader2, CalendarIcon } from "lucide-react";
|
|
||||||
import DeleteConfirmationDialog from "@/components/delete-confirmation-dialog";
|
|
||||||
import { CalendarCategory } from "./data";
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
} from "@/components/ui/dialog";
|
|
||||||
|
|
||||||
const schema = z.object({
|
|
||||||
title: z.string().min(3, { message: "Required" }),
|
|
||||||
});
|
|
||||||
|
|
||||||
const EventModal = ({
|
|
||||||
open,
|
|
||||||
onClose,
|
|
||||||
categories,
|
|
||||||
event,
|
|
||||||
selectedDate,
|
|
||||||
}: {
|
|
||||||
open: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
categories: any;
|
|
||||||
event: any;
|
|
||||||
selectedDate: any;
|
|
||||||
}) => {
|
|
||||||
const [startDate, setStartDate] = useState<Date>(new Date());
|
|
||||||
const [endDate, setEndDate] = useState<Date>(new Date());
|
|
||||||
const [isPending, startTransition] = React.useTransition();
|
|
||||||
const [calendarProps, setCalendarProps] = React.useState<any>(
|
|
||||||
categories[0].value
|
|
||||||
);
|
|
||||||
// delete modal state
|
|
||||||
const [deleteModalOpen, setDeleteModalOpen] = useState<boolean>(false);
|
|
||||||
const [eventIdToDelete, setEventIdToDelete] = useState<string | null>(null);
|
|
||||||
|
|
||||||
const {
|
|
||||||
register,
|
|
||||||
control,
|
|
||||||
reset,
|
|
||||||
setValue,
|
|
||||||
formState: { errors },
|
|
||||||
handleSubmit,
|
|
||||||
} = useForm({
|
|
||||||
resolver: zodResolver(schema),
|
|
||||||
mode: "all",
|
|
||||||
});
|
|
||||||
|
|
||||||
const onSubmit = (data: any) => {
|
|
||||||
startTransition(() => {
|
|
||||||
if (!event) {
|
|
||||||
data.start = startDate;
|
|
||||||
data.end = endDate;
|
|
||||||
data.allDay = false;
|
|
||||||
data.extendedProps = {
|
|
||||||
calendar: calendarProps,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (event) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedDate) {
|
|
||||||
setStartDate(selectedDate.date);
|
|
||||||
setEndDate(selectedDate.date);
|
|
||||||
}
|
|
||||||
if (event) {
|
|
||||||
setStartDate(event?.event?.start);
|
|
||||||
setEndDate(event?.event?.end);
|
|
||||||
const eventCalendar = event?.event?.extendedProps?.calendar;
|
|
||||||
if (eventCalendar) {
|
|
||||||
setCalendarProps(eventCalendar);
|
|
||||||
} else {
|
|
||||||
setCalendarProps(categories[0].value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setValue("title", event?.event?.title || "");
|
|
||||||
}, [event, selectedDate, open, categories, setValue]);
|
|
||||||
|
|
||||||
const onDeleteEventAction = async () => {
|
|
||||||
try {
|
|
||||||
} catch (error) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpenDeleteModal = (eventId: string) => {
|
|
||||||
setEventIdToDelete(eventId);
|
|
||||||
setDeleteModalOpen(true);
|
|
||||||
onClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<DeleteConfirmationDialog
|
|
||||||
open={deleteModalOpen}
|
|
||||||
onClose={() => setDeleteModalOpen(false)}
|
|
||||||
onConfirm={onDeleteEventAction}
|
|
||||||
defaultToast={false}
|
|
||||||
/>
|
|
||||||
<Dialog open={open} onOpenChange={onClose}>
|
|
||||||
<DialogContent onPointerDownOutside={onClose}>
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>
|
|
||||||
{event ? "Edit Event" : "Create Event"} {event?.title}
|
|
||||||
</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="mt-6 h-full">
|
|
||||||
<form className="h-full" onSubmit={handleSubmit(onSubmit)}>
|
|
||||||
<div className="space-y-4 pb-5 ">
|
|
||||||
<div className="space-y-1.5">
|
|
||||||
<Label htmlFor="title">Event Name</Label>
|
|
||||||
<Input
|
|
||||||
id="title"
|
|
||||||
type="text"
|
|
||||||
placeholder="Enter Event Name"
|
|
||||||
{...register("title")}
|
|
||||||
/>
|
|
||||||
{errors?.title?.message && (
|
|
||||||
<div className="text-destructive text-sm">
|
|
||||||
{typeof errors?.title?.message === "string"
|
|
||||||
? errors?.title?.message
|
|
||||||
: JSON.stringify(errors?.title?.message)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-1.5">
|
|
||||||
<Label htmlFor="startDate">Start Date </Label>
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="md"
|
|
||||||
className={cn(
|
|
||||||
"w-full justify-between text-left font-normal border-default-200 text-default-600 md:px-4",
|
|
||||||
!startDate && "text-muted-foreground"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{startDate ? (
|
|
||||||
format(startDate, "PP")
|
|
||||||
) : (
|
|
||||||
<span>Pick a date</span>
|
|
||||||
)}
|
|
||||||
<CalendarIcon className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-auto p-0">
|
|
||||||
<Controller
|
|
||||||
name="startDate"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Calendar
|
|
||||||
mode="single"
|
|
||||||
selected={startDate}
|
|
||||||
onSelect={(date) => setStartDate(date as Date)}
|
|
||||||
initialFocus
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1.5">
|
|
||||||
<Label htmlFor="endDate">End Date</Label>
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="md"
|
|
||||||
className={cn(
|
|
||||||
"w-full justify-between text-left font-normal border-default-200 text-default-600 md:px-4",
|
|
||||||
!endDate && "text-muted-foreground"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{endDate ? (
|
|
||||||
format(endDate, "PP")
|
|
||||||
) : (
|
|
||||||
<span>Pick a date</span>
|
|
||||||
)}
|
|
||||||
<CalendarIcon className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-auto p-0">
|
|
||||||
<Controller
|
|
||||||
name="endDate"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Calendar
|
|
||||||
mode="single"
|
|
||||||
selected={endDate}
|
|
||||||
onSelect={(date) => setEndDate(date as Date)}
|
|
||||||
initialFocus
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-1.5">
|
|
||||||
<Label htmlFor="calendarProps">Label </Label>
|
|
||||||
<Controller
|
|
||||||
name="calendarProps"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Select
|
|
||||||
value={calendarProps}
|
|
||||||
onValueChange={(data) => setCalendarProps(data)}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Label" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{categories.map((category: CalendarCategory) => (
|
|
||||||
<SelectItem
|
|
||||||
value={category.value}
|
|
||||||
key={category.value}
|
|
||||||
>
|
|
||||||
{category.label}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-2 mt-10">
|
|
||||||
<Button type="submit" disabled={isPending} className="flex-1">
|
|
||||||
{isPending ? (
|
|
||||||
<>
|
|
||||||
<Loader2 className="me-2 h-4 w-4 animate-spin" />
|
|
||||||
{event ? "Updating..." : "Adding..."}
|
|
||||||
</>
|
|
||||||
) : event ? (
|
|
||||||
"Update Event"
|
|
||||||
) : (
|
|
||||||
"Add Event"
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
{event && (
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
color="destructive"
|
|
||||||
onClick={() => handleOpenDeleteModal(event?.event?.id)}
|
|
||||||
className="flex-1"
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EventModal;
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import { getEvents, getCategories } from "./utils";
|
|
||||||
import { Category } from "./data"
|
|
||||||
import CalendarView from "./calender-view";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const CalenderPage = async () => {
|
|
||||||
const events = await getEvents();
|
|
||||||
const categories = await getCategories();
|
|
||||||
const formattedCategories = categories.map((category: Category) => ({
|
|
||||||
...category,
|
|
||||||
activeClass: "",
|
|
||||||
}));
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<CalendarView events={events} categories={formattedCategories} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CalenderPage;
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import { calendarEvents, categories } from "./data";
|
|
||||||
|
|
||||||
// get events
|
|
||||||
export const getEvents = async () => {
|
|
||||||
return calendarEvents;
|
|
||||||
};
|
|
||||||
|
|
||||||
// get categories
|
|
||||||
export const getCategories = async () => {
|
|
||||||
return categories;
|
|
||||||
}
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
"use client";
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { Icon } from "@/components/ui/icon";
|
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
import { useMediaQuery } from "@/hooks/use-media-query";
|
|
||||||
import { Contact, ProfileUser } from "@/app/api/chat/data";
|
|
||||||
import { useChatConfig } from "@/hooks/use-chat";
|
|
||||||
|
|
||||||
const ChatHeader = ({ contact }: { contact: any }) => {
|
|
||||||
let active = true;
|
|
||||||
const isLg = useMediaQuery("(max-width: 1024px)");
|
|
||||||
|
|
||||||
const [chatConfig, setChatConfig] = useChatConfig()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="flex-1 flex gap-3 items-center">
|
|
||||||
{isLg && (
|
|
||||||
<Button size="icon" variant='ghost' color="secondary" onClick={() => setChatConfig({
|
|
||||||
...chatConfig,
|
|
||||||
isOpen: true
|
|
||||||
})}>
|
|
||||||
<Icon icon="heroicons-outline:menu-alt-1" className="w-5 h-5" />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<div className="relative inline-block">
|
|
||||||
<Avatar className="border-none shadow-none bg-transparent hover:bg-transparent">
|
|
||||||
<AvatarImage src={contact?.avatar?.src} alt="" />
|
|
||||||
<AvatarFallback>{contact?.fullName?.slice(0, 2)}</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<Badge
|
|
||||||
className=" h-2 w-2 p-0 ring-1 ring-border ring-offset-[1px] absolute top-2 -end-0.5"
|
|
||||||
color={active ? "success" : "secondary"}
|
|
||||||
></Badge>
|
|
||||||
</div>
|
|
||||||
<div className="hidden lg:block">
|
|
||||||
<div className="text-default-800 text-sm font-medium mb-0.5 ">
|
|
||||||
<span className="relative">{contact?.fullName}</span>
|
|
||||||
</div>
|
|
||||||
<div className="text-default-600 text-xs font-normal">
|
|
||||||
{active ? "Active Now" : "Offline"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex-none flex gap-2">
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
size="icon"
|
|
||||||
className="rounded-full bg-default-100 text-default-900 hover:bg-default-100 hover:ring-0 hover:ring-transparent"
|
|
||||||
>
|
|
||||||
<Icon icon="heroicons-outline:phone" className="text-xl" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom" align="end">
|
|
||||||
<p>Start a voice call</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
size="icon"
|
|
||||||
className="rounded-full bg-default-100 text-default-900 hover:bg-default-100 hover:ring-0 hover:ring-transparent"
|
|
||||||
>
|
|
||||||
<Icon icon="heroicons-outline:video-camera" className="text-xl" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom" align="end">
|
|
||||||
<p>Start a video call</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
{!isLg && (
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
onClick={() => setChatConfig({ ...chatConfig, showInfo: !chatConfig.showInfo })}
|
|
||||||
type="button"
|
|
||||||
size="icon"
|
|
||||||
className={cn(
|
|
||||||
"rounded-full bg-default-100 text-primary hover:bg-default-100 hover:ring-0 hover:ring-transparent",
|
|
||||||
{
|
|
||||||
"text-default-900": !chatConfig.showInfo,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
|
|
||||||
>
|
|
||||||
<span className="text-xl ">
|
|
||||||
{chatConfig.showInfo ? (
|
|
||||||
<Icon icon="heroicons-outline:dots-vertical" className="text-xl" />
|
|
||||||
|
|
||||||
) : (
|
|
||||||
<Icon icon="heroicons-outline:dots-horizontal" className="text-xl" />
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom" align="end">
|
|
||||||
<p>Conversation information</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ChatHeader;
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
'use client'
|
|
||||||
|
|
||||||
import React from 'react'
|
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
|
||||||
import { useChatConfig } from '@/hooks/use-chat';
|
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
||||||
|
|
||||||
const InfoWrapper = ({ children }: { children: React.ReactNode }) => {
|
|
||||||
const [chatConfig] = useChatConfig();
|
|
||||||
if (!chatConfig.showInfo) return null
|
|
||||||
return (
|
|
||||||
<Card className='w-[285px]'>
|
|
||||||
<ScrollArea className='h-full'>
|
|
||||||
<CardContent className='p-0'> {children}</CardContent>
|
|
||||||
</ScrollArea>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default InfoWrapper
|
|
||||||
|
|
@ -1,143 +0,0 @@
|
||||||
"use client";
|
|
||||||
import React, { useState } from "react";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Icon } from "@/components/ui/icon";
|
|
||||||
import { Annoyed, SendHorizontal } from "lucide-react";
|
|
||||||
|
|
||||||
import data from "@emoji-mart/data";
|
|
||||||
import Picker from "@emoji-mart/react";
|
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipArrow,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
import { postMessageAction } from "@/action/app-actions";
|
|
||||||
import { useTheme } from "next-themes";
|
|
||||||
|
|
||||||
const MessageFooter = () => {
|
|
||||||
const { theme: mode } = useTheme();
|
|
||||||
const [message, setMessage] = useState("");
|
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
||||||
setMessage(e.target.value);
|
|
||||||
e.target.style.height = "auto"; // Reset the height to auto to adjust
|
|
||||||
e.target.style.height = `${e.target.scrollHeight - 15}px`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSelectEmoji = (emoji: any) => {
|
|
||||||
setMessage(message + emoji.native);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!message) return;
|
|
||||||
const data: any = {
|
|
||||||
message,
|
|
||||||
};
|
|
||||||
|
|
||||||
await postMessageAction("55fe838e-9a09-4caf-a591-559803309ef1", "sfsfsf");
|
|
||||||
setMessage("");
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
|
|
||||||
|
|
||||||
<div
|
|
||||||
className="w-full flex items-end gap-1 lg:gap-4 lg:px-4 relative px-2 "
|
|
||||||
>
|
|
||||||
<div className="flex-none flex gap-1 absolute md:static top-0 left-1.5 z-10 ">
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
className="rounded-full hover:ring-0 hover:ring-transparent bg-default-100 hover:bg-default-100 hover:text-default-900 text-default-900">
|
|
||||||
<Icon icon="heroicons-outline:link" className="w-5 h-5" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>Add link</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
className="rounded-full hover:ring-0 hover:ring-transparent bg-default-100 hover:bg-default-100 hover:text-default-900 text-default-900">
|
|
||||||
<Annoyed className="w-6 h-6 text-default" />
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent side="top" align="start" className="w-fit p-0 shadow-none border-none bottom-0 rtl:left-5 ltr:-left-[110px]">
|
|
||||||
<Picker
|
|
||||||
data={data}
|
|
||||||
onEmojiSelect={handleSelectEmoji}
|
|
||||||
theme={mode === "dark" ? "dark" : "light"}
|
|
||||||
/>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div className="flex-1">
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<div className="flex gap-1 relative">
|
|
||||||
<textarea
|
|
||||||
value={message}
|
|
||||||
onChange={handleChange}
|
|
||||||
placeholder="Type your message..."
|
|
||||||
className="bg-background focus:outline-none rounded-xl break-words ps-8 md:ps-3 px-3 flex-1 h-10 pt-2 p-1 pr-8 no-scrollbar "
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === "Enter" && !e.shiftKey) {
|
|
||||||
e.preventDefault();
|
|
||||||
handleSubmit(e as any);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
minHeight: "40px",
|
|
||||||
maxHeight: "120px",
|
|
||||||
overflowY: "auto",
|
|
||||||
resize: "none",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
type="submit"
|
|
||||||
className="rounded-full hover:ring-0 hover:ring-transparent bg-default-100 hover:bg-default-100 hover:text-default-900 text-default-900"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
icon="heroicons-outline:paper-airplane"
|
|
||||||
className="transform rotate-[60deg] w-5 h-5"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent align="end">
|
|
||||||
<p>Send Message</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MessageFooter;
|
|
||||||
|
|
@ -1,241 +0,0 @@
|
||||||
|
|
||||||
import { getChatsByContactId, getProfileUser } from '../utils'
|
|
||||||
import { Icon } from "@/components/ui/icon";
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
import { Card, CardContent, CardHeader, CardFooter } from "@/components/ui/card";
|
|
||||||
import Image from 'next/image';
|
|
||||||
import { redirect } from '@/components/navigation';
|
|
||||||
import MessageFooter from './components/message-footer';
|
|
||||||
import ChatHeader from './components/chat-header';
|
|
||||||
import InfoWrapper from './components/info-wrapper';
|
|
||||||
import { MoreHorizontal } from 'lucide-react';
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
|
||||||
|
|
||||||
const socials = [
|
|
||||||
{
|
|
||||||
name: "facebook",
|
|
||||||
icon: "bi:facebook",
|
|
||||||
link: "#",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "twitter",
|
|
||||||
link: "#",
|
|
||||||
icon: "bi:twitter",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "instagram",
|
|
||||||
link: "#",
|
|
||||||
icon: "bi:instagram",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const ChatPageSingle = async ({ params: { id } }: { params: { id: string }; }) => {
|
|
||||||
|
|
||||||
const { chat, contact } = await getChatsByContactId(id)
|
|
||||||
const profile = await getProfileUser()
|
|
||||||
|
|
||||||
if (!contact) {
|
|
||||||
redirect('/app/chat')
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Card className="h-full flex flex-col flex-1 ">
|
|
||||||
<CardHeader className="flex-none mb-0 border-b border-default-200 py-5">
|
|
||||||
<ChatHeader contact={contact} />
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className=" relative flex-1 overflow-y-auto no-scrollbar">
|
|
||||||
{chat && chat?.chat?.length > 0 ? (
|
|
||||||
chat?.chat?.map(({ senderId, message, }, index) => (
|
|
||||||
<div className="block " key={index}>
|
|
||||||
{senderId === "e2c1a571-5f7e-4f56-9020-13f98b0eaba2" ? (
|
|
||||||
<>
|
|
||||||
<div className="flex gap-2 items-start justify-end group w-full mb-4">
|
|
||||||
<div className="flex flex-col gap-1">
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<div className="opacity-0 invisible group-hover:opacity-100 group-hover:visible ">
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<span className="w-7 h-7 rounded-full bg-default-200 flex items-center justify-center">
|
|
||||||
<MoreHorizontal />
|
|
||||||
</span>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent
|
|
||||||
className="w-fit"
|
|
||||||
align="end"
|
|
||||||
side="top"
|
|
||||||
>
|
|
||||||
<DropdownMenuItem>Remove</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>Forward</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
|
||||||
<div className="whitespace-pre-wrap break-all">
|
|
||||||
<div className="bg-default-100 text-default-900 text-sm p-3 font-normal rounded-md ">
|
|
||||||
{message}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="font-normal text-xs text-default-400 dark:text-default-600 text-start mt-1"> 2:40 pm </div>
|
|
||||||
</div>
|
|
||||||
<div className="flex-none self-end -translate-y-5">
|
|
||||||
<div className="h-8 w-8 rounded-full ">
|
|
||||||
<Image
|
|
||||||
src={profile?.avatar}
|
|
||||||
alt=""
|
|
||||||
className="block w-full h-full object-cover rounded-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div className="flex gap-2 items-start group mb-4">
|
|
||||||
<div className="flex-none self-end -translate-y-5">
|
|
||||||
<div className="h-8 w-8 rounded-full">
|
|
||||||
<Image
|
|
||||||
src={contact?.avatar || `/images/users/user-5.jpg`}
|
|
||||||
alt=""
|
|
||||||
className="block w-full h-full object-cover rounded-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 flex flex-col gap-2">
|
|
||||||
<div className="flex flex-col gap-1">
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<div className="whitespace-pre-wrap break-all relative z-[1]">
|
|
||||||
<div className="bg-default-100 text-default-900 text-sm p-3 font-normal rounded-md ">
|
|
||||||
{message}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="opacity-0 invisible group-hover:opacity-100 group-hover:visible ">
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<span className="w-7 h-7 rounded-full bg-default-200 flex items-center justify-center">
|
|
||||||
<MoreHorizontal />
|
|
||||||
</span>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent
|
|
||||||
className="w-fit"
|
|
||||||
align="end"
|
|
||||||
side="top"
|
|
||||||
>
|
|
||||||
<DropdownMenuItem>Remove</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>Forward</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="font-normal text-xs text-default-400 dark:text-default-600 text-start mt-1"> 2:40 pm </div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<div className="text-center absolute start-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2">
|
|
||||||
<Icon icon="typcn:messages" className="h-20 w-20 text-default-300 mx-auto" />
|
|
||||||
<div className="mt-4 text-lg font-medium text-default-500">No messages </div>
|
|
||||||
<div className="mt-1 text-sm font-medium text-default-400">{`don't worry, just take a deep breath & say "Hello"`}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</CardContent>
|
|
||||||
<CardFooter className="flex-none flex-col px-0 py-4 border-t border-border">
|
|
||||||
<MessageFooter />
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<InfoWrapper>
|
|
||||||
<h4 className="text-xl text-default-900 font-medium mb-8 px-6 mt-6">
|
|
||||||
About
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<div className='flex flex-col items-center px-6'>
|
|
||||||
<Avatar className="h-24 w-24 border-none shadow-none bg-transparent hover:bg-transparent">
|
|
||||||
<AvatarImage src={contact?.avatar?.src || `/images/users/user-5.jpg`} alt="" />
|
|
||||||
<AvatarFallback>{contact?.fullName?.slice(0, 2)}</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<div className="text-center mt-4 ">
|
|
||||||
<h5 className="text-base text-default-600 font-medium mb-1">
|
|
||||||
{contact?.fullName}
|
|
||||||
</h5>
|
|
||||||
<h6 className="text-xs text-default-600 font-normal">
|
|
||||||
{contact?.role}
|
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul className="mt-5 px-6 space-y-4 border-b border-default-200 pb-5 ">
|
|
||||||
<li className="flex justify-between text-sm text-default-600 ">
|
|
||||||
<div className="flex gap-2 items-start ">
|
|
||||||
<Icon
|
|
||||||
icon="heroicons-outline:location-marker"
|
|
||||||
className="text-base"
|
|
||||||
/>
|
|
||||||
<span>Location</span>
|
|
||||||
</div>
|
|
||||||
<div className="font-medium">Bangladesh</div>
|
|
||||||
</li>
|
|
||||||
<li className="flex justify-between text-sm text-default-600 ">
|
|
||||||
<div className="flex gap-2 items-start">
|
|
||||||
<Icon icon="heroicons-outline:user" className="text-base" />
|
|
||||||
<span>Members since</span>
|
|
||||||
</div>
|
|
||||||
<div className="font-medium">Oct 2021</div>
|
|
||||||
</li>
|
|
||||||
<li className="flex justify-between text-sm text-default-600 ">
|
|
||||||
<div className="flex gap-2 items-start ">
|
|
||||||
<Icon icon="heroicons-outline:translate" className="text-base" />
|
|
||||||
<span>Language</span>
|
|
||||||
</div>
|
|
||||||
<div className="font-medium">English</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<ul className="mt-5 px-6 space-y-4 border-b border-default-200 pb-5 ">
|
|
||||||
{socials?.map((slink, sindex) => (
|
|
||||||
<li
|
|
||||||
key={sindex}
|
|
||||||
className="text-sm text-default-600"
|
|
||||||
>
|
|
||||||
<button className="flex gap-2">
|
|
||||||
<Icon icon={slink.icon} className="text-base" />
|
|
||||||
<span className="capitalize font-normal text-default-600">
|
|
||||||
{slink.name}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
<h4 className="py-4 text-sm px-6 text-default-500 font-medium">
|
|
||||||
Shared documents
|
|
||||||
</h4>
|
|
||||||
<div className="grid grid-cols-3 gap-2 px-6">
|
|
||||||
{
|
|
||||||
["/images/chat/sd1.png", "/images/chat/sd2.png", "/images/chat/sd3.png", "/images/chat/sd4.png", "/images/chat/sd5.png", "/images/chat/sd6.png"].map((image, index) => (
|
|
||||||
<Image
|
|
||||||
key={`image-${index}`}
|
|
||||||
src={image}
|
|
||||||
alt=""
|
|
||||||
width={200}
|
|
||||||
height={100}
|
|
||||||
className='w-full h-12 object-cover rounded-md'
|
|
||||||
/>
|
|
||||||
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</InfoWrapper>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ChatPageSingle
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
'use client'
|
|
||||||
import { useMediaQuery } from '@/hooks/use-media-query';
|
|
||||||
import React from 'react'
|
|
||||||
import { useChatConfig } from '@/hooks/use-chat';
|
|
||||||
|
|
||||||
const ChatWrapper = ({ children }: { children: React.ReactNode }) => {
|
|
||||||
const [chatConfig, setChatConfig] = useChatConfig();
|
|
||||||
const { isOpen } = chatConfig
|
|
||||||
const isTablet = useMediaQuery("(min-width: 1024px)");
|
|
||||||
return (
|
|
||||||
<div className=' app-height flex gap-5 relative'>
|
|
||||||
{!isTablet && isOpen && (
|
|
||||||
<div
|
|
||||||
onClick={() => setChatConfig({ ...chatConfig, isOpen: false })}
|
|
||||||
className="overlay bg-default-900 dark:bg-default-900 dark:bg-opacity-60 bg-opacity-60 backdrop-filter
|
|
||||||
backdrop-blur-sm absolute w-full flex-1 inset-0 z-20 rounded-md"
|
|
||||||
|
|
||||||
></div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ChatWrapper
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
"use client";
|
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
|
||||||
import { useMediaQuery } from "@/hooks/use-media-query";
|
|
||||||
import { Icon } from "@/components/ui/icon";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { useChatConfig } from "@/hooks/use-chat";
|
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
const Blank = () => {
|
|
||||||
const isLg = useMediaQuery("(max-width: 1024px)");
|
|
||||||
const [chatConfig, setChatConfig] = useChatConfig()
|
|
||||||
const t = useTranslations("ChatApp");
|
|
||||||
return (
|
|
||||||
<Card className="flex-1 h-full">
|
|
||||||
<CardContent className="h-full flex justify-center items-center">
|
|
||||||
<div className="text-center flex flex-col items-center">
|
|
||||||
<Icon icon="uiw:message" className="text-7xl text-default-300" />
|
|
||||||
<div className="mt-4 text-lg font-medium text-default-500">
|
|
||||||
{t("blankMessageTitle")}
|
|
||||||
</div>
|
|
||||||
<p className="mt-1 text-sm font-medium text-default-400">
|
|
||||||
{t("blankMessageDesc")}
|
|
||||||
</p>
|
|
||||||
{isLg && (
|
|
||||||
<Button className="mt-2" onClick={() => setChatConfig({ ...chatConfig, isOpen: true })}>
|
|
||||||
Start Conversation
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Blank;
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
|
|
||||||
'use client'
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { Icon } from "@/components/ui/icon";
|
|
||||||
import { type Contact as ContactType, type Chat as ChatType } from "../utils";
|
|
||||||
import { Link, usePathname } from "@/components/navigation";
|
|
||||||
import { useChatConfig } from "@/hooks/use-chat";
|
|
||||||
|
|
||||||
|
|
||||||
const ContactList = ({ contact }: {
|
|
||||||
contact: ContactType,
|
|
||||||
|
|
||||||
}) => {
|
|
||||||
const { avatar, id, fullName, status, about, unreadmessage, date } =
|
|
||||||
contact;
|
|
||||||
|
|
||||||
const pathname = usePathname();
|
|
||||||
const [chatConfig, setChatConfig] = useChatConfig()
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
onClick={() => setChatConfig({
|
|
||||||
...chatConfig,
|
|
||||||
isOpen: false
|
|
||||||
})}
|
|
||||||
href={`/app/chat/${id}`} className={cn(
|
|
||||||
" gap-4 py-2 lg:py-2.5 px-3 border-l-2 border-transparent hover:bg-default-100 cursor-pointer flex ",
|
|
||||||
{
|
|
||||||
"lg:bg-default-100 ": `/app/chat/${id}` === pathname
|
|
||||||
}
|
|
||||||
)} >
|
|
||||||
|
|
||||||
|
|
||||||
<div className="flex-1 flex items-center gap-3 ">
|
|
||||||
<div className="relative inline-block ">
|
|
||||||
<Avatar className="border-none bg-transparent hover:bg-transparent">
|
|
||||||
<AvatarImage src={avatar.src} />
|
|
||||||
<AvatarFallback className="uppercase">
|
|
||||||
{fullName.slice(0, 2)}
|
|
||||||
</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<Badge
|
|
||||||
className=" h-2 w-2 p-0 ring-1 ring-border ring-offset-[1px] items-center justify-center absolute top-2 -end-[3px]"
|
|
||||||
color={status === "online" ? "success" : "secondary"}
|
|
||||||
></Badge>
|
|
||||||
</div>
|
|
||||||
<div className="block">
|
|
||||||
<div className="truncate max-w-[120px]">
|
|
||||||
<span className="text-sm text-default-900 font-medium">
|
|
||||||
{" "}
|
|
||||||
{fullName}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="truncate max-w-[120px]">
|
|
||||||
<span className=" text-xs text-default-700 ">
|
|
||||||
{about}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex-none flex-col items-end gap-2 hidden lg:flex">
|
|
||||||
<span className="text-xs text-default-600 text-end uppercase">
|
|
||||||
{date}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className={cn(
|
|
||||||
"h-[14px] w-[14px] flex items-center justify-center bg-default-400 rounded-full text-default-foreground text-[10px] font-medium",
|
|
||||||
{
|
|
||||||
"bg-[#FFC155]": unreadmessage > 0,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{unreadmessage === 0 ? (
|
|
||||||
<Icon icon="uil:check" className="text-sm" />
|
|
||||||
) : (
|
|
||||||
unreadmessage
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ContactList;
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
'use client'
|
|
||||||
|
|
||||||
import { Icon } from "@/components/ui/icon"
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import { useChatConfig } from "@/hooks/use-chat";
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
||||||
|
|
||||||
const MyProfile = () => {
|
|
||||||
const [chatConfig, setChatConfig] = useChatConfig();
|
|
||||||
let status = "active";
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="flex justify-between gap-1">
|
|
||||||
<div className="flex gap-3">
|
|
||||||
<div className="flex-none">
|
|
||||||
<Avatar>
|
|
||||||
<AvatarImage src="/images/users/user-1.jpg" />
|
|
||||||
<AvatarFallback>SC</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 text-start">
|
|
||||||
<div className="text-default-800 text-sm font-medium mb-1">
|
|
||||||
Jane Cooper
|
|
||||||
<span className="bg-success inline-block h-2.5 w-2.5 rounded-full ms-3"></span>
|
|
||||||
</div>
|
|
||||||
<div className=" text-default-500 text-xs font-normal">
|
|
||||||
Available
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
className="w-8 h-8"
|
|
||||||
color="secondary"
|
|
||||||
rounded="full"
|
|
||||||
onClick={() => setChatConfig({ ...chatConfig, showProfile: true })}
|
|
||||||
>
|
|
||||||
<Icon icon="heroicons-outline:dots-horizontal" className="w-4 h-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={cn('absolute bg-card rounded-md h-full start-0 top-0 bottom-0 w-full z-50', {
|
|
||||||
'hidden -start-full': !chatConfig.showProfile
|
|
||||||
})}>
|
|
||||||
<ScrollArea className="h-full">
|
|
||||||
<div className="p-6">
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<Button size="icon" color="secondary" className="w-8 h-8" rounded="full" onClick={() => setChatConfig({ ...chatConfig, showProfile: false })}>
|
|
||||||
<Icon icon="heroicons-outline:x" className="w-4 h-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col items-center gap-3">
|
|
||||||
<div className="relative">
|
|
||||||
<Avatar className="h-16 w-16 border border-default-200 p-1 bg-transparent hover:bg-transparent ">
|
|
||||||
<AvatarImage src="/images/users/user-1.jpg" className="rounded-full" />
|
|
||||||
<AvatarFallback>SC</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<span
|
|
||||||
className={cn("absolute top-3 -end-[3px] h-3 w-3 rounded-full bg-success border border-primary-foreground",
|
|
||||||
{
|
|
||||||
"bg-success": status === "active",
|
|
||||||
"bg-warning": status === "away",
|
|
||||||
"bg-destructive": status === "busy",
|
|
||||||
"bg-secondary": status === "offline",
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-default-800 text-sm font-medium mb-0.5">
|
|
||||||
Jane Cooper
|
|
||||||
</div>
|
|
||||||
<div className=" text-default-500 text-xs font-normal">
|
|
||||||
Admin
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="my-8">
|
|
||||||
<Label htmlFor="bio" className="mb-2 block text-default-900"> About </Label>
|
|
||||||
<Textarea id="bio" placeholder="About your self" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label htmlFor="status" className="block mb-3 text-default-700">Status</Label>
|
|
||||||
<RadioGroup defaultValue="comfortable">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<RadioGroupItem value="default" id="active" color="success" />
|
|
||||||
<Label htmlFor="active">Active</Label>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<RadioGroupItem value="busy" id="busy" color="destructive" />
|
|
||||||
<Label htmlFor="busy">Do Not Disturb</Label>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<RadioGroupItem value="away" id="away" color="warning" />
|
|
||||||
<Label htmlFor="away">Away</Label>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<RadioGroupItem value="offline" id="offline" color="warning" />
|
|
||||||
<Label htmlFor="offline">Offline</Label>
|
|
||||||
</div>
|
|
||||||
</RadioGroup>
|
|
||||||
</div>
|
|
||||||
<Button className="mt-7">Logout</Button>
|
|
||||||
</div>
|
|
||||||
</ScrollArea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MyProfile;
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import React from 'react';
|
|
||||||
import {Search as SearchIcon} from "lucide-react"
|
|
||||||
const Search = () => {
|
|
||||||
return (
|
|
||||||
<div className='relative'>
|
|
||||||
<SearchIcon className='absolute top-1/2 -translate-y-1/2 start-6 w-4 h-4 text-default-600' />
|
|
||||||
<Input placeholder='Search...' className='dark:bg-transparent rounded-none border-l-0 border-r-0 dark:border-default-300 focus:border-default-200 ps-12 text-lg font-normal' size="lg"/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Search;
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue