feat:add lampiran contest
This commit is contained in:
commit
4a1f767861
|
|
@ -232,6 +232,10 @@ export default function CreateUserForm() {
|
||||||
id: "ADM-ID",
|
id: "ADM-ID",
|
||||||
name: "Admin",
|
name: "Admin",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "EXE-ID",
|
||||||
|
name: "Eksekutif",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "APP-ID",
|
id: "APP-ID",
|
||||||
name: "Approver",
|
name: "Approver",
|
||||||
|
|
|
||||||
|
|
@ -1,73 +1,98 @@
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
ColumnDef,
|
|
||||||
} from "@tanstack/react-table";
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
|
||||||
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 { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
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 {
|
||||||
|
Menubar,
|
||||||
|
MenubarContent,
|
||||||
|
MenubarMenu,
|
||||||
|
MenubarTrigger,
|
||||||
|
} from "@/components/ui/menubar";
|
||||||
|
import { htmlToString } from "@/utils/globals";
|
||||||
|
import EditSpvFAQModal from "./edit";
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: "no",
|
accessorKey: "no",
|
||||||
header: "No",
|
header: "No",
|
||||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
accessorKey: "question",
|
accessorKey: "question",
|
||||||
header: "Question",
|
header: "Pertanyaan",
|
||||||
cell: ({ row }) => <span>{row.getValue("question")}</span>,
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">
|
||||||
|
{htmlToString(row.getValue("question"))}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "answer",
|
accessorKey: "answer",
|
||||||
header: "Answer",
|
header: "Answer",
|
||||||
cell: ({ row }) => <span>{row.getValue("answer")}</span>,
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">
|
||||||
|
{htmlToString(row.getValue("answer"))}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: "actions",
|
||||||
accessorKey: "action",
|
accessorKey: "action",
|
||||||
header: "Actions",
|
header: "Actions",
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
cell: ({ row }) => {
|
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("/supervisor/faq?dataChange=true");
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<Menubar className="border-none">
|
||||||
<DropdownMenuTrigger asChild>
|
<MenubarMenu>
|
||||||
|
<MenubarTrigger>
|
||||||
<Button
|
<Button
|
||||||
size="icon"
|
size="icon"
|
||||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||||
>
|
>
|
||||||
<span className="sr-only">Open menu</span>
|
|
||||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</MenubarTrigger>
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
<MenubarContent className="flex flex-col gap-2 justify-center items-start p-4">
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
<EditSpvFAQModal id={row.original.id} isDetail={true} />
|
||||||
<Eye className="w-4 h-4 me-1.5" />
|
|
||||||
View
|
<EditSpvFAQModal id={row.original.id} isDetail={false} />
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
<a
|
||||||
<SquarePen className="w-4 h-4 me-1.5" />
|
onClick={() => faqDelete(row.original.id)}
|
||||||
Edit
|
className="hover:underline cursor-pointer hover:text-destructive"
|
||||||
</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
|
Delete
|
||||||
</DropdownMenuItem>
|
</a>
|
||||||
</DropdownMenuContent>
|
</MenubarContent>
|
||||||
</DropdownMenu>
|
</MenubarMenu>
|
||||||
|
</Menubar>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
"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 {
|
||||||
|
getUserRoles,
|
||||||
|
postCategory,
|
||||||
|
postDataFAQ,
|
||||||
|
} from "@/service/settings/settings";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { close, error, loading } from "@/config/swal";
|
||||||
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
import { stringify } from "querystring";
|
||||||
|
|
||||||
|
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 CreateSpvFAQModal() {
|
||||||
|
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 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("/supervisor/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="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,237 @@
|
||||||
|
"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 EditSpvFAQModal(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("/supervisor/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="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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -24,37 +24,32 @@ import {
|
||||||
TableHeader,
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
||||||
import {
|
import { useSearchParams } from "next/navigation";
|
||||||
ChevronLeft,
|
|
||||||
ChevronRight,
|
|
||||||
Eye,
|
|
||||||
MoreVertical,
|
|
||||||
Search,
|
|
||||||
SquarePen,
|
|
||||||
Trash2,
|
|
||||||
TrendingDown,
|
|
||||||
TrendingUp,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
|
||||||
import TablePagination from "@/components/table/table-pagination";
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
import { getFaqList } from "@/service/master/faq";
|
|
||||||
import columns from "./column";
|
import columns from "./column";
|
||||||
|
|
||||||
const FaqTable = () => {
|
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 CreateSpvFAQModal from "./create";
|
||||||
|
|
||||||
|
const SpvFAQTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
const dataChange = searchParams?.get("dataChange");
|
||||||
|
const [openModal, setOpenModal] = React.useState(false);
|
||||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
const [totalData, setTotalData] = React.useState<number>(1);
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
|
@ -68,10 +63,9 @@ const FaqTable = () => {
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
});
|
});
|
||||||
|
|
||||||
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 table = useReactTable({
|
const table = useReactTable({
|
||||||
data: dataTable,
|
data: dataTable,
|
||||||
columns,
|
columns,
|
||||||
|
|
@ -94,7 +88,14 @@ const FaqTable = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const pageFromUrl = searchParams?.get('page');
|
if (dataChange) {
|
||||||
|
router.push("/admin/settings/faq");
|
||||||
|
}
|
||||||
|
fetchData();
|
||||||
|
}, [dataChange]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get("page");
|
||||||
if (pageFromUrl) {
|
if (pageFromUrl) {
|
||||||
setPage(Number(pageFromUrl));
|
setPage(Number(pageFromUrl));
|
||||||
}
|
}
|
||||||
|
|
@ -102,55 +103,35 @@ const FaqTable = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [page, limit]);
|
}, [page]);
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const res = await getFaqList();
|
loading();
|
||||||
const contentData = res?.data?.data;
|
const response = await getListFAQ();
|
||||||
contentData.forEach((item: any, index: number) => {
|
const data = response?.data?.data;
|
||||||
item.no = (page - 1) * limit + index + 1;
|
console.log("respone", response);
|
||||||
|
data.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * 10 + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("contentData : ", contentData);
|
setDataTable(data);
|
||||||
|
setTotalData(data?.length);
|
||||||
setDataTable(contentData);
|
setTotalPage(1);
|
||||||
setTotalData(contentData?.totalElements || contentData?.length);
|
close();
|
||||||
setTotalPage(contentData?.totalPages || 1);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching tasks:", error);
|
console.error("Error fetching tasks:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<div className="flex justify-between items-center px-6">
|
<div className="flex justify-between mb-10 items-center">
|
||||||
<div>
|
<p className="text-xl font-medium text-default-900">FAQ</p>
|
||||||
<InputGroup merged>
|
<CreateSpvFAQModal />
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
|
||||||
<Search className=" h-4 w-4 dark:text-white" />
|
|
||||||
</InputGroupText>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
placeholder="Search Judul..."
|
|
||||||
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-none">
|
|
||||||
<Input
|
<Table className="overflow-hidden">
|
||||||
placeholder="Filter Status..."
|
|
||||||
value={
|
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
|
||||||
}
|
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
|
||||||
}
|
|
||||||
className="max-w-sm "
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Table className="overflow-hidden mt-3">
|
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
|
@ -191,9 +172,13 @@ const FaqTable = () => {
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
{/* <TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FaqTable;
|
export default SpvFAQTable;
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,11 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import SpvFAQTable from "./components/table";
|
||||||
import FaqTable from "./components/table";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Plus } from "lucide-react";
|
|
||||||
|
|
||||||
const FaqPage = async () => {
|
const FaqPage = async () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="space-y-4">
|
<SpvFAQTable />
|
||||||
<Card>
|
|
||||||
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
|
||||||
<CardTitle>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
|
||||||
FAQ Data
|
|
||||||
</div>
|
|
||||||
<div className="flex-none">
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
size="md"
|
|
||||||
>
|
|
||||||
<Plus className="w-6 h-6 me-1.5"/>
|
|
||||||
New FAQ
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="p-0">
|
|
||||||
<FaqTable />
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,7 @@
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
ColumnDef,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
|
|
||||||
import {
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
Eye,
|
|
||||||
MoreVertical,
|
|
||||||
SquarePen,
|
|
||||||
Trash2,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
|
|
@ -19,6 +11,7 @@ import {
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
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 { formatDateToIndonesian } from "@/utils/globals";
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
|
|
@ -34,19 +27,23 @@ const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: "title",
|
accessorKey: "title",
|
||||||
header: "Title",
|
header: "Title",
|
||||||
cell: ({ row }) => <span>{row.getValue("title")}</span>,
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("title")}</span>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "commentFromUserName",
|
accessorKey: "commentFromUserName",
|
||||||
header: "Sender",
|
header: "Sender",
|
||||||
cell: ({ row }) => <span>{row.getValue("commentFromUserName")}</span>,
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("commentFromUserName")}</span>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "type",
|
accessorKey: "type",
|
||||||
header: "Channel",
|
header: "Channel",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const type = row.getValue("type") as { name: string };
|
const type = row.getValue("type") as { name: string };
|
||||||
return <span>{type?.name}</span>;
|
return <span className="normal-case">{type?.name}</span>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -54,14 +51,16 @@ const columns: ColumnDef<any>[] = [
|
||||||
header: "Operator",
|
header: "Operator",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const createdBy = row.getValue("createdBy") as { fullname: string };
|
const createdBy = row.getValue("createdBy") as { fullname: string };
|
||||||
return <span>{createdBy?.fullname}</span>;
|
return <span className="normal-case">{createdBy?.fullname}</span>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "createdAt",
|
accessorKey: "createdAt",
|
||||||
header: "Tanggal Unggah ",
|
header: "Tanggal Unggah ",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<span className="whitespace-nowrap">{row.getValue("createdAt")}</span>
|
<span className="whitespace-nowrap">
|
||||||
|
{formatDateToIndonesian(row.getValue("createdAt"))}
|
||||||
|
</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -72,18 +71,18 @@ const columns: ColumnDef<any>[] = [
|
||||||
open: "bg-primary/20 text-primary",
|
open: "bg-primary/20 text-primary",
|
||||||
close: "bg-success/20 text-success",
|
close: "bg-success/20 text-success",
|
||||||
};
|
};
|
||||||
const status = row.getValue("status") as { id: number, name: string };;
|
const status = row.getValue("status") as { id: number; name: string };
|
||||||
const statusName = status?.name?.toLocaleLowerCase();
|
const statusName = status?.name?.toLocaleLowerCase();
|
||||||
console.log(statusName);
|
console.log(statusName);
|
||||||
const statusStyles = statusColors[statusName] || "default";
|
const statusStyles = statusColors[statusName] || "default";
|
||||||
if (statusName) {
|
if (statusName) {
|
||||||
return (
|
return (
|
||||||
<Badge
|
<Badge className={cn("rounded-full px-5", statusStyles)}>
|
||||||
className={cn("rounded-full px-5",statusStyles)}
|
{statusName}{" "}
|
||||||
>{statusName} </Badge>
|
</Badge>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: "actions",
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
|
|
@ -62,16 +64,19 @@ const TicketingTable = () => {
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
const [search, setSearch] = React.useState("");
|
||||||
|
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
|
const [showData, setShowData] = React.useState("10");
|
||||||
|
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
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 table = useReactTable({
|
const table = useReactTable({
|
||||||
data: dataTable,
|
data: dataTable,
|
||||||
|
|
@ -94,8 +99,24 @@ const TicketingTable = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let typingTimer: any;
|
||||||
|
const doneTypingInterval = 1500;
|
||||||
|
|
||||||
|
const handleKeyUp = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function doneTyping() {
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const pageFromUrl = searchParams?.get('page');
|
const pageFromUrl = searchParams?.get("page");
|
||||||
if (pageFromUrl) {
|
if (pageFromUrl) {
|
||||||
setPage(Number(pageFromUrl));
|
setPage(Number(pageFromUrl));
|
||||||
}
|
}
|
||||||
|
|
@ -103,15 +124,15 @@ const TicketingTable = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [page, limit]);
|
}, [page]);
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const res = await ticketingPagination('', limit, page-1);
|
const res = await ticketingPagination(search, Number(showData), page - 1);
|
||||||
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);
|
||||||
|
|
@ -125,34 +146,46 @@ const TicketingTable = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto">
|
<>
|
||||||
<div className="flex justify-between items-center px-5">
|
<div className="flex justify-between py-3">
|
||||||
<div>
|
|
||||||
<InputGroup merged>
|
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
|
||||||
<Search className=" h-4 w-4 dark:text-white" />
|
|
||||||
</InputGroupText>
|
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search Judul..."
|
placeholder="Search"
|
||||||
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
onKeyUp={handleKeyUp}
|
||||||
/>
|
onKeyDown={handleKeyDown}
|
||||||
</InputGroup>
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
</div>
|
className="max-w-[300px]"
|
||||||
<div className="flex-none">
|
|
||||||
<Input
|
|
||||||
placeholder="Filter Status..."
|
|
||||||
value={
|
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
|
||||||
}
|
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
|
||||||
}
|
|
||||||
className="max-w-sm "
|
|
||||||
/>
|
/>
|
||||||
|
<div 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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
|
@ -193,8 +226,12 @@ const TicketingTable = () => {
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination table={table} totalData={totalData} totalPage={totalPage} />
|
<TablePagination
|
||||||
</div>
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,21 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { UploadIcon } from "lucide-react";
|
|
||||||
import TicketingTable from "./components/table";
|
import TicketingTable from "./components/table";
|
||||||
|
|
||||||
const TicketingPage = async () => {
|
const TicketingPage = async () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="space-y-4">
|
|
||||||
<Card>
|
<section
|
||||||
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
id="table"
|
||||||
<CardTitle>
|
className="flex flex-col gap-2 bg-white rounded-lg p-3 mt-5"
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
|
||||||
Ticket Data
|
|
||||||
</div>
|
|
||||||
<div className="flex-none">
|
|
||||||
{/* <Button
|
|
||||||
fullWidth
|
|
||||||
size="md"
|
|
||||||
>
|
>
|
||||||
<Plus className="w-6 h-6 me-1.5"/>
|
<div className="flex justify-between py-3">
|
||||||
New Ticket
|
<p className="text-lg">Semua Ticket</p>
|
||||||
</Button> */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="p-0">
|
|
||||||
<TicketingTable />
|
<TicketingTable />
|
||||||
</CardContent>
|
</section>
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,9 @@ import { useTranslations } from "next-intl";
|
||||||
// Schema validasi menggunakan zod
|
// Schema validasi menggunakan zod
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
username: z.string().min(1, { message: "Judul diperlukan" }),
|
username: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
password: z.string().min(4, { message: "Password must be at least 4 characters." }),
|
password: z
|
||||||
|
.string()
|
||||||
|
.min(4, { message: "Password must be at least 4 characters." }),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Tipe untuk form values
|
// Tipe untuk form values
|
||||||
|
|
@ -37,7 +39,9 @@ const LoginForm = () => {
|
||||||
const t = useTranslations("LandingPage");
|
const t = useTranslations("LandingPage");
|
||||||
|
|
||||||
const togglePasswordType = () => {
|
const togglePasswordType = () => {
|
||||||
setPasswordType((prevType) => (prevType === "password" ? "text" : "password"));
|
setPasswordType((prevType) =>
|
||||||
|
prevType === "password" ? "text" : "password"
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -86,18 +90,29 @@ const LoginForm = () => {
|
||||||
const profile = await getProfile(access_token);
|
const profile = await getProfile(access_token);
|
||||||
console.log("PROFILE : ", profile?.data?.data);
|
console.log("PROFILE : ", profile?.data?.data);
|
||||||
|
|
||||||
if (profile?.data?.data?.isInternational == true || profile?.data?.data?.isActive == false || profile?.data?.data?.isDelete == true) {
|
if (
|
||||||
|
profile?.data?.data?.isInternational == true ||
|
||||||
|
profile?.data?.data?.isActive == false ||
|
||||||
|
profile?.data?.data?.isDelete == true
|
||||||
|
) {
|
||||||
Object.keys(Cookies.get()).forEach((cookieName) => {
|
Object.keys(Cookies.get()).forEach((cookieName) => {
|
||||||
Cookies.remove(cookieName);
|
Cookies.remove(cookieName);
|
||||||
});
|
});
|
||||||
warning("Akun Anda tidak dapat digunakan untuk masuk ke MediaHub Polri", "/auth/login");
|
warning(
|
||||||
|
"Akun Anda tidak dapat digunakan untuk masuk ke MediaHub Polri",
|
||||||
|
"/auth/login"
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
Cookies.set("home_path", profile?.data?.data?.homePath, {
|
Cookies.set("home_path", profile?.data?.data?.homePath, {
|
||||||
expires: 1,
|
expires: 1,
|
||||||
});
|
});
|
||||||
Cookies.set("profile_picture", profile?.data?.data?.profilePictureUrl, {
|
Cookies.set(
|
||||||
|
"profile_picture",
|
||||||
|
profile?.data?.data?.profilePictureUrl,
|
||||||
|
{
|
||||||
expires: 1,
|
expires: 1,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
Cookies.set("state", profile?.data?.data?.userLevel?.name, {
|
Cookies.set("state", profile?.data?.data?.userLevel?.name, {
|
||||||
expires: 1,
|
expires: 1,
|
||||||
});
|
});
|
||||||
|
|
@ -113,12 +128,20 @@ const LoginForm = () => {
|
||||||
setCookiesEncrypt("ulie", profile?.data?.data?.userLevel?.id, {
|
setCookiesEncrypt("ulie", profile?.data?.data?.userLevel?.id, {
|
||||||
expires: 1,
|
expires: 1,
|
||||||
});
|
});
|
||||||
setCookiesEncrypt("uplie", profile?.data?.data?.userLevel?.parentLevelId, {
|
setCookiesEncrypt(
|
||||||
|
"uplie",
|
||||||
|
profile?.data?.data?.userLevel?.parentLevelId,
|
||||||
|
{
|
||||||
expires: 1,
|
expires: 1,
|
||||||
});
|
}
|
||||||
setCookiesEncrypt("ulne", profile?.data?.data?.userLevel?.levelNumber, {
|
);
|
||||||
|
setCookiesEncrypt(
|
||||||
|
"ulne",
|
||||||
|
profile?.data?.data?.userLevel?.levelNumber,
|
||||||
|
{
|
||||||
expires: 1,
|
expires: 1,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
setCookiesEncrypt("ufne", profile?.data?.data?.fullname, {
|
setCookiesEncrypt("ufne", profile?.data?.data?.fullname, {
|
||||||
expires: 1,
|
expires: 1,
|
||||||
});
|
});
|
||||||
|
|
@ -128,7 +151,7 @@ const LoginForm = () => {
|
||||||
setCookiesEncrypt("uinse", profile?.data?.data?.instituteId, {
|
setCookiesEncrypt("uinse", profile?.data?.data?.instituteId, {
|
||||||
expires: 1,
|
expires: 1,
|
||||||
});
|
});
|
||||||
|
console.log("ssaddd", profile?.data?.data?.roleId);
|
||||||
if (
|
if (
|
||||||
Number(profile?.data?.data?.roleId) == 2 ||
|
Number(profile?.data?.data?.roleId) == 2 ||
|
||||||
Number(profile?.data?.data?.roleId) == 3 ||
|
Number(profile?.data?.data?.roleId) == 3 ||
|
||||||
|
|
@ -136,9 +159,19 @@ const LoginForm = () => {
|
||||||
Number(profile?.data?.data?.roleId) == 9 ||
|
Number(profile?.data?.data?.roleId) == 9 ||
|
||||||
Number(profile?.data?.data?.roleId) == 10 ||
|
Number(profile?.data?.data?.roleId) == 10 ||
|
||||||
Number(profile?.data?.data?.roleId) == 11 ||
|
Number(profile?.data?.data?.roleId) == 11 ||
|
||||||
Number(profile?.data?.data?.roleId) == 12
|
Number(profile?.data?.data?.roleId) == 12 ||
|
||||||
|
Number(profile?.data?.data?.roleId) == 18
|
||||||
|
) {
|
||||||
|
if (profile?.data?.data?.roleId === 18) {
|
||||||
|
window.location.href = "/in/dashboard/executive";
|
||||||
|
// router.push('/admin/dashboard');
|
||||||
|
Cookies.set("status", "login", {
|
||||||
|
expires: 1,
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
|
profile?.data?.data?.userLevel?.id == 761 ||
|
||||||
|
profile?.data?.data?.userLevel?.parentLevelId == 761
|
||||||
) {
|
) {
|
||||||
if (profile?.data?.data?.userLevel?.id == 761 || profile?.data?.data?.userLevel?.parentLevelId == 761) {
|
|
||||||
window.location.href = "/in/welcome";
|
window.location.href = "/in/welcome";
|
||||||
Cookies.set("status", "login", {
|
Cookies.set("status", "login", {
|
||||||
expires: 1,
|
expires: 1,
|
||||||
|
|
@ -182,7 +215,11 @@ const LoginForm = () => {
|
||||||
"border-destructive": errors.username,
|
"border-destructive": errors.username,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
{errors.username?.message && <div className="text-destructive mt-2 text-sm">{errors.username.message}</div>}
|
{errors.username?.message && (
|
||||||
|
<div className="text-destructive mt-2 text-sm">
|
||||||
|
{errors.username.message}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-3.5 space-y-2">
|
<div className="mt-3.5 space-y-2">
|
||||||
|
|
@ -190,12 +227,33 @@ const LoginForm = () => {
|
||||||
{t("password")}
|
{t("password")}
|
||||||
</Label>
|
</Label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Input size="lg" disabled={isPending} {...register("password")} id="password" type={passwordType} className="peer" />
|
<Input
|
||||||
<div className="absolute top-1/2 -translate-y-1/2 ltr:right-4 rtl:left-4 cursor-pointer" onClick={togglePasswordType}>
|
size="lg"
|
||||||
{passwordType === "password" ? <Icon icon="heroicons:eye" className="w-5 h-5 text-default-400" /> : <Icon icon="heroicons:eye-slash" className="w-5 h-5 text-default-400" />}
|
disabled={isPending}
|
||||||
|
{...register("password")}
|
||||||
|
id="password"
|
||||||
|
type={passwordType}
|
||||||
|
className="peer"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="absolute top-1/2 -translate-y-1/2 ltr:right-4 rtl:left-4 cursor-pointer"
|
||||||
|
onClick={togglePasswordType}
|
||||||
|
>
|
||||||
|
{passwordType === "password" ? (
|
||||||
|
<Icon icon="heroicons:eye" className="w-5 h-5 text-default-400" />
|
||||||
|
) : (
|
||||||
|
<Icon
|
||||||
|
icon="heroicons:eye-slash"
|
||||||
|
className="w-5 h-5 text-default-400"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{errors.password?.message && <div className="text-destructive mt-2 text-sm">{errors.password.message}</div>}
|
{errors.password?.message && (
|
||||||
|
<div className="text-destructive mt-2 text-sm">
|
||||||
|
{errors.password.message}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
|
|
@ -203,7 +261,10 @@ const LoginForm = () => {
|
||||||
<Checkbox id="checkbox" defaultChecked />
|
<Checkbox id="checkbox" defaultChecked />
|
||||||
<Label htmlFor="checkbox">{t("rememberMe")}</Label>
|
<Label htmlFor="checkbox">{t("rememberMe")}</Label>
|
||||||
</div>
|
</div>
|
||||||
<Link href="/auth/forgot-password" className="text-sm text-default-800 dark:text-default-400 leading-6 font-medium">
|
<Link
|
||||||
|
href="/auth/forgot-password"
|
||||||
|
className="text-sm text-default-800 dark:text-default-400 leading-6 font-medium"
|
||||||
|
>
|
||||||
{t("forgotPass")}
|
{t("forgotPass")}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -24,20 +24,6 @@ import {
|
||||||
TableHeader,
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
} from "@/components/ui/table";
|
} 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 { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
|
|
@ -75,7 +61,6 @@ const UserInternalTable = () => {
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const dataChange = searchParams?.get("dataChange");
|
const dataChange = searchParams?.get("dataChange");
|
||||||
const [showData, setShowData] = React.useState("10");
|
const [showData, setShowData] = React.useState("10");
|
||||||
const [categories, setCategories] = React.useState<any>();
|
|
||||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
const [totalData, setTotalData] = React.useState<number>(1);
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
|
|
||||||
25
lib/menus.ts
25
lib/menus.ts
|
|
@ -3194,6 +3194,31 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
} else if (Number(roleId) === 18) {
|
||||||
|
menusSelected = [
|
||||||
|
{
|
||||||
|
groupLabel: t("apps"),
|
||||||
|
id: "dashboard",
|
||||||
|
menus: [
|
||||||
|
{
|
||||||
|
id: "dashboard",
|
||||||
|
href: "/dashboard",
|
||||||
|
label: t("dashboard"),
|
||||||
|
active: pathname.includes("/dashboard"),
|
||||||
|
icon: "material-symbols:dashboard",
|
||||||
|
submenus: [
|
||||||
|
{
|
||||||
|
href: "/dashboard/executive",
|
||||||
|
label: "Executive",
|
||||||
|
active: pathname === "/dashboard/executive",
|
||||||
|
icon: "heroicons:arrow-trending-up",
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return menusSelected;
|
return menusSelected;
|
||||||
|
|
|
||||||
1366
pnpm-lock.yaml
1366
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
0
vendor/ckeditor5/node_modules/typescript/bin/tsserver
generated
vendored
Executable file → Normal file
0
vendor/ckeditor5/node_modules/typescript/bin/tsserver
generated
vendored
Executable file → Normal file
Loading…
Reference in New Issue