feat:admin page, shared:communcation page

This commit is contained in:
Rama Priyanto 2025-01-09 19:28:37 +07:00
parent 19aae2e778
commit b14a52e65f
40 changed files with 4166 additions and 338 deletions

View File

@ -124,7 +124,6 @@ const AddExpertTable = () => {
const handleKeyDown = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {

View File

@ -69,6 +69,7 @@ import { Link } from "@/i18n/routing";
const BroadcastTable = () => {
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[]>([]);
@ -119,7 +120,6 @@ const BroadcastTable = () => {
const handleKeyDown = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {
@ -147,7 +147,7 @@ const BroadcastTable = () => {
const res = await listDataMedia(
page - 1,
showData,
"",
search,
categoryFilter?.sort().join(","),
statusFilter?.sort().join(",")
);
@ -218,6 +218,7 @@ const BroadcastTable = () => {
placeholder="Search"
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
onChange={(e) => setSearch(e.target.value)}
className="max-w-[300px]"
/>
<div className="flex flex-row gap-2">

View File

@ -0,0 +1,291 @@
"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>
);
}

View File

@ -0,0 +1,381 @@
"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>
);
}

View File

@ -277,6 +277,7 @@ export default function CreateUserForm() {
memberIdentity: data.nrp,
address: data.address,
email: data.email,
phoneNumber: data.phoneNumber,
password: data.password,
passwordConf: data.confirmPassword,
isDefault: false,
@ -304,9 +305,8 @@ export default function CreateUserForm() {
MySwal.fire({
title: "Sukses",
icon: "success",
showCancelButton: true,
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
confirmButtonText: "Oke",
}).then((result) => {
if (result.isConfirmed) {
router.push("/admin/management-user");

View File

@ -337,9 +337,8 @@ export default function EditUserForm() {
MySwal.fire({
title: "Sukses",
icon: "success",
showCancelButton: true,
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
confirmButtonText: "Oke",
}).then((result) => {
if (result.isConfirmed) {
router.push("/admin/management-user");

View File

@ -6,7 +6,8 @@ import InternalTable from "@/components/table/management-user/management-user-in
import { Button } from "@/components/ui/button";
import DashboardVisualization from "@/components/visualization/dashboard-viz";
import ManagementUserVisualization from "@/components/visualization/management-user-viz";
import { useRouter } from "@/i18n/routing";
import { Link, useRouter } from "@/i18n/routing";
import { PlusIcon } from "lucide-react";
import { useEffect, useState } from "react";
export default function ManagementUser() {
@ -18,20 +19,32 @@ export default function ManagementUser() {
return (
<div>
<SiteBreadcrumb />
{isInternal && (
<section id="viz">
<ManagementUserVisualization />
</section>
)}
<section id="viz">
<ManagementUserVisualization />
</section>
<section
id="table"
className="flex flex-col gap-2 bg-white rounded-lg p-3 mt-5"
>
<div className="flex flex-row gap-1 border-2 w-fit mb-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 rounded-none
className={` hover:text-white
${
!isInternal ? "bg-white text-black " : "bg-black text-white "
}`}
@ -39,8 +52,9 @@ export default function ManagementUser() {
User Internal
</Button>
<Button
rounded="md"
onClick={() => setIsInternal(false)}
className={`hover:text-white rounded-none ${
className={`hover:text-white ${
!isInternal ? "bg-black text-white " : "bg-white text-black "
}
`}

View File

@ -4,6 +4,7 @@ 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");
@ -17,23 +18,31 @@ export default function AdminBanner() {
? "Daftar List Media"
: "Table List Banner"}
<div className="flex flex-row text-sm">
<a
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
<Button
rounded="md"
onClick={() => setSelectedTab("content")}
className={`px-3 py-2 cursor-pointer ${
selectedTab === "content" ? "bg-primary text-white" : ""
} `}
className={` hover:text-white
${
selectedTab === "content"
? "bg-black text-white "
: "bg-white text-black "
}`}
>
Kontent
</a>
<a
</Button>
<Button
rounded="md"
onClick={() => setSelectedTab("banner")}
className={`px-3 py-2 cursor-pointer ${
selectedTab === "banner" ? "bg-primary text-white" : ""
} `}
className={` hover:text-white
${
selectedTab === "banner"
? "bg-black text-white "
: "bg-white text-black "
}`}
>
Banner
</a>
</Button>
</div>
</div>

View File

@ -256,7 +256,10 @@ export default function EditCategoryModal(props: {
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger>
<a onClick={() => setIsOpen(true)} className="hover:underline">
<a
onClick={() => setIsOpen(true)}
className="hover:underline cursor-pointer"
>
{isDetail ? "Detail" : "Edit"}
</a>
</DialogTrigger>

View File

@ -0,0 +1,112 @@
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;

View File

@ -0,0 +1,299 @@
"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";
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 [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">
Tambah FAQ
</Button>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>Tambah 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>
);
}

View File

@ -0,0 +1,325 @@
"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>
);
}

View File

@ -0,0 +1,184 @@
"use client";
import * as React from "react";
import {
ColumnDef,
ColumnFiltersState,
PaginationState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { useSearchParams } from "next/navigation";
import TablePagination from "@/components/table/table-pagination";
import columns from "./column";
import { listEnableCategory } from "@/service/content/content";
import { Checkbox } from "@/components/ui/checkbox";
import { close, loading } from "@/config/swal";
import { Link, useRouter } from "@/i18n/routing";
import { NewCampaignIcon } from "@/components/icon";
import { getCategories, 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;

View File

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

View File

@ -0,0 +1,121 @@
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;

View File

@ -0,0 +1,275 @@
"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";
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 [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">
Tambah Feedback
</Button>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>Tambah 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>
);
}

View File

@ -0,0 +1,311 @@
"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>
);
}

View File

@ -0,0 +1,181 @@
"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";
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;

View File

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

View File

@ -0,0 +1,127 @@
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;

View File

@ -0,0 +1,211 @@
"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";
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 [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">
Tambah Tag
</Button>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>Tambah 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>
<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>
);
}

View File

@ -0,0 +1,273 @@
"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>
);
}

View File

@ -0,0 +1,181 @@
"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";
const AdminTagTable = () => {
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/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">Tag</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;

View File

@ -1,11 +1,13 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import AdminTagTable from "./component/table";
export default function TagCategory() {
return (
<>
<SiteBreadcrumb />
<AdminTagTable />
</>
);
}

View File

@ -41,6 +41,8 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
@ -75,24 +77,15 @@ const EscalationTable = () => {
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [showData, setShowData] = React.useState("10");
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
pageSize: Number(showData),
});
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState<string>("");
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const [categories, setCategories] = React.useState();
const [categoryFilter, setCategoryFilter] = React.useState([]);
const [statusFilter, setStatusFilter] = React.useState([]);
const [startDateString, setStartDateString] = React.useState("");
const [endDateString, setEndDateString] = React.useState("");
const [filterByCreator, setFilterByCreator] = React.useState("");
const [filterBySource, setFilterBySource] = React.useState("");
const roleId = getCookiesDecrypt("urie");
@ -126,19 +119,35 @@ const EscalationTable = () => {
React.useEffect(() => {
fetchData();
}, [page, limit, search]);
}, [page, showData]);
let typingTimer: any;
const doneTypingInterval = 1500;
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
async function doneTyping() {
fetchData();
}
async function fetchData() {
try {
const res = await getTicketingCollaborationPagination(
page - 1,
limit,
showData,
search
);
const data = res?.data?.data;
const contentData = data?.content;
contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * limit + index + 1;
item.no = (page - 1) * Number(showData) + index + 1;
});
console.log("contentData : ", contentData);
setDataTable(contentData);
@ -148,16 +157,10 @@ const EscalationTable = () => {
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">
<div className="flex justify-between items-center px-5">
<div className="mt-3">
<div className="flex justify-between items-center">
<div className="mt-3 flex flex-row items-center gap-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" />
@ -167,23 +170,40 @@ const EscalationTable = () => {
placeholder="Search Judul..."
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
value={search}
onChange={handleSearch}
onChange={(e) => setSearch(e.target.value)}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
/>
</InputGroup>
</div>
<div className="flex-none">
<Input
placeholder="Filter Status..."
value={
(table.getColumn("status")?.getFilterValue() as string) ?? ""
}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
table.getColumn("status")?.setFilterValue(event.target.value)
}
className="max-w-sm "
/>
</div>
<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>
</div>
<Table className="overflow-hidden mt-3">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (

View File

@ -12,58 +12,39 @@ import {
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { format } from "date-fns";
import { Link, useRouter } from "@/i18n/routing";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div>
),
cell: ({ row }) => <span> {row.getValue("no")}</span>,
},
{
accessorKey: "title",
header: "Question",
header: "Pertanyaan",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("title")}
</h4>
</div>
</div>
<span className="normal-case"> {row.getValue("title")}</span>
),
},
{
accessorKey: "commentFromUserName",
header: "CreateBy",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("commentFromUserName")}
</h4>
</div>
</div>
<span className="normal-case">{row.getValue("commentFromUserName")}</span>
),
},
{
accessorKey: "Type",
header: "SendTo",
header: "Channel",
cell: ({ row }) => {
const type = row.original.type; // Akses properti category
return <span className="whitespace-nowrap">{type?.name || "N/A"}</span>;
const type = row.original.type;
return <span className="normal-case">{type?.name || "N/A"}</span>;
},
},
{
accessorKey: "createdAt",
header: "Upload Date",
header: "Waktu",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
@ -99,6 +80,7 @@ const columns: ColumnDef<any>[] = [
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
const router = useRouter();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@ -111,17 +93,16 @@ const columns: ColumnDef<any>[] = [
</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">
<DropdownMenuItem
onClick={() =>
router.push(
`/shared/communication/collaboration/detail/${row.original.id}`
)
}
className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none items-center"
>
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
<Trash2 className="w-4 h-4 me-1.5" />
Delete
Detail
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

View File

@ -0,0 +1,143 @@
"use client";
import { Button } from "@/components/ui/button";
import Select, { MultiValue } from "react-select";
import { Icon } from "@iconify/react/dist/iconify.js";
import { useEffect, useState } from "react";
import { Separator } from "@/components/ui/separator";
import {
getCuratorUser,
saveCollaborationTeams,
} from "@/service/communication/communication";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, loading } from "@/config/swal";
import { useParams } from "next/navigation";
import { useToast } from "@/components/ui/use-toast";
import { stringify } from "querystring";
const assigneeOptions = [
{ value: "mahedi", label: "Mahedi Amin", image: "/images/avatar/av-1.svg" },
{ value: "sovo", label: "Sovo Haldar", image: "/images/avatar/av-2.svg" },
{
value: "rakibul",
label: "Rakibul Islam",
image: "/images/avatar/av-3.svg",
},
{ value: "pritom", label: "Pritom Miha", image: "/images/avatar/av-4.svg" },
];
export default function UsersCard(props: { team: any; fetchData: () => void }) {
const params = useParams();
const id = params?.id;
const [openSearch, setOpenSearch] = useState(false);
const [selectedUsers, setSelectedUsers] = useState<any>([]);
const [allUser, setAllUser] = useState([]);
const MySwal = withReactContent(Swal);
const { toast } = useToast();
useEffect(() => {
async function getUser() {
const res = await getCuratorUser();
if (res?.data !== null) {
const rawUser = res?.data?.data?.content;
const optionArr: any = [];
rawUser.map((option: any) => {
optionArr.push({
id: option.id,
label: option.username,
value: option.id,
});
});
setAllUser(optionArr);
}
}
getUser();
}, []);
const inviteHandle = () => {
MySwal.fire({
title: "Undang Pengguna Ke Kolom Diskusi?",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
inviteUser();
}
});
};
async function inviteUser() {
const userId: any = [];
selectedUsers?.map((user: any) => {
userId.push(user.id);
});
loading();
const res = await saveCollaborationTeams(String(id), userId);
if (res?.error) {
toast({ title: stringify(res?.message) });
return false;
}
close();
toast({ title: "Berhasil menambahkan user" });
props.fetchData();
}
return (
<div className="flex flex-col bg-white p-4 rounded-t-xl w-[30%]">
<div className="flex justify-between items-center mb-3">
<div className="flex flex-row gap-3 items-center">
<p>Kolaborator</p>
<p className="p-2 rounded-full bg-slate-300 w-5 h-5 flex justify-center items-center">
{props?.team?.length}
</p>
</div>
<a onClick={() => setOpenSearch(!openSearch)}>
<Icon icon="material-symbols:search" width={24} />
</a>
</div>
{openSearch && (
<div className="flex flex-col">
<Select
className="react-select my-2 transition-shadow"
classNamePrefix="select"
options={allUser}
isMulti
onChange={(selectedOption) => setSelectedUsers(selectedOption)}
placeholder="Select users"
/>
{selectedUsers?.length > 0 && (
<div className="flex justify-end">
<Button color="primary" size="sm" onClick={inviteHandle}>
Invie
</Button>
</div>
)}
</div>
)}
<div className="mt-3 flex flex-col gap-2">
{props?.team?.map((list: any) => (
<div key={list.id} className="flex flex-col">
<div className="flex flex-row gap-2 items-center">
<Icon icon="qlementine-icons:user-16" width={36} />
<div className="flex flex-col">
<p className="text-sm">{list?.fullname}</p>
<p className="text-xs">{list?.username}</p>
</div>
</div>
<Separator className="my-2" />
</div>
))}
</div>
</div>
);
}

View File

@ -0,0 +1,199 @@
"use client";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { Textarea } from "@/components/ui/textarea";
import { useToast } from "@/components/ui/use-toast";
import { close, loading } from "@/config/swal";
import {
deleteCollabDiscussion,
getCollabDiscussion,
getCuratorUser,
getTicketCollaborationTeams,
saveCollabDiscussion,
} from "@/service/communication/communication";
import {
getLocaleTimestamp,
htmlToString,
textEllipsis,
} from "@/utils/globals";
import { Icon } from "@iconify/react/dist/iconify.js";
import { useParams } from "next/navigation";
import { stringify } from "querystring";
import { useEffect, useState } from "react";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import UsersCard from "./component/users-card";
export default function CollaborationPage() {
const params = useParams();
const id = params?.id;
const { toast } = useToast();
const [detailTickets, setDetailTickets] = useState<any>();
const [teams, setTeams] = useState([]);
const [listDiscussion, setListDiscussion] = useState([]);
const [replyValue, setReplyValue] = useState("");
const MySwal = withReactContent(Swal);
useEffect(() => {
async function getCollabTeam() {
if (id != undefined) {
const res = await getTicketCollaborationTeams(String(id));
if (res?.data !== null) {
console.log("response teams:", res);
setDetailTickets(res?.data?.data?.tickets);
setTeams(res?.data?.data?.userTeam);
console.log("userteam", res?.data?.data?.userTeam);
}
}
}
initState();
getCollabTeam();
}, [id]);
async function initState() {
if (id != undefined) {
loading();
const responseGet = await getCollabDiscussion(String(id));
console.log("get res data", responseGet?.data?.data);
close();
setListDiscussion(responseGet?.data?.data);
}
}
async function deleteDataSuggestion(dataId: string | number) {
loading();
const response = await deleteCollabDiscussion(dataId);
console.log(response);
close();
initState();
}
async function sendDiscussionParent() {
if (replyValue?.length > 0) {
loading();
const data = {
ticketId: id,
replyValue,
parentId: null,
};
const response = await saveCollabDiscussion(data);
if (response?.error) {
toast({
title: stringify(response?.message),
});
return false;
}
toast({
title: "Sukses Komen",
});
setReplyValue("");
close();
initState();
}
}
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-row gap-4 pt-6">
<div className="flex flex-col gap-4 w-[70%]">
<div className="bg-white flex flex-col rounded-t-xl">
<div className="flex flex-row gap-2 bg-slate-300 rounded-t-xl p-4 items-center">
<Icon icon="qlementine-icons:user-16" width={36} />
<div className="flex flex-col gap-1">
<div className="flex flex-row">
<p className="text-sm">
<span className="font-semibold">
{detailTickets?.commentFromUserName}
</span>{" "}
mengirimkan komentar untuk
</p>
<a
href={detailTickets?.feed?.permalink_url}
className="font-weight-bold"
>
{textEllipsis(detailTickets?.feed?.message, 25)}
</a>
</div>
<p className="text-xs">
{" "}
{`${new Date(detailTickets?.createdAt).getDate()}-${
new Date(detailTickets?.createdAt).getMonth() + 1
}-${new Date(
detailTickets?.createdAt
).getFullYear()} ${new Date(
detailTickets?.createdAt
).getHours()}:${new Date(
detailTickets?.createdAt
).getMinutes()}`}
</p>
</div>
</div>
<div className="p-4"> {htmlToString(detailTickets?.message)}</div>
</div>
<div className="bg-white rounded-b-xl flex flex-col p-4">
<p className="font-bold text-sm">{listDiscussion?.length} Respon</p>
<Separator className="my-2" />
<div className="flex flex-col gap-2 max-h-[360px]">
{listDiscussion?.length > 1 ? (
listDiscussion?.map((data: any) => (
<div key={data?.id} className="flex flex-col ">
<div className="flex flex-row gap-2 items-center">
<Icon icon="qlementine-icons:user-16" width={36} />
<div className="flex flex-col w-full">
<div className="flex justify-between items-center">
<p className="text-sm font-semibold">
{data?.messageFrom?.fullname}
</p>
<p className="text-xs">
{getLocaleTimestamp(new Date(data?.createdAt))}
</p>
</div>
<div className="flex justify-between items-center">
<p className="text-sm">{data?.message}</p>
<a
onClick={() => deleteDataSuggestion(data?.id)}
className="text-destructive cursor-pointer text-xs"
>
Hapus
</a>
</div>
</div>
</div>
<Separator className="my-2" />
</div>
))
) : (
<p>Belum Ada Tanggapan</p>
)}
</div>
<Textarea
value={replyValue}
onChange={(e) => setReplyValue(e.target.value)}
/>
<div className="flex justify-end py-3">
<Button
className="w-fit"
color="primary"
size="sm"
type="button"
onClick={sendDiscussionParent}
>
Kirim
</Button>
</div>
</div>
</div>{" "}
<UsersCard team={teams} fetchData={() => initState()} />
</div>
</div>
);
}

View File

@ -18,53 +18,33 @@ const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div>
),
cell: ({ row }) => <span> {row.getValue("no")}</span>,
},
{
accessorKey: "title",
header: "Question",
header: "Pertanyaan",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("title")}
</h4>
</div>
</div>
<span className="normal-case"> {row.getValue("title")}</span>
),
},
{
accessorKey: "commentFromUserName",
header: "CreateBY",
header: "Penerima",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("commentFromUserName")}
</h4>
</div>
</div>
<span className="normal-case">{row.getValue("commentFromUserName")}</span>
),
},
{
accessorKey: "type",
header: "SendTo",
header: "Penerima",
cell: ({ row }) => {
const type = row.original.type; // Akses properti category
return <span className="whitespace-nowrap">{type?.name || "N/A"}</span>;
return <span className="normal-case">{type?.name || "N/A"}</span>;
},
},
{
accessorKey: "createdAt",
header: "Upload Date",
header: "Waktu",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string

View File

@ -41,6 +41,8 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
@ -74,24 +76,15 @@ const EscalationTable = () => {
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [showData, setShowData] = React.useState("10");
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
pageSize: Number(showData),
});
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState<string>("");
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const [categories, setCategories] = React.useState();
const [categoryFilter, setCategoryFilter] = React.useState([]);
const [statusFilter, setStatusFilter] = React.useState([]);
const [startDateString, setStartDateString] = React.useState("");
const [endDateString, setEndDateString] = React.useState("");
const [filterByCreator, setFilterByCreator] = React.useState("");
const [filterBySource, setFilterBySource] = React.useState("");
const roleId = getCookiesDecrypt("urie");
@ -125,19 +118,39 @@ const EscalationTable = () => {
React.useEffect(() => {
fetchData();
}, [page, limit, search]);
setPagination({
pageIndex: 0,
pageSize: Number(showData),
});
}, [page, showData]);
let typingTimer: any;
const doneTypingInterval = 1500;
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
async function doneTyping() {
fetchData();
}
async function fetchData() {
try {
const res = await getTicketingEscalationPagination(
page - 1,
limit,
Number(showData),
search
);
const data = res?.data?.data;
const contentData = data?.content;
contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * limit + index + 1;
item.no = (page - 1) * Number(showData) + index + 1;
});
console.log("contentData : ", contentData);
setDataTable(contentData);
@ -155,8 +168,8 @@ const EscalationTable = () => {
return (
<div className="w-full overflow-x-auto">
<div className="flex justify-between items-center px-5">
<div className="mt-3">
<div className="flex justify-between items-center">
<div className="mt-3 flex flex-row items-center gap-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" />
@ -166,22 +179,38 @@ const EscalationTable = () => {
placeholder="Search Judul..."
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
value={search}
onChange={handleSearch}
onChange={(e) => setSearch(e.target.value)}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
/>
</InputGroup>
</div>
<div className="flex-none">
<Input
placeholder="Filter Status..."
value={
(table.getColumn("status")?.getFilterValue() as string) ?? ""
}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
table.getColumn("status")?.setFilterValue(event.target.value)
}
className="max-w-sm "
/>
</div>
<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>
</div>
<Table className="overflow-hidden mt-3">
<TableHeader>

View File

@ -18,54 +18,36 @@ const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div>
),
cell: ({ row }) => <span> {row.getValue("no")}</span>,
},
{
accessorKey: "title",
header: "Question",
header: "Pertanyaan",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("title")}
</h4>
</div>
</div>
<span className="normal-case"> {row.getValue("title")}</span>
),
},
{
accessorKey: "createdBy",
header: "CreateBy",
header: "Pengirim",
cell: ({ row }) => {
const createdBy = row.original.createdBy; // Akses properti category
return (
<span className="whitespace-nowrap">
{createdBy?.fullname || "N/A"}
</span>
<span className="normal-case">{createdBy?.fullname || "N/A"}</span>
);
},
},
{
accessorKey: "sendTo",
header: "SendTo",
header: "Penerima",
cell: ({ row }) => {
const sendTo = row.original.sendTo; // Akses properti category
return (
<span className="whitespace-nowrap">{sendTo?.fullname || "N/A"}</span>
);
return <span className="normal-case">{sendTo?.fullname || "N/A"}</span>;
},
},
{
accessorKey: "createdAt",
header: "Upload Date",
header: "Waktu",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string

View File

@ -42,6 +42,8 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
@ -73,25 +75,18 @@ const TableAudio = () => {
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [showData, setShowData] = React.useState("10");
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
pageSize: Number(showData),
});
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState<string>("");
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const [categories, setCategories] = React.useState();
const [categoryFilter, setCategoryFilter] = React.useState([]);
const [statusFilter, setStatusFilter] = React.useState([]);
const [startDateString, setStartDateString] = React.useState("");
const [endDateString, setEndDateString] = React.useState("");
const [filterByCreator, setFilterByCreator] = React.useState("");
const [filterBySource, setFilterBySource] = React.useState("");
const roleId = getCookiesDecrypt("urie");
const table = useReactTable({
@ -124,15 +119,39 @@ const TableAudio = () => {
React.useEffect(() => {
fetchData();
}, [page, limit, search]);
setPagination({
pageIndex: 0,
pageSize: Number(showData),
});
}, [page, showData]);
let typingTimer: any;
const doneTypingInterval = 1500;
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
async function doneTyping() {
fetchData();
}
async function fetchData() {
try {
const res = await listTicketingInternal(page - 1, limit, search);
const res = await listTicketingInternal(
page - 1,
Number(showData),
search
);
const data = res?.data?.data;
const contentData = data?.content;
contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * limit + index + 1;
item.no = (page - 1) * Number(showData) + index + 1;
});
console.log("contentData : ", contentData);
@ -145,21 +164,10 @@ const TableAudio = () => {
}
}
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 ">
<div className="flex justify-between items-center px-5">
<div className="flex justify-between items-center">
<div className="mt-3 flex flex-row items-center gap-2">
<Link href={"/shared/communication/internal/create"}>
<Button color="primary" className="text-white" size="md">
<UploadIcon />
Pertanyaan Baru
</Button>
</Link>
<InputGroup merged>
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
<Search className=" h-4 w-4 dark:text-white" />
@ -169,22 +177,38 @@ const TableAudio = () => {
placeholder="Search Judul..."
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
value={search}
onChange={handleSearch}
onChange={(e) => setSearch(e.target.value)}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
/>
</InputGroup>
</div>
<div className="flex-none">
<Input
placeholder="Filter Status..."
value={
(table.getColumn("status")?.getFilterValue() as string) ?? ""
}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
table.getColumn("status")?.setFilterValue(event.target.value)
}
className="max-w-sm "
/>
</div>
<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>
</div>
<Table className="overflow-hidden mt-3">
<TableHeader>

View File

@ -1,3 +1,4 @@
"use client";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import InternalTable from "./internal/components/internal-table";
@ -5,71 +6,77 @@ import SiteBreadcrumb from "@/components/site-breadcrumb";
import CollaborationTable from "./collaboration/components/collabroation-table";
import EscalationTable from "./escalation/components/escalation-table";
import { useState } from "react";
import { Link, useRouter } from "@/i18n/routing";
import { PlusIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
const CommunicationPage = () => {
const [tab, setTab] = useState("Komunikasi");
return (
<div>
<SiteBreadcrumb />
<div className="my-3">
<Card className="py-3 px-2 my-4">
<Tabs defaultValue="internal" className="w-full">
<p className="text-lg font-semibold ml-2">Komunikasi</p>
<TabsList className="flex-wrap">
<TabsTrigger
value="internal"
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
>
Pertanyaan Internal
</TabsTrigger>
<TabsTrigger
value="escalation"
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
>
Eskalasi
</TabsTrigger>
<TabsTrigger
value="collaboration"
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
>
Kolaborasi
</TabsTrigger>
</TabsList>
<TabsContent value="internal">
<div className="grid grid-cols-12 gap-5">
<div className="lg:col-span-12 col-span-12">
<Card>
<CardContent className="p-0">
<InternalTable />
</CardContent>
</Card>
</div>
</div>
</TabsContent>
<TabsContent value="escalation">
<div className="grid grid-cols-12 gap-5">
<div className="lg:col-span-12 col-span-12">
<Card>
<CardContent className="p-0">
<EscalationTable />
</CardContent>
</Card>
</div>
</div>
</TabsContent>
<TabsContent value="collaboration">
<div className="grid grid-cols-12 gap-5">
<div className="lg:col-span-12 col-span-12">
<Card>
<CardContent className="p-0">
<CollaborationTable />
</CardContent>
</Card>
</div>
</div>
</TabsContent>
</Tabs>
</Card>
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
<div className="flex justify-between py-3">
<p className="text-lg">{tab}</p>
{tab === "Komunikasi" && (
<Link href="/shared/communication/internal/create">
<Button color="primary" size="md">
<PlusIcon />
Pertanyaan baru
</Button>
</Link>
)}
{tab === "Kolaborasi" && (
<Link href="/shared/communication/collaboration/create">
<Button color="primary" size="md">
<PlusIcon />
Kolaborasi baru
</Button>
</Link>
)}
</div>
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
<Button
rounded="md"
onClick={() => setTab("Komunikasi")}
className={` hover:text-white
${
tab === "Komunikasi"
? "bg-black text-white "
: "bg-white text-black "
}`}
>
Komunikasi
</Button>
<Button
rounded="md"
onClick={() => setTab("Eskalasi")}
className={` hover:text-white
${
tab === "Eskalasi"
? "bg-black text-white "
: "bg-white text-black "
}`}
>
Eskalasi
</Button>
<Button
rounded="md"
onClick={() => setTab("Kolaborasi")}
className={` hover:text-white
${
tab === "Kolaborasi"
? "bg-black text-white "
: "bg-white text-black "
}`}
>
Kolaborasi
</Button>
</div>
{tab === "Komunikasi" && <InternalTable />}
{tab === "Eskalasi" && <EscalationTable />}
{tab === "Kolaborasi" && <CollaborationTable />}
</div>
</div>
);

View File

@ -17,7 +17,10 @@ import { Link, useRouter } from "@/i18n/routing";
import { setBanner } from "@/service/settings/settings";
import { error } from "@/config/swal";
import { useToast } from "@/components/ui/use-toast";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { deleteUser } from "@/service/management-user/management-user";
import { stringify } from "querystring";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
@ -89,11 +92,34 @@ const columns: ColumnDef<any>[] = [
enableHiding: false,
cell: ({ row }) => {
const { toast } = useToast();
const handleBanner = async (id: number) => {
const response = setBanner(id, true);
const MySwal = withReactContent(Swal);
const router = useRouter();
const doDelete = async (id: number) => {
const response = await deleteUser(id);
if (response?.error) {
toast({
title: stringify(response?.message),
variant: "destructive",
});
}
toast({
title: "Success",
title: "Success delete",
});
router.push("?dataChange=true");
};
const handleDelete = (id: number) => {
MySwal.fire({
title: "Apakah anda ingin menghapus data user?",
showCancelButton: true,
confirmButtonColor: "#dc3545",
confirmButtonText: "Iya",
cancelButtonText: "Tidak",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
return (
@ -119,7 +145,7 @@ const columns: ColumnDef<any>[] = [
</DropdownMenuItem>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Link
href={`/admin/management-user/external/detail/${row.original.id}`}
href={`/admin/management-user/external/edit/${row.original.id}`}
>
Edit
</Link>
@ -128,7 +154,13 @@ const columns: ColumnDef<any>[] = [
color="red"
className="p-2 border-b text-red-500 group focus:bg-red-500 focus:text-white rounded-none"
>
<a onClick={() => {}}>Hapus</a>
<a
onClick={() => {
handleDelete(row.original.id);
}}
>
Hapus
</a>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

View File

@ -36,7 +36,7 @@ import {
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { useRouter, useSearchParams } from "next/navigation";
import { useSearchParams } from "next/navigation";
import TablePagination from "@/components/table/table-pagination";
import columns from "./management-user-external-column-table";
import {
@ -47,7 +47,7 @@ import {
import { Checkbox } from "@/components/ui/checkbox";
import { close, loading } from "@/config/swal";
import { Link } from "@/i18n/routing";
import { useRouter, Link } from "@/i18n/routing";
import { AdministrationUserList } from "@/service/management-user/management-user";
const UserExternalTable = () => {
@ -55,6 +55,7 @@ const UserExternalTable = () => {
const levelId = getCookiesDecrypt("ulie");
const searchParams = useSearchParams();
const dataChange = searchParams?.get("dataChange");
const [showData, setShowData] = React.useState("10");
const [categories, setCategories] = React.useState<any>();
const [dataTable, setDataTable] = React.useState<any[]>([]);
@ -106,13 +107,16 @@ const UserExternalTable = () => {
const handleKeyDown = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {
fetchData();
}
React.useEffect(() => {
router.push("/admin/management-user");
}, [dataChange]);
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
@ -180,9 +184,6 @@ const UserExternalTable = () => {
return (
<>
<div className="flex justify-between py-3">
<p className="text-lg">Data User Eksternal</p>
</div>
<div className="flex justify-between py-3">
<Input
type="text"

View File

@ -1,6 +1,7 @@
import * as React from "react";
import { ColumnDef } from "@tanstack/react-table";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
import { cn } from "@/lib/utils";
import {
@ -10,30 +11,12 @@ import {
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 { formatDateToIndonesian } 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";
import { deleteUser } from "@/service/management-user/management-user";
import { stringify } from "querystring";
const columns: ColumnDef<any>[] = [
{
@ -106,11 +89,34 @@ const columns: ColumnDef<any>[] = [
enableHiding: false,
cell: ({ row }) => {
const { toast } = useToast();
const handleBanner = async (id: number) => {
const response = setBanner(id, true);
const MySwal = withReactContent(Swal);
const router = useRouter();
const doDelete = async (id: number) => {
const response = await deleteUser(id);
if (response?.error) {
toast({
title: stringify(response?.message),
variant: "destructive",
});
}
toast({
title: "Success",
title: "Success delete",
});
router.push("?dataChange=true");
};
const handleDelete = (id: number) => {
MySwal.fire({
title: "Apakah anda ingin menghapus data user?",
showCancelButton: true,
confirmButtonColor: "#dc3545",
confirmButtonText: "Iya",
cancelButtonText: "Tidak",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
return (
@ -142,7 +148,13 @@ const columns: ColumnDef<any>[] = [
color="red"
className="p-2 border-b text-red-500 group focus:bg-red-500 focus:text-white rounded-none"
>
<a onClick={() => {}}>Hapus</a>
<a
onClick={() => {
handleDelete(row.original.id);
}}
>
Hapus
</a>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

View File

@ -52,7 +52,7 @@ 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 { useSearchParams } from "next/navigation";
import TablePagination from "@/components/table/table-pagination";
import columns from "./management-user-internal-column-table";
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
@ -65,7 +65,7 @@ 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 { Link, useRouter } from "@/i18n/routing";
import { AdministrationUserList } from "@/service/management-user/management-user";
const UserInternalTable = () => {
@ -73,6 +73,7 @@ const UserInternalTable = () => {
const levelId = getCookiesDecrypt("ulie");
const searchParams = useSearchParams();
const dataChange = searchParams?.get("dataChange");
const [showData, setShowData] = React.useState("10");
const [categories, setCategories] = React.useState<any>();
const [dataTable, setDataTable] = React.useState<any[]>([]);
@ -125,13 +126,17 @@ const UserInternalTable = () => {
const handleKeyDown = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {
fetchData();
}
React.useEffect(() => {
fetchData();
router.push("/admin/management-user");
}, [dataChange]);
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
@ -199,16 +204,6 @@ const UserInternalTable = () => {
return (
<>
<div className="flex justify-between py-3">
<p className="text-lg">Data User Internal</p>
<Link href="/admin/management-user/internal/create">
{" "}
<Button color="primary" size="md">
<PlusIcon />
Add User
</Button>
</Link>
</div>
<div className="flex justify-between py-3">
<Input
type="text"

View File

@ -1,4 +1,5 @@
import {
httpDeleteInterceptor,
httpGetInterceptor,
httpPostInterceptor,
} from "../http-config/http-interceptor-service";
@ -71,3 +72,31 @@ export async function getTicketingDetail(id: any) {
const url = `ticketing?id=${id}`;
return httpGetInterceptor(url);
}
export async function getTicketCollaborationTeams(id: string | number) {
const url = `ticketing/collaboration/teams?ticketId=${id}`;
return httpGetInterceptor(url);
}
export async function saveCollaborationTeams(
id: string | number,
userId: string | number
) {
const url = `ticketing/collaboration/teams?ticketId=${id}&userId=${userId}`;
return httpPostInterceptor(url);
}
export async function getCollabDiscussion(id: string | number) {
const url = `ticketing/collaboration/discussion?ticketId=${id}`;
return httpGetInterceptor(url);
}
export async function saveCollabDiscussion(data: any) {
const url = "ticketing/collaboration/discussion";
return httpPostInterceptor(url, data);
}
export async function deleteCollabDiscussion(id: string | number) {
const url = `ticketing/collaboration/discussion?id=${id}`;
return httpDeleteInterceptor(url);
}

View File

@ -44,3 +44,13 @@ export async function getUserById(id: string) {
const url = `users?id=${id}`;
return httpGetInterceptor(url);
}
export async function disableUser(userId: number | string) {
const url = `users/disable-user?userId=${userId}`;
return httpPostInterceptor(url);
}
export async function deleteUser(userId: number | string) {
const url = `users/delete-user?userId=${userId}`;
return httpDeleteInterceptor(url);
}

View File

@ -9,6 +9,11 @@ export async function getCategories(page: number) {
return httpGetInterceptor(url);
}
export async function getCategoriesAll() {
const url = "media/categories/list?enablePage=0&size=100&sort=desc&sortBy=id";
return httpGetInterceptor(url);
}
export async function publishUnpublishCategory(id: number, status: string) {
const url = `media/categories/publish?id=${id}&status=${status}`;
return httpPostInterceptor(url);
@ -66,3 +71,61 @@ export async function setBanner(id: number, status: boolean) {
const url = `media/banner?id=${id}&status=${status}`;
return httpPostInterceptor(url);
}
export async function getListFAQ() {
const url = "master/faq/list";
return httpGetInterceptor(url);
}
export async function postDataFAQ(data: any) {
const url = "master/faq";
return httpPostInterceptor(url, data);
}
export async function deleteDataFAQ(id: string) {
const url = `master/faq/${id}`;
return httpDeleteInterceptor(url);
}
export async function detailDataFAQ(id: string) {
const url = `master/faq/${id}`;
return httpGetInterceptor(url);
}
export async function getListFeedback() {
const url = "master/feedback/list";
return httpGetInterceptor(url);
}
// export async function getUserFeedbacks() {
// const url = 'feedback/list-all';
// return httpGetInterceptor({ url });
// }
export async function detailDataFeedback(id: string) {
const url = `master/feedback/${id}`;
return httpGetInterceptor(url);
}
export async function postDataFeedback(data: any) {
const url = "master/feedback";
return httpPostInterceptor(url, data);
}
export async function deleteDataFeedback(id: string | number) {
const url = `master/feedback/${id}`;
return httpDeleteInterceptor(url);
}
export async function getTags() {
const url = "media/tags/list";
return httpGetInterceptor(url);
}
export async function getTagsBySubCategoryId(subCategory: string | number) {
const url = `media/tags/list?subCategoryId=${subCategory}`;
return httpGetInterceptor(url);
}
export async function getTagsByParentId(parentId: string | number) {
const url = `media/tags/list?categoryId=${parentId}`;
return httpGetInterceptor(url);
}