feat:fix task,konten
This commit is contained in:
commit
c667987dac
|
|
@ -124,7 +124,6 @@ const AddExpertTable = () => {
|
||||||
|
|
||||||
const handleKeyDown = () => {
|
const handleKeyDown = () => {
|
||||||
clearTimeout(typingTimer);
|
clearTimeout(typingTimer);
|
||||||
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
async function doneTyping() {
|
async function doneTyping() {
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ import { Link } from "@/i18n/routing";
|
||||||
const BroadcastTable = () => {
|
const BroadcastTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
const [search, setSearch] = React.useState("");
|
||||||
const [showData, setShowData] = React.useState("10");
|
const [showData, setShowData] = React.useState("10");
|
||||||
const [categories, setCategories] = React.useState<any>();
|
const [categories, setCategories] = React.useState<any>();
|
||||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
|
|
@ -119,7 +120,6 @@ const BroadcastTable = () => {
|
||||||
|
|
||||||
const handleKeyDown = () => {
|
const handleKeyDown = () => {
|
||||||
clearTimeout(typingTimer);
|
clearTimeout(typingTimer);
|
||||||
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
async function doneTyping() {
|
async function doneTyping() {
|
||||||
|
|
@ -147,7 +147,7 @@ const BroadcastTable = () => {
|
||||||
const res = await listDataMedia(
|
const res = await listDataMedia(
|
||||||
page - 1,
|
page - 1,
|
||||||
showData,
|
showData,
|
||||||
"",
|
search,
|
||||||
categoryFilter?.sort().join(","),
|
categoryFilter?.sort().join(","),
|
||||||
statusFilter?.sort().join(",")
|
statusFilter?.sort().join(",")
|
||||||
);
|
);
|
||||||
|
|
@ -218,6 +218,7 @@ const BroadcastTable = () => {
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
onKeyUp={handleKeyUp}
|
onKeyUp={handleKeyUp}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
className="max-w-[300px]"
|
className="max-w-[300px]"
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row gap-2">
|
||||||
|
|
|
||||||
291
app/[locale]/(protected)/admin/management-user/external/detail/[id]/page.tsx
vendored
Normal file
291
app/[locale]/(protected)/admin/management-user/external/detail/[id]/page.tsx
vendored
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,752 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { Check, ChevronsUpDown } from "lucide-react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
} from "@/components/ui/command";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
import {
|
||||||
|
AdministrationLevelList,
|
||||||
|
getListCompetencies,
|
||||||
|
getListEducation,
|
||||||
|
getListSchools,
|
||||||
|
saveUserInternal,
|
||||||
|
} from "@/service/management-user/management-user";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import { close, error, loading } from "@/config/swal";
|
||||||
|
|
||||||
|
const PasswordChecklist = dynamic(() => import("react-password-checklist"), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const sns = [
|
||||||
|
{
|
||||||
|
key: 1,
|
||||||
|
id: "comment",
|
||||||
|
typeId: 1,
|
||||||
|
name: "Komentar Konten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 2,
|
||||||
|
id: "fb",
|
||||||
|
typeId: 2,
|
||||||
|
name: "Facebook",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 3,
|
||||||
|
id: "ig",
|
||||||
|
typeId: 3,
|
||||||
|
name: "Instagram",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 4,
|
||||||
|
id: "twt",
|
||||||
|
typeId: 4,
|
||||||
|
name: "Twitter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 5,
|
||||||
|
id: "yt",
|
||||||
|
typeId: 5,
|
||||||
|
name: "Youtube",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 6,
|
||||||
|
id: "emergency",
|
||||||
|
typeId: 6,
|
||||||
|
name: "Emergency Issue",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 7,
|
||||||
|
id: "email",
|
||||||
|
typeId: 7,
|
||||||
|
name: "Email",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 8,
|
||||||
|
id: "inbox",
|
||||||
|
typeId: 8,
|
||||||
|
name: "Pesan Masuk",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 9,
|
||||||
|
id: "whatsapp",
|
||||||
|
typeId: 9,
|
||||||
|
name: "Whatssapp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 10,
|
||||||
|
id: "tiktok",
|
||||||
|
typeId: 10,
|
||||||
|
name: "Tiktok",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
interface RoleData {
|
||||||
|
id: number;
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
levelNumber: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
level: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
fullname: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
username: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
role: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
nrp: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
address: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
email: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
phoneNumber: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
password: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
confirmPassword: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
isValidPassword: z.boolean().refine((val) => val === true, {
|
||||||
|
message: "Check Password",
|
||||||
|
}),
|
||||||
|
sns: z.array(z.string()).optional(),
|
||||||
|
education: z.string().optional(),
|
||||||
|
school: z.string().optional(),
|
||||||
|
competency: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function CreateUserForm() {
|
||||||
|
const router = useRouter();
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const levelName = getCookiesDecrypt("ulnae");
|
||||||
|
const [roleList, setRoleList] = useState<RoleData[]>([]);
|
||||||
|
|
||||||
|
const [userEducations, setUserEducations] = useState<any>();
|
||||||
|
const [userSchools, setUserSchools] = useState<any>();
|
||||||
|
const [userCompetencies, setUserCompetencies] = useState<any>();
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: {
|
||||||
|
password: "",
|
||||||
|
confirmPassword: "",
|
||||||
|
sns: [],
|
||||||
|
education: "1",
|
||||||
|
school: "4",
|
||||||
|
competency: "2",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const passwordVal = form.watch("password");
|
||||||
|
const confPasswordVal = form.watch("confirmPassword");
|
||||||
|
const selectedRole = form.watch("role");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
initFetch();
|
||||||
|
getDataAdditional();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const initFetch = async () => {
|
||||||
|
const response = await AdministrationLevelList();
|
||||||
|
const res = response?.data?.data;
|
||||||
|
var levelsArr: RoleData[] = [];
|
||||||
|
res.forEach((levels: RoleData) => {
|
||||||
|
levelsArr.push({
|
||||||
|
id: levels.id,
|
||||||
|
label: levels.name,
|
||||||
|
name: levels.name,
|
||||||
|
value: String(levels.id),
|
||||||
|
levelNumber: levels.levelNumber,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setRoleList(levelsArr);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getDataAdditional() {
|
||||||
|
const resEducations = await getListEducation();
|
||||||
|
setUserEducations(resEducations?.data?.data);
|
||||||
|
const resSchools = await getListSchools();
|
||||||
|
setUserSchools(resSchools?.data?.data);
|
||||||
|
const resCompetencies = await getListCompetencies();
|
||||||
|
setUserCompetencies(resCompetencies?.data?.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const roles =
|
||||||
|
levelName == "MABES POLRI"
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
id: "ADM-ID",
|
||||||
|
name: "Admin",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "APP-ID",
|
||||||
|
name: "Approver",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "CON-ID",
|
||||||
|
name: "Kontributor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "SPV-ID",
|
||||||
|
name: "Supervisor Feedback Center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "OPT-ID",
|
||||||
|
name: "Operator Feedback Center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "KKUR-ID",
|
||||||
|
name: "Koor Kurator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "KUR-ID",
|
||||||
|
name: "Kurator",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
id: "APP-ID",
|
||||||
|
name: "Approver",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "CON-ID",
|
||||||
|
name: "Kontributor",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function save(data: z.infer<typeof FormSchema>) {
|
||||||
|
let req: any = {
|
||||||
|
firstName: data.fullname,
|
||||||
|
username: data.username,
|
||||||
|
roleId: data.role,
|
||||||
|
userLevelId: Number(data.level),
|
||||||
|
memberIdentity: data.nrp,
|
||||||
|
address: data.address,
|
||||||
|
email: data.email,
|
||||||
|
phoneNumber: data.phoneNumber,
|
||||||
|
password: data.password,
|
||||||
|
passwordConf: data.confirmPassword,
|
||||||
|
isDefault: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.role == "OPT-ID") {
|
||||||
|
req.handledSocialMedia = data?.sns ? data.sns.join(",") : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.role == "KUR-ID") {
|
||||||
|
req.userEducationId = Number(data.education);
|
||||||
|
req.userSchoolsId = Number(data.school);
|
||||||
|
req.userCompetencyId = Number(data.competency);
|
||||||
|
}
|
||||||
|
|
||||||
|
loading();
|
||||||
|
const response = await saveUserInternal(req);
|
||||||
|
|
||||||
|
if (response?.error) {
|
||||||
|
error(response.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
close();
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Sukses",
|
||||||
|
icon: "success",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Oke",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
router.push("/admin/management-user");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Simpan Data?",
|
||||||
|
text: "",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Simpan",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
save(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="space-y-6 bg-white p-10 w-full"
|
||||||
|
>
|
||||||
|
<p className="text-xl">Data Pengelola Media Hub</p>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="level"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-col">
|
||||||
|
<FormLabel>Pilih Level</FormLabel>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<FormControl>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
className={cn(
|
||||||
|
"w-[400px] justify-between",
|
||||||
|
!field.value && "text-muted-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{field.value
|
||||||
|
? roleList.find((role) => role.value === field.value)
|
||||||
|
?.label
|
||||||
|
: "Pilih level"}
|
||||||
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</FormControl>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-[400px] p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No role found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{roleList.map((role) => (
|
||||||
|
<CommandItem
|
||||||
|
value={role.label}
|
||||||
|
key={role.value}
|
||||||
|
onSelect={() => {
|
||||||
|
form.setValue("level", role.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{role.label}
|
||||||
|
<Check
|
||||||
|
className={cn(
|
||||||
|
"ml-auto",
|
||||||
|
role.value === field.value
|
||||||
|
? "opacity-100"
|
||||||
|
: "opacity-0"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="fullname"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nama Lengkap</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="Masukkan nama lengkap"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="username"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Username</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="Masukkan username"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="role"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="space-y-3">
|
||||||
|
<FormLabel>Role</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value}
|
||||||
|
className="flex flex-wrap gap-3 w-1/2"
|
||||||
|
>
|
||||||
|
{roles.map((role) => (
|
||||||
|
<FormItem
|
||||||
|
key={role.id}
|
||||||
|
className="flex items-center space-x-3 space-y-0"
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value={role.id} />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
{role.name}
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{selectedRole === "OPT-ID" && (
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="sns"
|
||||||
|
render={() => (
|
||||||
|
<FormItem>
|
||||||
|
<div className="mb-4">
|
||||||
|
<FormLabel>Social Media Yang Ditangani</FormLabel>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-5 gap-2 w-1/2">
|
||||||
|
{sns.map((item) => (
|
||||||
|
<FormField
|
||||||
|
key={item.id}
|
||||||
|
control={form.control}
|
||||||
|
name="sns"
|
||||||
|
render={({ field }) => {
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
key={item.typeId}
|
||||||
|
className="flex flex-row items-start space-x-3 space-y-0"
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
checked={field.value?.includes(
|
||||||
|
String(item.typeId)
|
||||||
|
)}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
return checked
|
||||||
|
? field.onChange([
|
||||||
|
...(field.value || []),
|
||||||
|
String(item.typeId),
|
||||||
|
])
|
||||||
|
: field.onChange(
|
||||||
|
(field.value || []).filter(
|
||||||
|
(value) =>
|
||||||
|
value !== String(item.typeId)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
{item.name}
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{selectedRole === "KUR-ID" && (
|
||||||
|
<>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="education"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Pendidikan Terakhir</FormLabel>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{userEducations?.map((edu: any) => (
|
||||||
|
<SelectItem key={edu.id} value={String(edu.id)}>
|
||||||
|
{edu.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="school"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Universitas / Perguruan Tinggi</FormLabel>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{userSchools?.map((edu: any) => (
|
||||||
|
<SelectItem key={edu.id} value={String(edu.id)}>
|
||||||
|
{edu.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="competency"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Kompetensi</FormLabel>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{userCompetencies?.map((edu: any) => (
|
||||||
|
<SelectItem key={edu.id} value={String(edu.id)}>
|
||||||
|
{edu.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="nrp"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nomor Regitrasi Polri {`(NRP)`}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="Masukkan NRP"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="address"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Alamat</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Masukkan alamat"
|
||||||
|
className="resize-none w-1/2"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="email"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Email</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="email"
|
||||||
|
placeholder="Masukkan email"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="phoneNumber"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>No. Handphone</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="Masukkan nomor handphone"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="password"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Password</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
placeholder="Masukkan kata sandi"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="confirmPassword"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Konfirmasi Kata Sandi</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
placeholder="Masukkan kata sandi"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<PasswordChecklist
|
||||||
|
rules={["minLength", "specialChar", "number", "capital", "match"]}
|
||||||
|
minLength={8}
|
||||||
|
value={passwordVal}
|
||||||
|
valueAgain={confPasswordVal}
|
||||||
|
onChange={(isValid) => {
|
||||||
|
form.setValue("isValidPassword", isValid);
|
||||||
|
}}
|
||||||
|
className="text-sm"
|
||||||
|
messages={{
|
||||||
|
minLength: "Password harus lebih dari 8 karakter",
|
||||||
|
specialChar: "Password harus memiliki spesial karakter",
|
||||||
|
number: "Password harus memiliki angka",
|
||||||
|
capital: "Password harus memiliki huruf kapital",
|
||||||
|
match: "Password sama",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Link href="/admin/management-user">
|
||||||
|
<Button type="button" color="primary" variant="outline">
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Button type="submit" color="primary" className="mx-3">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,743 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { Check, ChevronsUpDown } from "lucide-react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
} from "@/components/ui/command";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
import {
|
||||||
|
AdministrationLevelList,
|
||||||
|
getListCompetencies,
|
||||||
|
getListEducation,
|
||||||
|
getListSchools,
|
||||||
|
getUserById,
|
||||||
|
saveUserInternal,
|
||||||
|
} from "@/service/management-user/management-user";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import { close, error, loading } from "@/config/swal";
|
||||||
|
import { useParams } from "next/navigation";
|
||||||
|
|
||||||
|
const sns = [
|
||||||
|
{
|
||||||
|
key: 1,
|
||||||
|
id: "comment",
|
||||||
|
typeId: 1,
|
||||||
|
name: "Komentar Konten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 2,
|
||||||
|
id: "fb",
|
||||||
|
typeId: 2,
|
||||||
|
name: "Facebook",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 3,
|
||||||
|
id: "ig",
|
||||||
|
typeId: 3,
|
||||||
|
name: "Instagram",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 4,
|
||||||
|
id: "twt",
|
||||||
|
typeId: 4,
|
||||||
|
name: "Twitter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 5,
|
||||||
|
id: "yt",
|
||||||
|
typeId: 5,
|
||||||
|
name: "Youtube",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 6,
|
||||||
|
id: "emergency",
|
||||||
|
typeId: 6,
|
||||||
|
name: "Emergency Issue",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 7,
|
||||||
|
id: "email",
|
||||||
|
typeId: 7,
|
||||||
|
name: "Email",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 8,
|
||||||
|
id: "inbox",
|
||||||
|
typeId: 8,
|
||||||
|
name: "Pesan Masuk",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 9,
|
||||||
|
id: "whatsapp",
|
||||||
|
typeId: 9,
|
||||||
|
name: "Whatssapp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 10,
|
||||||
|
id: "tiktok",
|
||||||
|
typeId: 10,
|
||||||
|
name: "Tiktok",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
interface RoleData {
|
||||||
|
id: number;
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
levelNumber: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
level: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
fullname: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
username: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
role: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
nrp: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
address: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
email: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
phoneNumber: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
password: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
confirmPassword: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
isValidPassword: z.boolean().refine((val) => val === true, {
|
||||||
|
message: "Check Password",
|
||||||
|
}),
|
||||||
|
sns: z.array(z.string()).optional(),
|
||||||
|
education: z.string().optional(),
|
||||||
|
school: z.string().optional(),
|
||||||
|
competency: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function DetailUserForm() {
|
||||||
|
const router = useRouter();
|
||||||
|
const params = useParams();
|
||||||
|
const id = params?.id;
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const levelName = getCookiesDecrypt("ulnae");
|
||||||
|
const [roleList, setRoleList] = useState<RoleData[]>([]);
|
||||||
|
|
||||||
|
const [userEducations, setUserEducations] = useState<any>();
|
||||||
|
const [userSchools, setUserSchools] = useState<any>();
|
||||||
|
const [userCompetencies, setUserCompetencies] = useState<any>();
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: {
|
||||||
|
password: "",
|
||||||
|
confirmPassword: "",
|
||||||
|
sns: [],
|
||||||
|
education: "1",
|
||||||
|
school: "4",
|
||||||
|
competency: "2",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const passwordVal = form.watch("password");
|
||||||
|
const confPasswordVal = form.watch("confirmPassword");
|
||||||
|
const selectedRole = form.watch("role");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getDataAdditional();
|
||||||
|
initData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const initData = async () => {
|
||||||
|
loading();
|
||||||
|
const response = await getUserById(String(id));
|
||||||
|
const res = response?.data?.data;
|
||||||
|
close();
|
||||||
|
console.log("res", res);
|
||||||
|
if (Number(res.roleId) > 4) {
|
||||||
|
form.setValue("fullname", res?.fullname);
|
||||||
|
form.setValue("username", res?.username);
|
||||||
|
form.setValue("phoneNumber", res?.phoneNumber);
|
||||||
|
form.setValue("nrp", res?.memberIdentity);
|
||||||
|
form.setValue("address", res?.address);
|
||||||
|
form.setValue("email", res?.email);
|
||||||
|
form.setValue("role", res?.role?.code);
|
||||||
|
form.setValue("level", String(res?.userLevelId));
|
||||||
|
} else {
|
||||||
|
initFetch();
|
||||||
|
console.log("sadad", res?.role?.code);
|
||||||
|
form.setValue("fullname", res?.fullname);
|
||||||
|
form.setValue("username", res?.username);
|
||||||
|
form.setValue("phoneNumber", res?.phoneNumber);
|
||||||
|
form.setValue("nrp", res?.memberIdentity);
|
||||||
|
form.setValue("address", res?.address);
|
||||||
|
form.setValue("email", res?.email);
|
||||||
|
form.setValue("role", res?.role?.code);
|
||||||
|
form.setValue("level", String(res?.userLevelId));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const initFetch = async () => {
|
||||||
|
const response = await AdministrationLevelList();
|
||||||
|
const res = response?.data?.data;
|
||||||
|
var levelsArr: RoleData[] = [];
|
||||||
|
res.forEach((levels: RoleData) => {
|
||||||
|
levelsArr.push({
|
||||||
|
id: levels.id,
|
||||||
|
label: levels.name,
|
||||||
|
name: levels.name,
|
||||||
|
value: String(levels.id),
|
||||||
|
levelNumber: levels.levelNumber,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setRoleList(levelsArr);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getDataAdditional() {
|
||||||
|
const resEducations = await getListEducation();
|
||||||
|
setUserEducations(resEducations?.data?.data);
|
||||||
|
const resSchools = await getListSchools();
|
||||||
|
setUserSchools(resSchools?.data?.data);
|
||||||
|
const resCompetencies = await getListCompetencies();
|
||||||
|
setUserCompetencies(resCompetencies?.data?.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const roles =
|
||||||
|
levelName == "MABES POLRI"
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
id: "ADM-ID",
|
||||||
|
name: "Admin",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "APP-ID",
|
||||||
|
name: "Approver",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "CON-ID",
|
||||||
|
name: "Kontributor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "SPV-ID",
|
||||||
|
name: "Supervisor Feedback Center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "OPT-ID",
|
||||||
|
name: "Operator Feedback Center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "KKUR-ID",
|
||||||
|
name: "Koor Kurator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "KUR-ID",
|
||||||
|
name: "Kurator",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
id: "APP-ID",
|
||||||
|
name: "Approver",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "CON-ID",
|
||||||
|
name: "Kontributor",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function save(data: z.infer<typeof FormSchema>) {
|
||||||
|
let req: any = {
|
||||||
|
id: id,
|
||||||
|
firstName: data.fullname,
|
||||||
|
username: data.username,
|
||||||
|
roleId: data.role,
|
||||||
|
userLevelId: Number(data.level),
|
||||||
|
memberIdentity: data.nrp,
|
||||||
|
address: data.address,
|
||||||
|
email: data.email,
|
||||||
|
password: data.password,
|
||||||
|
passwordConf: data.confirmPassword,
|
||||||
|
isDefault: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.role == "OPT-ID") {
|
||||||
|
req.handledSocialMedia = data?.sns ? data.sns.join(",") : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.role == "KUR-ID") {
|
||||||
|
req.userEducationId = Number(data.education);
|
||||||
|
req.userSchoolsId = Number(data.school);
|
||||||
|
req.userCompetencyId = Number(data.competency);
|
||||||
|
}
|
||||||
|
|
||||||
|
loading();
|
||||||
|
const response = await saveUserInternal(req);
|
||||||
|
|
||||||
|
if (response?.error) {
|
||||||
|
error(response.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
close();
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Sukses",
|
||||||
|
icon: "success",
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Simpan",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
router.push("/admin/management-user");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Simpan Data?",
|
||||||
|
text: "",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Simpan",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
save(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="space-y-6 bg-white p-10 w-full"
|
||||||
|
>
|
||||||
|
<p className="text-xl">Data Pengelola Media Hub</p>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="level"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-col">
|
||||||
|
<FormLabel>Pilih Level</FormLabel>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild disabled>
|
||||||
|
<FormControl>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
className={cn(
|
||||||
|
"w-[400px] justify-between",
|
||||||
|
!field.value && "text-muted-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{field.value
|
||||||
|
? roleList.find((role) => role.value === field.value)
|
||||||
|
?.label
|
||||||
|
: "Pilih level"}
|
||||||
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</FormControl>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-[400px] p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No role found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{roleList.map((role) => (
|
||||||
|
<CommandItem
|
||||||
|
value={role.label}
|
||||||
|
key={role.value}
|
||||||
|
onSelect={() => {
|
||||||
|
form.setValue("level", role.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{role.label}
|
||||||
|
<Check
|
||||||
|
className={cn(
|
||||||
|
"ml-auto",
|
||||||
|
role.value === field.value
|
||||||
|
? "opacity-100"
|
||||||
|
: "opacity-0"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="fullname"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nama Lengkap</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="Masukkan nama lengkap"
|
||||||
|
{...field}
|
||||||
|
readOnly
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="username"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Username</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="Masukkan username"
|
||||||
|
{...field}
|
||||||
|
readOnly
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="role"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="space-y-3">
|
||||||
|
<FormLabel>Role</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
value={field.value}
|
||||||
|
className="flex flex-wrap gap-3 w-1/2"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
{roles.map((role) => (
|
||||||
|
<FormItem
|
||||||
|
key={role.id}
|
||||||
|
className="flex items-center space-x-3 space-y-0"
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value={role.id} />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
{role.name}
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{selectedRole === "OPT-ID" && (
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="sns"
|
||||||
|
render={() => (
|
||||||
|
<FormItem>
|
||||||
|
<div className="mb-4">
|
||||||
|
<FormLabel>Social Media Yang Ditangani</FormLabel>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-5 gap-2 w-1/2">
|
||||||
|
{sns.map((item) => (
|
||||||
|
<FormField
|
||||||
|
key={item.id}
|
||||||
|
control={form.control}
|
||||||
|
name="sns"
|
||||||
|
render={({ field }) => {
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
key={item.typeId}
|
||||||
|
className="flex flex-row items-start space-x-3 space-y-0"
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
disabled
|
||||||
|
checked={field.value?.includes(
|
||||||
|
String(item.typeId)
|
||||||
|
)}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
return checked
|
||||||
|
? field.onChange([
|
||||||
|
...(field.value || []),
|
||||||
|
String(item.typeId),
|
||||||
|
])
|
||||||
|
: field.onChange(
|
||||||
|
(field.value || []).filter(
|
||||||
|
(value) =>
|
||||||
|
value !== String(item.typeId)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
{item.name}
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{selectedRole === "KUR-ID" && (
|
||||||
|
<>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="education"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Pendidikan Terakhir</FormLabel>
|
||||||
|
<Select
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
value={field.value}
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{userEducations?.map((edu: any) => (
|
||||||
|
<SelectItem key={edu.id} value={String(edu.id)}>
|
||||||
|
{edu.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="school"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Universitas / Perguruan Tinggi</FormLabel>
|
||||||
|
<Select
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
value={field.value}
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{userSchools?.map((edu: any) => (
|
||||||
|
<SelectItem key={edu.id} value={String(edu.id)}>
|
||||||
|
{edu.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="competency"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Kompetensi</FormLabel>
|
||||||
|
<Select
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
value={field.value}
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{userCompetencies?.map((edu: any) => (
|
||||||
|
<SelectItem key={edu.id} value={String(edu.id)}>
|
||||||
|
{edu.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="nrp"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nomor Regitrasi Polri {`(NRP)`}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="Masukkan NRP"
|
||||||
|
readOnly
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="address"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Alamat</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Masukkan alamat"
|
||||||
|
readOnly
|
||||||
|
className="resize-none w-1/2"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="email"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Email</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="email"
|
||||||
|
readOnly
|
||||||
|
placeholder="Masukkan email"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="phoneNumber"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>No. Handphone</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="Masukkan nomor handphone"
|
||||||
|
{...field}
|
||||||
|
readOnly
|
||||||
|
className="w-1/2 mb-2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Link href="/admin/management-user">
|
||||||
|
<Button type="button" color="primary" variant="outline">
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,784 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { Check, ChevronsUpDown } from "lucide-react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
} from "@/components/ui/command";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
import {
|
||||||
|
AdministrationLevelList,
|
||||||
|
getListCompetencies,
|
||||||
|
getListEducation,
|
||||||
|
getListSchools,
|
||||||
|
getUserById,
|
||||||
|
saveUserInternal,
|
||||||
|
} from "@/service/management-user/management-user";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import { close, error, loading } from "@/config/swal";
|
||||||
|
import { useParams } from "next/navigation";
|
||||||
|
|
||||||
|
const PasswordChecklist = dynamic(() => import("react-password-checklist"), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const sns = [
|
||||||
|
{
|
||||||
|
key: 1,
|
||||||
|
id: "comment",
|
||||||
|
typeId: 1,
|
||||||
|
name: "Komentar Konten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 2,
|
||||||
|
id: "fb",
|
||||||
|
typeId: 2,
|
||||||
|
name: "Facebook",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 3,
|
||||||
|
id: "ig",
|
||||||
|
typeId: 3,
|
||||||
|
name: "Instagram",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 4,
|
||||||
|
id: "twt",
|
||||||
|
typeId: 4,
|
||||||
|
name: "Twitter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 5,
|
||||||
|
id: "yt",
|
||||||
|
typeId: 5,
|
||||||
|
name: "Youtube",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 6,
|
||||||
|
id: "emergency",
|
||||||
|
typeId: 6,
|
||||||
|
name: "Emergency Issue",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 7,
|
||||||
|
id: "email",
|
||||||
|
typeId: 7,
|
||||||
|
name: "Email",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 8,
|
||||||
|
id: "inbox",
|
||||||
|
typeId: 8,
|
||||||
|
name: "Pesan Masuk",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 9,
|
||||||
|
id: "whatsapp",
|
||||||
|
typeId: 9,
|
||||||
|
name: "Whatssapp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 10,
|
||||||
|
id: "tiktok",
|
||||||
|
typeId: 10,
|
||||||
|
name: "Tiktok",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
interface RoleData {
|
||||||
|
id: number;
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
levelNumber: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
level: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
fullname: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
username: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
role: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
nrp: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
address: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
email: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
phoneNumber: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
password: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
confirmPassword: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
isValidPassword: z.boolean().refine((val) => val === true, {
|
||||||
|
message: "Check Password",
|
||||||
|
}),
|
||||||
|
sns: z.array(z.string()).optional(),
|
||||||
|
education: z.string().optional(),
|
||||||
|
school: z.string().optional(),
|
||||||
|
competency: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function EditUserForm() {
|
||||||
|
const router = useRouter();
|
||||||
|
const params = useParams();
|
||||||
|
const id = params?.id;
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const levelName = getCookiesDecrypt("ulnae");
|
||||||
|
const [roleList, setRoleList] = useState<RoleData[]>([]);
|
||||||
|
|
||||||
|
const [userEducations, setUserEducations] = useState<any>();
|
||||||
|
const [userSchools, setUserSchools] = useState<any>();
|
||||||
|
const [userCompetencies, setUserCompetencies] = useState<any>();
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: {
|
||||||
|
password: "",
|
||||||
|
confirmPassword: "",
|
||||||
|
sns: [],
|
||||||
|
education: "1",
|
||||||
|
school: "4",
|
||||||
|
competency: "2",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const passwordVal = form.watch("password");
|
||||||
|
const confPasswordVal = form.watch("confirmPassword");
|
||||||
|
const selectedRole = form.watch("role");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getDataAdditional();
|
||||||
|
initData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const initData = async () => {
|
||||||
|
loading();
|
||||||
|
const response = await getUserById(String(id));
|
||||||
|
const res = response?.data?.data;
|
||||||
|
close();
|
||||||
|
console.log("res", res);
|
||||||
|
if (Number(res.roleId) > 4) {
|
||||||
|
form.setValue("fullname", res?.fullname);
|
||||||
|
form.setValue("username", res?.username);
|
||||||
|
form.setValue("phoneNumber", res?.phoneNumber);
|
||||||
|
form.setValue("nrp", res?.memberIdentity);
|
||||||
|
form.setValue("address", res?.address);
|
||||||
|
form.setValue("email", res?.email);
|
||||||
|
form.setValue("role", res?.role?.code);
|
||||||
|
form.setValue("level", String(res?.userLevelId));
|
||||||
|
} else {
|
||||||
|
initFetch();
|
||||||
|
console.log("sadad", res?.role?.code);
|
||||||
|
form.setValue("fullname", res?.fullname);
|
||||||
|
form.setValue("username", res?.username);
|
||||||
|
form.setValue("phoneNumber", res?.phoneNumber);
|
||||||
|
form.setValue("nrp", res?.memberIdentity);
|
||||||
|
form.setValue("address", res?.address);
|
||||||
|
form.setValue("email", res?.email);
|
||||||
|
form.setValue("role", res?.role?.code);
|
||||||
|
form.setValue("level", String(res?.userLevelId));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const initFetch = async () => {
|
||||||
|
const response = await AdministrationLevelList();
|
||||||
|
const res = response?.data?.data;
|
||||||
|
var levelsArr: RoleData[] = [];
|
||||||
|
res.forEach((levels: RoleData) => {
|
||||||
|
levelsArr.push({
|
||||||
|
id: levels.id,
|
||||||
|
label: levels.name,
|
||||||
|
name: levels.name,
|
||||||
|
value: String(levels.id),
|
||||||
|
levelNumber: levels.levelNumber,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setRoleList(levelsArr);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getDataAdditional() {
|
||||||
|
const resEducations = await getListEducation();
|
||||||
|
setUserEducations(resEducations?.data?.data);
|
||||||
|
const resSchools = await getListSchools();
|
||||||
|
setUserSchools(resSchools?.data?.data);
|
||||||
|
const resCompetencies = await getListCompetencies();
|
||||||
|
setUserCompetencies(resCompetencies?.data?.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const roles =
|
||||||
|
levelName == "MABES POLRI"
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
id: "ADM-ID",
|
||||||
|
name: "Admin",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "APP-ID",
|
||||||
|
name: "Approver",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "CON-ID",
|
||||||
|
name: "Kontributor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "SPV-ID",
|
||||||
|
name: "Supervisor Feedback Center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "OPT-ID",
|
||||||
|
name: "Operator Feedback Center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "KKUR-ID",
|
||||||
|
name: "Koor Kurator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "KUR-ID",
|
||||||
|
name: "Kurator",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
id: "APP-ID",
|
||||||
|
name: "Approver",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "CON-ID",
|
||||||
|
name: "Kontributor",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function save(data: z.infer<typeof FormSchema>) {
|
||||||
|
let req: any = {
|
||||||
|
id: id,
|
||||||
|
firstName: data.fullname,
|
||||||
|
username: data.username,
|
||||||
|
roleId: data.role,
|
||||||
|
userLevelId: Number(data.level),
|
||||||
|
memberIdentity: data.nrp,
|
||||||
|
address: data.address,
|
||||||
|
email: data.email,
|
||||||
|
password: data.password,
|
||||||
|
passwordConf: data.confirmPassword,
|
||||||
|
isDefault: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.role == "OPT-ID") {
|
||||||
|
req.handledSocialMedia = data?.sns ? data.sns.join(",") : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.role == "KUR-ID") {
|
||||||
|
req.userEducationId = Number(data.education);
|
||||||
|
req.userSchoolsId = Number(data.school);
|
||||||
|
req.userCompetencyId = Number(data.competency);
|
||||||
|
}
|
||||||
|
|
||||||
|
loading();
|
||||||
|
const response = await saveUserInternal(req);
|
||||||
|
|
||||||
|
if (response?.error) {
|
||||||
|
error(response.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
close();
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Sukses",
|
||||||
|
icon: "success",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Oke",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
router.push("/admin/management-user");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Simpan Data?",
|
||||||
|
text: "",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Simpan",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
save(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="space-y-6 bg-white p-10 w-full"
|
||||||
|
>
|
||||||
|
<p className="text-xl">Data Pengelola Media Hub</p>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="level"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-col">
|
||||||
|
<FormLabel>Pilih Level</FormLabel>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<FormControl>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
className={cn(
|
||||||
|
"w-[400px] justify-between",
|
||||||
|
!field.value && "text-muted-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{field.value
|
||||||
|
? roleList.find((role) => role.value === field.value)
|
||||||
|
?.label
|
||||||
|
: "Pilih level"}
|
||||||
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</FormControl>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-[400px] p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No role found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{roleList.map((role) => (
|
||||||
|
<CommandItem
|
||||||
|
value={role.label}
|
||||||
|
key={role.value}
|
||||||
|
onSelect={() => {
|
||||||
|
form.setValue("level", role.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{role.label}
|
||||||
|
<Check
|
||||||
|
className={cn(
|
||||||
|
"ml-auto",
|
||||||
|
role.value === field.value
|
||||||
|
? "opacity-100"
|
||||||
|
: "opacity-0"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="fullname"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nama Lengkap</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="Masukkan nama lengkap"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="username"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Username</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="Masukkan username"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="role"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="space-y-3">
|
||||||
|
<FormLabel>Role</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
value={field.value}
|
||||||
|
className="flex flex-wrap gap-3 w-1/2"
|
||||||
|
>
|
||||||
|
{roles.map((role) => (
|
||||||
|
<FormItem
|
||||||
|
key={role.id}
|
||||||
|
className="flex items-center space-x-3 space-y-0"
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value={role.id} />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
{role.name}
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{selectedRole === "OPT-ID" && (
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="sns"
|
||||||
|
render={() => (
|
||||||
|
<FormItem>
|
||||||
|
<div className="mb-4">
|
||||||
|
<FormLabel>Social Media Yang Ditangani</FormLabel>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-5 gap-2 w-1/2">
|
||||||
|
{sns.map((item) => (
|
||||||
|
<FormField
|
||||||
|
key={item.id}
|
||||||
|
control={form.control}
|
||||||
|
name="sns"
|
||||||
|
render={({ field }) => {
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
key={item.typeId}
|
||||||
|
className="flex flex-row items-start space-x-3 space-y-0"
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
checked={field.value?.includes(
|
||||||
|
String(item.typeId)
|
||||||
|
)}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
return checked
|
||||||
|
? field.onChange([
|
||||||
|
...(field.value || []),
|
||||||
|
String(item.typeId),
|
||||||
|
])
|
||||||
|
: field.onChange(
|
||||||
|
(field.value || []).filter(
|
||||||
|
(value) =>
|
||||||
|
value !== String(item.typeId)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
{item.name}
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{selectedRole === "KUR-ID" && (
|
||||||
|
<>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="education"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Pendidikan Terakhir</FormLabel>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{userEducations?.map((edu: any) => (
|
||||||
|
<SelectItem key={edu.id} value={String(edu.id)}>
|
||||||
|
{edu.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="school"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Universitas / Perguruan Tinggi</FormLabel>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{userSchools?.map((edu: any) => (
|
||||||
|
<SelectItem key={edu.id} value={String(edu.id)}>
|
||||||
|
{edu.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="competency"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Kompetensi</FormLabel>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{userCompetencies?.map((edu: any) => (
|
||||||
|
<SelectItem key={edu.id} value={String(edu.id)}>
|
||||||
|
{edu.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="nrp"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nomor Regitrasi Polri {`(NRP)`}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="Masukkan NRP"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="address"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Alamat</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Masukkan alamat"
|
||||||
|
className="resize-none w-1/2"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="email"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Email</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="email"
|
||||||
|
placeholder="Masukkan email"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="phoneNumber"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>No. Handphone</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="Masukkan nomor handphone"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="password"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Password</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
placeholder="Masukkan kata sandi"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="confirmPassword"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Konfirmasi Kata Sandi</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
placeholder="Masukkan kata sandi"
|
||||||
|
{...field}
|
||||||
|
className="w-1/2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<PasswordChecklist
|
||||||
|
rules={["minLength", "specialChar", "number", "capital", "match"]}
|
||||||
|
minLength={8}
|
||||||
|
value={passwordVal}
|
||||||
|
valueAgain={confPasswordVal}
|
||||||
|
onChange={(isValid) => {
|
||||||
|
form.setValue("isValidPassword", isValid);
|
||||||
|
}}
|
||||||
|
className="text-sm"
|
||||||
|
messages={{
|
||||||
|
minLength: "Password harus lebih dari 8 karakter",
|
||||||
|
specialChar: "Password harus memiliki spesial karakter",
|
||||||
|
number: "Password harus memiliki angka",
|
||||||
|
capital: "Password harus memiliki huruf kapital",
|
||||||
|
match: "Password sama",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Link href="/admin/management-user">
|
||||||
|
<Button type="button" color="primary" variant="outline">
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Button type="submit" color="primary" className="mx-3">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,69 @@
|
||||||
|
"use client";
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import UserExternalTable from "@/components/table/management-user/management-user-external-table";
|
||||||
|
import UserInternalTable from "@/components/table/management-user/management-user-internal-table";
|
||||||
|
import InternalTable from "@/components/table/management-user/management-user-internal-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import DashboardVisualization from "@/components/visualization/dashboard-viz";
|
||||||
|
import ManagementUserVisualization from "@/components/visualization/management-user-viz";
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import { PlusIcon } from "lucide-react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function ManagementUser() {
|
export default function ManagementUser() {
|
||||||
|
const [isInternal, setIsInternal] = useState(true);
|
||||||
|
const router = useRouter();
|
||||||
|
useEffect(() => {
|
||||||
|
router.push("?page=1");
|
||||||
|
}, [isInternal]);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<p className="font-semibold">STATISTIK JUMLAH PENGGUNA</p>
|
<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 justify-between py-3">
|
||||||
|
<p className="text-lg">
|
||||||
|
Data User {isInternal ? "Internal" : "Eksternal"}
|
||||||
|
</p>
|
||||||
|
{isInternal && (
|
||||||
|
<Link href="/admin/management-user/internal/create">
|
||||||
|
<Button color="primary" size="md">
|
||||||
|
<PlusIcon />
|
||||||
|
Add User
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
|
||||||
|
<Button
|
||||||
|
rounded="md"
|
||||||
|
onClick={() => setIsInternal(true)}
|
||||||
|
className={` hover:text-white
|
||||||
|
${
|
||||||
|
!isInternal ? "bg-white text-black " : "bg-black text-white "
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
User Internal
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
rounded="md"
|
||||||
|
onClick={() => setIsInternal(false)}
|
||||||
|
className={`hover:text-white ${
|
||||||
|
!isInternal ? "bg-black text-white " : "bg-white text-black "
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
User Eksternal
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{isInternal ? <UserInternalTable /> : <UserExternalTable />}
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import ContentListTable from "./component/table";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
import BannerListTable from "./component/banner-table";
|
import BannerListTable from "./component/banner-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
export default function AdminBanner() {
|
export default function AdminBanner() {
|
||||||
const [selectedTab, setSelectedTab] = useState("content");
|
const [selectedTab, setSelectedTab] = useState("content");
|
||||||
|
|
@ -17,23 +18,31 @@ export default function AdminBanner() {
|
||||||
? "Daftar List Media"
|
? "Daftar List Media"
|
||||||
: "Table List Banner"}
|
: "Table List Banner"}
|
||||||
|
|
||||||
<div className="flex flex-row text-sm">
|
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
|
||||||
<a
|
<Button
|
||||||
|
rounded="md"
|
||||||
onClick={() => setSelectedTab("content")}
|
onClick={() => setSelectedTab("content")}
|
||||||
className={`px-3 py-2 cursor-pointer ${
|
className={` hover:text-white
|
||||||
selectedTab === "content" ? "bg-primary text-white" : ""
|
${
|
||||||
} `}
|
selectedTab === "content"
|
||||||
|
? "bg-black text-white "
|
||||||
|
: "bg-white text-black "
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
Kontent
|
Kontent
|
||||||
</a>
|
</Button>
|
||||||
<a
|
<Button
|
||||||
|
rounded="md"
|
||||||
onClick={() => setSelectedTab("banner")}
|
onClick={() => setSelectedTab("banner")}
|
||||||
className={`px-3 py-2 cursor-pointer ${
|
className={` hover:text-white
|
||||||
selectedTab === "banner" ? "bg-primary text-white" : ""
|
${
|
||||||
} `}
|
selectedTab === "banner"
|
||||||
|
? "bg-black text-white "
|
||||||
|
: "bg-white text-black "
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
Banner
|
Banner
|
||||||
</a>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,15 @@ const columns: ColumnDef<any>[] = [
|
||||||
</Button>
|
</Button>
|
||||||
</MenubarTrigger>
|
</MenubarTrigger>
|
||||||
<MenubarContent className="flex flex-col gap-2 justify-center items-start p-4">
|
<MenubarContent className="flex flex-col gap-2 justify-center items-start p-4">
|
||||||
<EditCategoryModal id={row.original.id} isDetail={true} />
|
<EditCategoryModal
|
||||||
<EditCategoryModal id={row.original.id} />
|
id={row.original.id}
|
||||||
|
isDetail={true}
|
||||||
|
thumbnailLink={row.original.thumbnailLink}
|
||||||
|
/>
|
||||||
|
<EditCategoryModal
|
||||||
|
id={row.original.id}
|
||||||
|
thumbnailLink={row.original.thumbnailLink}
|
||||||
|
/>
|
||||||
<a
|
<a
|
||||||
onClick={() => categoryDelete(row.original.id)}
|
onClick={() => categoryDelete(row.original.id)}
|
||||||
className="hover:underline cursor-pointer hover:text-destructive"
|
className="hover:underline cursor-pointer hover:text-destructive"
|
||||||
|
|
|
||||||
|
|
@ -105,15 +105,15 @@ const publishToList = [
|
||||||
|
|
||||||
export default function EditCategoryModal(props: {
|
export default function EditCategoryModal(props: {
|
||||||
id: string;
|
id: string;
|
||||||
|
thumbnailLink: string;
|
||||||
isDetail?: boolean;
|
isDetail?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { id, isDetail } = props;
|
const { id, isDetail, thumbnailLink } = props;
|
||||||
const [files, setFiles] = useState<File[]>([]);
|
const [files, setFiles] = useState<File[]>([]);
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [initDataUnit, setInitDataUnit] = useState<string[]>([]);
|
|
||||||
const [satkerData, setSatkerData] = useState<string[]>([]);
|
const [satkerData, setSatkerData] = useState<string[]>([]);
|
||||||
const [unitData, setUnitData] = useState<string[]>([]);
|
const [unitData, setUnitData] = useState<string[]>([]);
|
||||||
const [userList, setUserList] = useState<
|
const [userList, setUserList] = useState<
|
||||||
|
|
@ -146,6 +146,7 @@ export default function EditCategoryModal(props: {
|
||||||
removeAndReturn(data?.publishedFor, [2, 3, 4])
|
removeAndReturn(data?.publishedFor, [2, 3, 4])
|
||||||
);
|
);
|
||||||
form.setValue("publishTo", data?.publishedLocation?.split(","));
|
form.setValue("publishTo", data?.publishedLocation?.split(","));
|
||||||
|
form.setValue("file", thumbnailLink);
|
||||||
|
|
||||||
setUnitData(filterString(data?.publishedLocationLevel, "under"));
|
setUnitData(filterString(data?.publishedLocationLevel, "under"));
|
||||||
setSatkerData(filterString(data?.publishedLocationLevel, "above"));
|
setSatkerData(filterString(data?.publishedLocationLevel, "above"));
|
||||||
|
|
@ -163,15 +164,15 @@ export default function EditCategoryModal(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterString(inputString: string, type: string) {
|
function filterString(inputString: string, type: string) {
|
||||||
const numbers = inputString.split(",").map(Number);
|
const numbers = inputString?.split(",").map(Number);
|
||||||
if (type === "above") {
|
if (type === "above") {
|
||||||
const above700 = numbers.filter((num) => num > 700);
|
const above700 = numbers?.filter((num) => num > 700);
|
||||||
|
|
||||||
return above700.map(String);
|
return above700?.map(String);
|
||||||
} else {
|
} else {
|
||||||
const under700 = numbers.filter((num) => num < 700);
|
const under700 = numbers?.filter((num) => num < 700);
|
||||||
|
|
||||||
return under700.map(String);
|
return under700?.map(String);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -226,9 +227,11 @@ export default function EditCategoryModal(props: {
|
||||||
formMedia.append("description", data.description);
|
formMedia.append("description", data.description);
|
||||||
formMedia.append("mediaTypes", data.contentType.join(","));
|
formMedia.append("mediaTypes", data.contentType.join(","));
|
||||||
formMedia.append("publishedFor", data.selectedUser.join(","));
|
formMedia.append("publishedFor", data.selectedUser.join(","));
|
||||||
formMedia.append("file", files[0]);
|
|
||||||
formMedia.append("publishedLocation", data.publishTo.sort().join(","));
|
formMedia.append("publishedLocation", data.publishTo.sort().join(","));
|
||||||
formMedia.append("publishedLocationLevel", removeDuplicates(join));
|
formMedia.append("publishedLocationLevel", removeDuplicates(join));
|
||||||
|
if (files?.length > 0) {
|
||||||
|
formMedia.append("file", files[0]);
|
||||||
|
}
|
||||||
|
|
||||||
const response = await postCategory(formMedia);
|
const response = await postCategory(formMedia);
|
||||||
close();
|
close();
|
||||||
|
|
@ -253,7 +256,10 @@ export default function EditCategoryModal(props: {
|
||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<DialogTrigger>
|
<DialogTrigger>
|
||||||
<a onClick={() => setIsOpen(true)} className="hover:underline">
|
<a
|
||||||
|
onClick={() => setIsOpen(true)}
|
||||||
|
className="hover:underline cursor-pointer"
|
||||||
|
>
|
||||||
{isDetail ? "Detail" : "Edit"}
|
{isDetail ? "Detail" : "Edit"}
|
||||||
</a>
|
</a>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
@ -520,50 +526,71 @@ export default function EditCategoryModal(props: {
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{/* <FormField
|
{isDetail ? (
|
||||||
control={form.control}
|
<div className="flex flex-row gap-2">
|
||||||
name="file"
|
<img src={thumbnailLink} className="w-[30%]" alt="thumbnail" />
|
||||||
render={({ field }) => (
|
</div>
|
||||||
<FormItem>
|
) : (
|
||||||
<FormLabel>Thumbnail Category</FormLabel>
|
<FormField
|
||||||
{files.length < 1 && (
|
control={form.control}
|
||||||
<Fragment>
|
name="file"
|
||||||
<div {...getRootProps({ className: "dropzone" })}>
|
render={({ field }) => (
|
||||||
<input {...getInputProps()} />
|
<FormItem>
|
||||||
<div className=" w-full text-center border-dashed border border-default-200 dark:border-default-300 rounded-md py-[52px] flex items-center flex-col">
|
<FormLabel>Thumbnail Category</FormLabel>
|
||||||
<CloudUpload className="text-default-300 w-10 h-10" />
|
{files.length < 1 && field.value === "" && (
|
||||||
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
<Fragment>
|
||||||
Tarik file disini atau klik untuk upload.
|
<div {...getRootProps({ className: "dropzone" })}>
|
||||||
</h4>
|
<input {...getInputProps()} />
|
||||||
<div className=" text-xs text-muted-foreground">
|
<div className=" w-full text-center border-dashed border border-default-200 dark:border-default-300 rounded-md py-[52px] flex items-center flex-col">
|
||||||
( Upload file dengan format .jpg, .jpeg, atau .png.
|
<CloudUpload className="text-default-300 w-10 h-10" />
|
||||||
Ukuran maksimal 100mb.)
|
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
||||||
|
Tarik file disini atau klik untuk upload.
|
||||||
|
</h4>
|
||||||
|
<div className=" text-xs text-muted-foreground">
|
||||||
|
( Upload file dengan format .jpg, .jpeg, atau
|
||||||
|
.png. Ukuran maksimal 100mb.)
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
{field.value !== "" && (
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<img
|
||||||
|
src={field.value}
|
||||||
|
className="w-[30%]"
|
||||||
|
alt="thumbnail"
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
onClick={() => form.setValue("file", "")}
|
||||||
|
className="cursor-pointer"
|
||||||
|
>
|
||||||
|
<Icon icon="fa-solid:times" color="red" />
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
)}
|
||||||
)}
|
|
||||||
|
|
||||||
{files.length > 0 && (
|
{files.length > 0 && (
|
||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row gap-2">
|
||||||
<img
|
<img
|
||||||
src={URL.createObjectURL(files[0])}
|
src={URL.createObjectURL(files[0])}
|
||||||
className="w-[30%]"
|
className="w-[30%]"
|
||||||
alt="thumbnail"
|
alt="thumbnail"
|
||||||
/>
|
/>
|
||||||
<a
|
<a
|
||||||
onClick={() => handleRemoveFile(files[0])}
|
onClick={() => handleRemoveFile(files[0])}
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
>
|
>
|
||||||
<Icon icon="fa-solid:times" color="red" />
|
<Icon icon="fa-solid:times" color="red" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/> */}
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
|
@ -584,16 +611,18 @@ export default function EditCategoryModal(props: {
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<DialogFooter>
|
{!isDetail && (
|
||||||
<Button
|
<DialogFooter>
|
||||||
type="submit"
|
<Button
|
||||||
color="primary"
|
type="submit"
|
||||||
size="md"
|
color="primary"
|
||||||
className="text-xs"
|
size="md"
|
||||||
>
|
className="text-xs"
|
||||||
Edit Kategori
|
>
|
||||||
</Button>
|
Edit Kategori
|
||||||
</DialogFooter>
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import AdminFAQTable from "./component/table";
|
||||||
|
|
||||||
|
export default function FAQSetting() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<AdminFAQTable />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import AdminFeedbackTable from "./component/table";
|
||||||
|
|
||||||
|
export default function FAQSetting() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<AdminFeedbackTable />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,14 @@ import { useEffect, useRef } from "react";
|
||||||
import { getPrivacy, savePrivacy } from "@/service/settings/settings";
|
import { getPrivacy, savePrivacy } from "@/service/settings/settings";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
|
const CustomEditor = dynamic(
|
||||||
|
() => {
|
||||||
|
return import("@/components/editor/custom-editor");
|
||||||
|
},
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
const FormSchema = z.object({
|
const FormSchema = z.object({
|
||||||
title: z.string({
|
title: z.string({
|
||||||
|
|
@ -96,7 +104,7 @@ export default function AdminPrivacyPolicy() {
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Konten</FormLabel>
|
<FormLabel>Konten</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<JoditEditor
|
{/* <JoditEditor
|
||||||
ref={editor}
|
ref={editor}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
config={{
|
config={{
|
||||||
|
|
@ -104,6 +112,11 @@ export default function AdminPrivacyPolicy() {
|
||||||
}}
|
}}
|
||||||
className="dark:text-black"
|
className="dark:text-black"
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
|
|
||||||
|
/> */}
|
||||||
|
<CustomEditor
|
||||||
|
onChange={field.onChange}
|
||||||
|
initialData={field.value}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import AdminTagTable from "./component/table";
|
||||||
|
|
||||||
export default function TagCategory() {
|
export default function TagCategory() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
|
<AdminTagTable />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
@ -75,24 +77,15 @@ const EscalationTable = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("10");
|
||||||
|
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
const [limit, setLimit] = React.useState(10);
|
|
||||||
const [search, setSearch] = React.useState<string>("");
|
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 roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
|
@ -126,19 +119,35 @@ const EscalationTable = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
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() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const res = await getTicketingCollaborationPagination(
|
const res = await getTicketingCollaborationPagination(
|
||||||
page - 1,
|
page - 1,
|
||||||
limit,
|
showData,
|
||||||
search
|
search
|
||||||
);
|
);
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
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);
|
console.log("contentData : ", contentData);
|
||||||
setDataTable(contentData);
|
setDataTable(contentData);
|
||||||
|
|
@ -148,16 +157,10 @@ const EscalationTable = () => {
|
||||||
console.error("Error fetching tasks:", error);
|
console.error("Error fetching tasks:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setSearch(e.target.value); // Perbarui state search
|
|
||||||
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto">
|
<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">
|
<div className="mt-3 flex flex-row items-center gap-2">
|
||||||
<InputGroup merged>
|
<InputGroup merged>
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
<Search className=" h-4 w-4 dark:text-white" />
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
|
|
@ -167,23 +170,40 @@ const EscalationTable = () => {
|
||||||
placeholder="Search Judul..."
|
placeholder="Search Judul..."
|
||||||
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
||||||
value={search}
|
value={search}
|
||||||
onChange={handleSearch}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-none">
|
<DropdownMenu>
|
||||||
<Input
|
<DropdownMenuTrigger asChild>
|
||||||
placeholder="Filter Status..."
|
<Button size="md" variant="outline">
|
||||||
value={
|
1 - {showData} Data
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
</Button>
|
||||||
}
|
</DropdownMenuTrigger>
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
<DropdownMenuRadioGroup
|
||||||
}
|
value={showData}
|
||||||
className="max-w-sm "
|
onValueChange={setShowData}
|
||||||
/>
|
>
|
||||||
</div>
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="20">
|
||||||
|
1 - 20 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="25">
|
||||||
|
1 - 25 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
|
|
||||||
|
|
@ -12,58 +12,39 @@ import {
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: "no",
|
accessorKey: "no",
|
||||||
header: "No",
|
header: "No",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => <span> {row.getValue("no")}</span>,
|
||||||
<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>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "title",
|
accessorKey: "title",
|
||||||
header: "Question",
|
header: "Pertanyaan",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex items-center gap-5">
|
<span className="normal-case"> {row.getValue("title")}</span>
|
||||||
<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>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "commentFromUserName",
|
accessorKey: "commentFromUserName",
|
||||||
header: "CreateBy",
|
header: "CreateBy",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex items-center gap-5">
|
<span className="normal-case">{row.getValue("commentFromUserName")}</span>
|
||||||
<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>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "Type",
|
accessorKey: "Type",
|
||||||
header: "SendTo",
|
header: "Channel",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const type = row.original.type; // Akses properti category
|
const type = row.original.type;
|
||||||
return <span className="whitespace-nowrap">{type?.name || "N/A"}</span>;
|
return <span className="normal-case">{type?.name || "N/A"}</span>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "createdAt",
|
accessorKey: "createdAt",
|
||||||
header: "Upload Date",
|
header: "Waktu",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const createdAt = row.getValue("createdAt") as
|
const createdAt = row.getValue("createdAt") as
|
||||||
| string
|
| string
|
||||||
|
|
@ -99,6 +80,7 @@ const columns: ColumnDef<any>[] = [
|
||||||
header: "Actions",
|
header: "Actions",
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
|
const router = useRouter();
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
|
|
@ -111,17 +93,16 @@ const columns: ColumnDef<any>[] = [
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
<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" />
|
<Eye className="w-4 h-4 me-1.5" />
|
||||||
View
|
Detail
|
||||||
</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
|
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -18,53 +18,33 @@ const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: "no",
|
accessorKey: "no",
|
||||||
header: "No",
|
header: "No",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => <span> {row.getValue("no")}</span>,
|
||||||
<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>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "title",
|
accessorKey: "title",
|
||||||
header: "Question",
|
header: "Pertanyaan",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex items-center gap-5">
|
<span className="normal-case"> {row.getValue("title")}</span>
|
||||||
<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>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "commentFromUserName",
|
accessorKey: "commentFromUserName",
|
||||||
header: "CreateBY",
|
header: "Penerima",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex items-center gap-5">
|
<span className="normal-case">{row.getValue("commentFromUserName")}</span>
|
||||||
<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>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "type",
|
accessorKey: "type",
|
||||||
header: "SendTo",
|
header: "Penerima",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const type = row.original.type; // Akses properti category
|
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",
|
accessorKey: "createdAt",
|
||||||
header: "Upload Date",
|
header: "Waktu",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const createdAt = row.getValue("createdAt") as
|
const createdAt = row.getValue("createdAt") as
|
||||||
| string
|
| string
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
@ -74,24 +76,15 @@ const EscalationTable = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("10");
|
||||||
|
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
const [limit, setLimit] = React.useState(10);
|
|
||||||
const [search, setSearch] = React.useState<string>("");
|
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 roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
|
@ -125,19 +118,39 @@ const EscalationTable = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
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() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const res = await getTicketingEscalationPagination(
|
const res = await getTicketingEscalationPagination(
|
||||||
page - 1,
|
page - 1,
|
||||||
limit,
|
Number(showData),
|
||||||
search
|
search
|
||||||
);
|
);
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
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);
|
console.log("contentData : ", contentData);
|
||||||
setDataTable(contentData);
|
setDataTable(contentData);
|
||||||
|
|
@ -155,8 +168,8 @@ const EscalationTable = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto">
|
<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">
|
<div className="mt-3 flex flex-row items-center gap-2">
|
||||||
<InputGroup merged>
|
<InputGroup merged>
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
<Search className=" h-4 w-4 dark:text-white" />
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
|
|
@ -166,22 +179,38 @@ const EscalationTable = () => {
|
||||||
placeholder="Search Judul..."
|
placeholder="Search Judul..."
|
||||||
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
||||||
value={search}
|
value={search}
|
||||||
onChange={handleSearch}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-none">
|
<DropdownMenu>
|
||||||
<Input
|
<DropdownMenuTrigger asChild>
|
||||||
placeholder="Filter Status..."
|
<Button size="md" variant="outline">
|
||||||
value={
|
1 - {showData} Data
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
</Button>
|
||||||
}
|
</DropdownMenuTrigger>
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
<DropdownMenuRadioGroup
|
||||||
}
|
value={showData}
|
||||||
className="max-w-sm "
|
onValueChange={setShowData}
|
||||||
/>
|
>
|
||||||
</div>
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="20">
|
||||||
|
1 - 20 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="25">
|
||||||
|
1 - 25 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
|
|
|
||||||
|
|
@ -18,54 +18,36 @@ const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: "no",
|
accessorKey: "no",
|
||||||
header: "No",
|
header: "No",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => <span> {row.getValue("no")}</span>,
|
||||||
<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>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "title",
|
accessorKey: "title",
|
||||||
header: "Question",
|
header: "Pertanyaan",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex items-center gap-5">
|
<span className="normal-case"> {row.getValue("title")}</span>
|
||||||
<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>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "createdBy",
|
accessorKey: "createdBy",
|
||||||
header: "CreateBy",
|
header: "Pengirim",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const createdBy = row.original.createdBy; // Akses properti category
|
const createdBy = row.original.createdBy; // Akses properti category
|
||||||
return (
|
return (
|
||||||
<span className="whitespace-nowrap">
|
<span className="normal-case">{createdBy?.fullname || "N/A"}</span>
|
||||||
{createdBy?.fullname || "N/A"}
|
|
||||||
</span>
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "sendTo",
|
accessorKey: "sendTo",
|
||||||
header: "SendTo",
|
header: "Penerima",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const sendTo = row.original.sendTo; // Akses properti category
|
const sendTo = row.original.sendTo; // Akses properti category
|
||||||
return (
|
return <span className="normal-case">{sendTo?.fullname || "N/A"}</span>;
|
||||||
<span className="whitespace-nowrap">{sendTo?.fullname || "N/A"}</span>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "createdAt",
|
accessorKey: "createdAt",
|
||||||
header: "Upload Date",
|
header: "Waktu",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const createdAt = row.getValue("createdAt") as
|
const createdAt = row.getValue("createdAt") as
|
||||||
| string
|
| string
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
@ -73,25 +75,18 @@ const TableAudio = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("10");
|
||||||
|
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
const [limit, setLimit] = React.useState(10);
|
|
||||||
const [search, setSearch] = React.useState<string>("");
|
const [search, setSearch] = React.useState<string>("");
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
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 roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
|
|
@ -124,15 +119,39 @@ const TableAudio = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
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() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const res = await listTicketingInternal(page - 1, limit, search);
|
const res = await listTicketingInternal(
|
||||||
|
page - 1,
|
||||||
|
Number(showData),
|
||||||
|
search
|
||||||
|
);
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
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);
|
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 (
|
return (
|
||||||
<div className="w-full overflow-x-auto ">
|
<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">
|
<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>
|
<InputGroup merged>
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
<Search className=" h-4 w-4 dark:text-white" />
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
|
|
@ -169,22 +177,38 @@ const TableAudio = () => {
|
||||||
placeholder="Search Judul..."
|
placeholder="Search Judul..."
|
||||||
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
||||||
value={search}
|
value={search}
|
||||||
onChange={handleSearch}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-none">
|
<DropdownMenu>
|
||||||
<Input
|
<DropdownMenuTrigger asChild>
|
||||||
placeholder="Filter Status..."
|
<Button size="md" variant="outline">
|
||||||
value={
|
1 - {showData} Data
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
</Button>
|
||||||
}
|
</DropdownMenuTrigger>
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
<DropdownMenuRadioGroup
|
||||||
}
|
value={showData}
|
||||||
className="max-w-sm "
|
onValueChange={setShowData}
|
||||||
/>
|
>
|
||||||
</div>
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="20">
|
||||||
|
1 - 20 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="25">
|
||||||
|
1 - 25 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
"use client";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import InternalTable from "./internal/components/internal-table";
|
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 CollaborationTable from "./collaboration/components/collabroation-table";
|
||||||
import EscalationTable from "./escalation/components/escalation-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 CommunicationPage = () => {
|
||||||
|
const [tab, setTab] = useState("Komunikasi");
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="my-3">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<Card className="py-3 px-2 my-4">
|
<div className="flex justify-between py-3">
|
||||||
<Tabs defaultValue="internal" className="w-full">
|
<p className="text-lg">{tab}</p>
|
||||||
<p className="text-lg font-semibold ml-2">Komunikasi</p>
|
{tab === "Komunikasi" && (
|
||||||
<TabsList className="flex-wrap">
|
<Link href="/shared/communication/internal/create">
|
||||||
<TabsTrigger
|
<Button color="primary" size="md">
|
||||||
value="internal"
|
<PlusIcon />
|
||||||
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
|
Pertanyaan baru
|
||||||
>
|
</Button>
|
||||||
Pertanyaan Internal
|
</Link>
|
||||||
</TabsTrigger>
|
)}
|
||||||
<TabsTrigger
|
{tab === "Kolaborasi" && (
|
||||||
value="escalation"
|
<Link href="/shared/communication/collaboration/create">
|
||||||
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
|
<Button color="primary" size="md">
|
||||||
>
|
<PlusIcon />
|
||||||
Eskalasi
|
Kolaborasi baru
|
||||||
</TabsTrigger>
|
</Button>
|
||||||
<TabsTrigger
|
</Link>
|
||||||
value="collaboration"
|
)}
|
||||||
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
|
</div>
|
||||||
>
|
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
|
||||||
Kolaborasi
|
<Button
|
||||||
</TabsTrigger>
|
rounded="md"
|
||||||
</TabsList>
|
onClick={() => setTab("Komunikasi")}
|
||||||
|
className={` hover:text-white
|
||||||
<TabsContent value="internal">
|
${
|
||||||
<div className="grid grid-cols-12 gap-5">
|
tab === "Komunikasi"
|
||||||
<div className="lg:col-span-12 col-span-12">
|
? "bg-black text-white "
|
||||||
<Card>
|
: "bg-white text-black "
|
||||||
<CardContent className="p-0">
|
}`}
|
||||||
<InternalTable />
|
>
|
||||||
</CardContent>
|
Komunikasi
|
||||||
</Card>
|
</Button>
|
||||||
</div>
|
<Button
|
||||||
</div>
|
rounded="md"
|
||||||
</TabsContent>
|
onClick={() => setTab("Eskalasi")}
|
||||||
<TabsContent value="escalation">
|
className={` hover:text-white
|
||||||
<div className="grid grid-cols-12 gap-5">
|
${
|
||||||
<div className="lg:col-span-12 col-span-12">
|
tab === "Eskalasi"
|
||||||
<Card>
|
? "bg-black text-white "
|
||||||
<CardContent className="p-0">
|
: "bg-white text-black "
|
||||||
<EscalationTable />
|
}`}
|
||||||
</CardContent>
|
>
|
||||||
</Card>
|
Eskalasi
|
||||||
</div>
|
</Button>
|
||||||
</div>
|
<Button
|
||||||
</TabsContent>
|
rounded="md"
|
||||||
<TabsContent value="collaboration">
|
onClick={() => setTab("Kolaborasi")}
|
||||||
<div className="grid grid-cols-12 gap-5">
|
className={` hover:text-white
|
||||||
<div className="lg:col-span-12 col-span-12">
|
${
|
||||||
<Card>
|
tab === "Kolaborasi"
|
||||||
<CardContent className="p-0">
|
? "bg-black text-white "
|
||||||
<CollaborationTable />
|
: "bg-white text-black "
|
||||||
</CardContent>
|
}`}
|
||||||
</Card>
|
>
|
||||||
</div>
|
Kolaborasi
|
||||||
</div>
|
</Button>
|
||||||
</TabsContent>
|
</div>
|
||||||
</Tabs>
|
{tab === "Komunikasi" && <InternalTable />}
|
||||||
</Card>
|
{tab === "Eskalasi" && <EscalationTable />}
|
||||||
|
{tab === "Kolaborasi" && <CollaborationTable />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { formatDateToIndonesian } from "@/utils/globals";
|
||||||
|
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",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
accessorKey: "fullname",
|
||||||
|
header: "Nama",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("fullname")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "username",
|
||||||
|
header: "Username",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">
|
||||||
|
{row.original?.userKeycloak?.username || ""}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "phoneNumber",
|
||||||
|
header: "No. HP",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("phoneNumber")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "email",
|
||||||
|
header: "Email",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("email")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "level",
|
||||||
|
header: "Level Pengguna",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.original?.role?.name || ""}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "createdAt",
|
||||||
|
header: "Tanggal Unggah",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span>{formatDateToIndonesian(row.getValue("createdAt"))}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
accessorKey: "isActive",
|
||||||
|
header: "Status",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
row.getValue("isActive") ? "text-success" : "text-destructive"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{row.getValue("isActive") ? "Aktif" : "Belum Aktif"}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: "actions",
|
||||||
|
accessorKey: "action",
|
||||||
|
header: "Actions",
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const { toast } = useToast();
|
||||||
|
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 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 (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||||
|
>
|
||||||
|
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<a>Aktivasi</a>
|
||||||
|
</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}`}
|
||||||
|
>
|
||||||
|
Detail
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<Link
|
||||||
|
href={`/admin/management-user/external/edit/${row.original.id}`}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
color="red"
|
||||||
|
className="p-2 border-b text-red-500 group focus:bg-red-500 focus:text-white rounded-none"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
onClick={() => {
|
||||||
|
handleDelete(row.original.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Hapus
|
||||||
|
</a>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,390 @@
|
||||||
|
"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 { PlusIcon } from "lucide-react";
|
||||||
|
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import columns from "./management-user-external-column-table";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { close, loading } from "@/config/swal";
|
||||||
|
import { useRouter, Link } from "@/i18n/routing";
|
||||||
|
import { AdministrationUserList } from "@/service/management-user/management-user";
|
||||||
|
|
||||||
|
const UserExternalTable = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
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[]>([]);
|
||||||
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
const [search, setSearch] = React.useState("");
|
||||||
|
const [categoryFilter, setCategoryFilter] = React.useState<number[]>([]);
|
||||||
|
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let typingTimer: any;
|
||||||
|
const doneTypingInterval = 1500;
|
||||||
|
|
||||||
|
const handleKeyUp = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function doneTyping() {
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
router.push("/admin/management-user");
|
||||||
|
}, [dataChange]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get("page");
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
setPagination({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
}, [page, showData]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
loading();
|
||||||
|
const res = await AdministrationUserList(
|
||||||
|
String(levelId),
|
||||||
|
page - 1,
|
||||||
|
search,
|
||||||
|
showData,
|
||||||
|
"2",
|
||||||
|
statusFilter?.sort().join(",")
|
||||||
|
);
|
||||||
|
const data = res?.data?.data;
|
||||||
|
const contentData = data?.content;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(data?.totalElements);
|
||||||
|
setTotalPage(data?.totalPages);
|
||||||
|
close();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChange = (type: string, id: number, checked: boolean) => {
|
||||||
|
if (type === "category") {
|
||||||
|
if (checked) {
|
||||||
|
const temp: number[] = [...categoryFilter];
|
||||||
|
temp.push(id);
|
||||||
|
setCategoryFilter(temp);
|
||||||
|
} else {
|
||||||
|
const temp = categoryFilter.filter((a) => a !== id);
|
||||||
|
setCategoryFilter(temp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (checked) {
|
||||||
|
const temp: number[] = [...statusFilter];
|
||||||
|
temp.push(id);
|
||||||
|
setStatusFilter(temp);
|
||||||
|
} else {
|
||||||
|
const temp = statusFilter.filter((a) => a !== id);
|
||||||
|
setStatusFilter(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex justify-between py-3">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search"
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
className="max-w-[300px]"
|
||||||
|
/>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
1 - {showData} Data
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
|
<DropdownMenuRadioGroup
|
||||||
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="20">
|
||||||
|
1 - 20 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="25">
|
||||||
|
1 - 25 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
Filter
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-80 ">
|
||||||
|
<div className="flex flex-col gap-2 px-2">
|
||||||
|
<div className="flex justify-between text-sm">
|
||||||
|
<p>Filter</p>
|
||||||
|
<a
|
||||||
|
onClick={() => fetchData()}
|
||||||
|
className="cursor-pointer text-primary"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1 overflow-auto max-h-[300px] text-xs custom-scrollbar-table">
|
||||||
|
<p>Admin</p>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(3)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 3, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Approver
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(4)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 4, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Kontributor
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p className="mt-3">Kurator</p>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(11)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 11, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Koordinator
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(12)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 12, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Kurator
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p className="mt-3">Feedback Center</p>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(9)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 9, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Supervisor
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(10)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 10, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Operator
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table className="overflow-hidden">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserExternalTable;
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
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 {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { formatDateToIndonesian } from "@/utils/globals";
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
|
||||||
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
import { deleteUser } from "@/service/management-user/management-user";
|
||||||
|
import { stringify } from "querystring";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
accessorKey: "fullname",
|
||||||
|
header: "Nama",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("fullname")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "username",
|
||||||
|
header: "Username",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">
|
||||||
|
{row.original?.userKeycloak?.username || ""}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "phoneNumber",
|
||||||
|
header: "No. HP",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("phoneNumber")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "email",
|
||||||
|
header: "Email",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("email")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "level",
|
||||||
|
header: "Level Pengguna",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.original.userLevel.name}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "createdAt",
|
||||||
|
header: "Tanggal Unggah",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span>{formatDateToIndonesian(row.getValue("createdAt"))}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
accessorKey: "isActive",
|
||||||
|
header: "Status",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
row.getValue("isActive") ? "text-success" : "text-destructive"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{row.getValue("isActive") ? "Aktif" : "Belum Aktif"}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: "actions",
|
||||||
|
accessorKey: "action",
|
||||||
|
header: "Actions",
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const { toast } = useToast();
|
||||||
|
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 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 (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||||
|
>
|
||||||
|
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<Link
|
||||||
|
href={`/admin/management-user/internal/detail/${row.original.id}`}
|
||||||
|
>
|
||||||
|
Detail
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<Link
|
||||||
|
href={`/admin/management-user/internal/edit/${row.original.id}`}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
color="red"
|
||||||
|
className="p-2 border-b text-red-500 group focus:bg-red-500 focus:text-white rounded-none"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
onClick={() => {
|
||||||
|
handleDelete(row.original.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Hapus
|
||||||
|
</a>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,410 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
PlusIcon,
|
||||||
|
Search,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
TrendingDown,
|
||||||
|
TrendingUp,
|
||||||
|
UserIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { paginationBlog } from "@/service/blog/blog";
|
||||||
|
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { 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";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
import { listDataMedia } from "@/service/broadcast/broadcast";
|
||||||
|
import { listEnableCategory } from "@/service/content/content";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { close, loading } from "@/config/swal";
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import { AdministrationUserList } from "@/service/management-user/management-user";
|
||||||
|
|
||||||
|
const UserInternalTable = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
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[]>([]);
|
||||||
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
const [search, setSearch] = React.useState("");
|
||||||
|
|
||||||
|
const [categoryFilter, setCategoryFilter] = React.useState<number[]>([]);
|
||||||
|
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let typingTimer: any;
|
||||||
|
const doneTypingInterval = 1500;
|
||||||
|
|
||||||
|
const handleKeyUp = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function doneTyping() {
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
router.push("/admin/management-user");
|
||||||
|
}, [dataChange]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get("page");
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
setPagination({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
}, [page, showData]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
loading();
|
||||||
|
const res = await AdministrationUserList(
|
||||||
|
String(levelId),
|
||||||
|
page - 1,
|
||||||
|
search,
|
||||||
|
showData,
|
||||||
|
"1",
|
||||||
|
statusFilter?.sort().join(",")
|
||||||
|
);
|
||||||
|
const data = res?.data?.data;
|
||||||
|
const contentData = data?.content;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(data?.totalElements);
|
||||||
|
setTotalPage(data?.totalPages);
|
||||||
|
close();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChange = (type: string, id: number, checked: boolean) => {
|
||||||
|
if (type === "category") {
|
||||||
|
if (checked) {
|
||||||
|
const temp: number[] = [...categoryFilter];
|
||||||
|
temp.push(id);
|
||||||
|
setCategoryFilter(temp);
|
||||||
|
} else {
|
||||||
|
const temp = categoryFilter.filter((a) => a !== id);
|
||||||
|
setCategoryFilter(temp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (checked) {
|
||||||
|
const temp: number[] = [...statusFilter];
|
||||||
|
temp.push(id);
|
||||||
|
setStatusFilter(temp);
|
||||||
|
} else {
|
||||||
|
const temp = statusFilter.filter((a) => a !== id);
|
||||||
|
setStatusFilter(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex justify-between py-3">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search"
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
className="max-w-[300px]"
|
||||||
|
/>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
1 - {showData} Data
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
|
<DropdownMenuRadioGroup
|
||||||
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="20">
|
||||||
|
1 - 20 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="25">
|
||||||
|
1 - 25 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
Filter
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-80 ">
|
||||||
|
<div className="flex flex-col gap-2 px-2">
|
||||||
|
<div className="flex justify-between text-sm">
|
||||||
|
<p>Filter</p>
|
||||||
|
<a
|
||||||
|
onClick={() => fetchData()}
|
||||||
|
className="cursor-pointer text-primary"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1 overflow-auto max-h-[300px] text-xs custom-scrollbar-table">
|
||||||
|
<p>Admin</p>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(3)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 3, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Approver
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(4)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 4, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Kontributor
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p className="mt-3">Kurator</p>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(11)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 11, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Koordinator
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(12)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 12, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Kurator
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p className="mt-3">Feedback Center</p>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(9)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 9, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Supervisor
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={statusFilter.includes(10)}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleChange("status", 10, Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Operator
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table className="overflow-hidden">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserInternalTable;
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
"use client";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import { generateTicket } from "@/service/tableau/tableau-service";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
|
export default function ManagementUserVisualization() {
|
||||||
|
const [ticket, setTicket] = useState("");
|
||||||
|
const baseUrl = "https://db-mediahub.polri.go.id/";
|
||||||
|
const url = "https://db-mediahub.polri.go.id/trusted/";
|
||||||
|
const view = "views/2022_05_MediaHUB-Viz_Rev110/db-user-count?:iid=5&";
|
||||||
|
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
|
||||||
|
const [isInternational, setIsInternational] = useState(false);
|
||||||
|
const t = useTranslations("AnalyticsDashboard");
|
||||||
|
|
||||||
|
const userId = getCookiesDecrypt("uie");
|
||||||
|
useEffect(() => {
|
||||||
|
const initState = async () => {
|
||||||
|
const response = await generateTicket();
|
||||||
|
console.log("Data :", response?.data?.data);
|
||||||
|
setTicket(response?.data?.data);
|
||||||
|
console.log(userId);
|
||||||
|
};
|
||||||
|
|
||||||
|
initState();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-2 bg-white rounded-lg p-3">
|
||||||
|
{isInternational ? (
|
||||||
|
<p className="font-semibold">STATISTICS TO THE NUMBER OF USERS</p>
|
||||||
|
) : (
|
||||||
|
<p className="font-semibold">STATISTIK JUMLAH PENGGUNA</p>
|
||||||
|
)}
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<p>{t("choose_category")}</p>
|
||||||
|
<div className="flex flex-row gap-1 border-2 w-fit">
|
||||||
|
<Button
|
||||||
|
onClick={() => setIsInternational(false)}
|
||||||
|
className={` hover:text-white rounded-none
|
||||||
|
${
|
||||||
|
isInternational
|
||||||
|
? "bg-white text-black "
|
||||||
|
: "bg-black text-white "
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Indonesia
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => setIsInternational(true)}
|
||||||
|
className={`hover:text-white rounded-none ${
|
||||||
|
isInternational ? "bg-black text-white " : "bg-white text-black "
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{t("international")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{ticket == undefined ? (
|
||||||
|
<iframe
|
||||||
|
src={`${baseUrl + view + param}`}
|
||||||
|
width="100%"
|
||||||
|
height="750"
|
||||||
|
frameBorder="0"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<iframe
|
||||||
|
src={`${`${url + ticket}/${view}${param}`}`}
|
||||||
|
width="100%"
|
||||||
|
height="750"
|
||||||
|
frameBorder="0"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -117,6 +117,7 @@
|
||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.3.0",
|
||||||
"react-leaflet": "^4.2.1",
|
"react-leaflet": "^4.2.1",
|
||||||
"react-loading-skeleton": "^3.5.0",
|
"react-loading-skeleton": "^3.5.0",
|
||||||
|
"react-password-checklist": "^1.8.1",
|
||||||
"react-player": "^2.16.0",
|
"react-player": "^2.16.0",
|
||||||
"react-quill": "^0.0.2",
|
"react-quill": "^0.0.2",
|
||||||
"react-resizable-panels": "^2.0.19",
|
"react-resizable-panels": "^2.0.19",
|
||||||
|
|
@ -14011,6 +14012,14 @@
|
||||||
"react": ">=16.8.0"
|
"react": ">=16.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-password-checklist": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-password-checklist/-/react-password-checklist-1.8.1.tgz",
|
||||||
|
"integrity": "sha512-QHIU/OejxoH4/cIfYLHaHLb+yYc8mtL0Vr4HTmULxQg3ZNdI9Ni/yYf7pwLBgsUh4sseKCV/GzzYHWpHqejTGw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">16.0.0-alpha || >17.0.0-alpha || >18.0.0-alpha"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-player": {
|
"node_modules/react-player": {
|
||||||
"version": "2.16.0",
|
"version": "2.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-player/-/react-player-2.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-player/-/react-player-2.16.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,7 @@
|
||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.3.0",
|
||||||
"react-leaflet": "^4.2.1",
|
"react-leaflet": "^4.2.1",
|
||||||
"react-loading-skeleton": "^3.5.0",
|
"react-loading-skeleton": "^3.5.0",
|
||||||
|
"react-password-checklist": "^1.8.1",
|
||||||
"react-player": "^2.16.0",
|
"react-player": "^2.16.0",
|
||||||
"react-quill": "^0.0.2",
|
"react-quill": "^0.0.2",
|
||||||
"react-resizable-panels": "^2.0.19",
|
"react-resizable-panels": "^2.0.19",
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,10 @@ import {
|
||||||
} from "./http-config/http-interceptor-service";
|
} from "./http-config/http-interceptor-service";
|
||||||
|
|
||||||
export async function login(data: any) {
|
export async function login(data: any) {
|
||||||
|
|
||||||
const res = await getCsrfToken();
|
|
||||||
const csrfToken = res?.data?.token;
|
|
||||||
|
|
||||||
|
|
||||||
console.log("Token CSRF : ", csrfToken);
|
|
||||||
|
|
||||||
const pathUrl = "signin";
|
const pathUrl = "signin";
|
||||||
const headers = {
|
const headers = {
|
||||||
'accept': 'application/json',
|
'accept': 'application/json',
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
'X-XSRF-TOKEN': csrfToken
|
|
||||||
};
|
};
|
||||||
return httpPost(pathUrl, headers, data);
|
return httpPost(pathUrl, headers, data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
httpDeleteInterceptor,
|
||||||
httpGetInterceptor,
|
httpGetInterceptor,
|
||||||
httpPostInterceptor,
|
httpPostInterceptor,
|
||||||
} from "../http-config/http-interceptor-service";
|
} from "../http-config/http-interceptor-service";
|
||||||
|
|
@ -71,3 +72,31 @@ export async function getTicketingDetail(id: any) {
|
||||||
const url = `ticketing?id=${id}`;
|
const url = `ticketing?id=${id}`;
|
||||||
return httpGetInterceptor(url);
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,24 @@ import axiosBaseInstance from "./axios-base-instance";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
import qs from "qs";
|
import qs from "qs";
|
||||||
|
import { getCsrfToken } from "../auth";
|
||||||
|
|
||||||
export async function httpPost(pathUrl: any, headers: any, data?: any) {
|
export async function httpPost(pathUrl: any, headers: any, data?: any) {
|
||||||
const response = await axiosBaseInstance
|
const resCsrf = await getCsrfToken();
|
||||||
.post(pathUrl, data, { headers })
|
const csrfToken = resCsrf?.data?.token;
|
||||||
|
|
||||||
|
const defaultHeaders = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergedHeaders = {
|
||||||
|
...defaultHeaders,
|
||||||
|
...headers,
|
||||||
|
...(csrfToken ? { "X-XSRF-TOKEN": csrfToken } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await axiosBaseInstance
|
||||||
|
.post(pathUrl, data, { headers: mergedHeaders })
|
||||||
.catch(function (error: any) {
|
.catch(function (error: any) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return error.response;
|
return error.response;
|
||||||
|
|
@ -50,8 +64,21 @@ export async function httpGet(pathUrl: any, headers: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function httpPut(pathUrl: any, headers: any, data?: any) {
|
export async function httpPut(pathUrl: any, headers: any, data?: any) {
|
||||||
|
const resCsrf = await getCsrfToken();
|
||||||
|
const csrfToken = resCsrf?.data?.token;
|
||||||
|
|
||||||
|
const defaultHeaders = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergedHeaders = {
|
||||||
|
...defaultHeaders,
|
||||||
|
...headers,
|
||||||
|
...(csrfToken ? { "X-XSRF-TOKEN": csrfToken } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
const response = await axiosBaseInstance
|
const response = await axiosBaseInstance
|
||||||
.put(pathUrl, data, { headers })
|
.put(pathUrl, data, { headers: mergedHeaders })
|
||||||
.catch(function (error: any) {
|
.catch(function (error: any) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return error.response;
|
return error.response;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
import {
|
||||||
|
httpDeleteInterceptor,
|
||||||
|
httpGetInterceptor,
|
||||||
|
httpPostInterceptor,
|
||||||
|
} from "../http-config/http-interceptor-service";
|
||||||
|
|
||||||
|
export async function AdministrationUserList(
|
||||||
|
id: string,
|
||||||
|
page: number,
|
||||||
|
name = "",
|
||||||
|
size: string,
|
||||||
|
featureId: string,
|
||||||
|
role = ""
|
||||||
|
) {
|
||||||
|
const url = `users/pagination/internal?enablePage=1&size=${size}&page=${page}&levelId=${id}&name=${name}&featureId=${featureId}&roleFilter=${role}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function AdministrationLevelList() {
|
||||||
|
const url = "users/user-levels/list";
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getListEducation() {
|
||||||
|
const url = "users/user-educations/list";
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getListSchools() {
|
||||||
|
const url = "users/user-schools/list";
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
export async function getListCompetencies() {
|
||||||
|
const url = "users/user-competencies/list";
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveUserInternal(data: any) {
|
||||||
|
const url = "users/save";
|
||||||
|
return httpPostInterceptor(url, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,11 @@ export async function getCategories(page: number) {
|
||||||
return httpGetInterceptor(url);
|
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) {
|
export async function publishUnpublishCategory(id: number, status: string) {
|
||||||
const url = `media/categories/publish?id=${id}&status=${status}`;
|
const url = `media/categories/publish?id=${id}&status=${status}`;
|
||||||
return httpPostInterceptor(url);
|
return httpPostInterceptor(url);
|
||||||
|
|
@ -66,3 +71,61 @@ export async function setBanner(id: number, status: boolean) {
|
||||||
const url = `media/banner?id=${id}&status=${status}`;
|
const url = `media/banner?id=${id}&status=${status}`;
|
||||||
return httpPostInterceptor(url);
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
11
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-paste-from-office/src/index.js
generated
vendored
Normal file
11
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-paste-from-office/src/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
/**
|
||||||
|
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @module paste-from-office
|
||||||
|
*/
|
||||||
|
export { default as PasteFromOffice } from '@ckeditor/ckeditor5-paste-from-office/src/pastefromoffice.js';
|
||||||
|
export { default as MSWordNormalizer } from '@ckeditor/ckeditor5-paste-from-office/src/normalizers/mswordnormalizer.js';
|
||||||
|
export { parseHtml } from '@ckeditor/ckeditor5-paste-from-office/src/filters/parse.js';
|
||||||
|
import '@ckeditor/ckeditor5-paste-from-office/src/augmentation.js';
|
||||||
Loading…
Reference in New Issue