Compare commits
5 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
073dfe3b1c | |
|
|
71416a40d0 | |
|
|
9b044937fc | |
|
|
6254d43173 | |
|
|
149b434d22 |
|
|
@ -1,14 +1,13 @@
|
|||
{
|
||||
"ci": {
|
||||
"collect": {
|
||||
"staticDistDir": "./public"
|
||||
},
|
||||
"assert": {
|
||||
"assertions": {}
|
||||
},
|
||||
"upload": {
|
||||
"target": "temporary-public-storage"
|
||||
}
|
||||
"ci": {
|
||||
"collect": {
|
||||
"staticDistDir": "./public"
|
||||
},
|
||||
"assert": {
|
||||
"assertions": {}
|
||||
},
|
||||
"upload": {
|
||||
"target": "temporary-public-storage"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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