feat:userlevel form

This commit is contained in:
Rama Priyanto 2025-02-18 12:17:41 +07:00
parent e4dd1da844
commit 66aaa5f0b3
12 changed files with 885 additions and 449 deletions

View File

@ -1,12 +1,34 @@
import { AddIcon } from "@/components/icons";
import MappingUserLevel from "@/components/table/master/master-user-level/mapping-user-level"; import MappingUserLevel from "@/components/table/master/master-user-level/mapping-user-level";
import MasterUserLevelTable from "@/components/table/master/master-user-level/master-user-level-table"; import MasterUserLevelTable from "@/components/table/master/master-user-level/master-user-level-table";
import { Button } from "@heroui/button";
import Link from "next/link";
import React from "react"; import React from "react";
const AdminMasterUserLevel = () => { const AdminMasterUserLevel = () => {
return ( return (
// <div><MasterUserLevelTable /></div> // <div>
<div> // <MasterUserLevelTable />
<MappingUserLevel /> // </div>
// <div>
// <MappingUserLevel />
// </div>
<div className="overflow-x-hidden overflow-y-scroll">
<div className="px-2 md:px-4 md:py-4 w-full">
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl py-3">
<Link href="/admin/master-user-level/create" className="mx-3">
<Button
size="md"
color="primary"
className="bg-[#F07C00] text-white"
>
Pengguna Baru
<AddIcon />
</Button>
</Link>
<MasterUserLevelTable />
</div>
</div>
</div> </div>
); );
}; };

View File

@ -0,0 +1,12 @@
import CreateMasterUserLevelForm from "@/components/form/master/master-user-level/master-user-level-form";
import React from "react";
const AdminMasterUserLevelCreate = () => {
return (
<div>
<CreateMasterUserLevelForm />
</div>
);
};
export default AdminMasterUserLevelCreate;

View File

@ -0,0 +1,8 @@
import EditUserLevelForm from "@/components/form/master/master-user-level/edit-user-level-form";
import React from "react";
const AdminMasterUserLevelCreate = () => {
return <EditUserLevelForm />;
};
export default AdminMasterUserLevelCreate;

View File

@ -0,0 +1,36 @@
import { AddIcon } from "@/components/icons";
import MappingUserLevel from "@/components/table/master/master-user-level/mapping-user-level";
import MasterUserLevelTable from "@/components/table/master/master-user-level/master-user-level-table";
import { Button } from "@heroui/button";
import Link from "next/link";
import React from "react";
const AdminMasterUserLevel = () => {
return (
// <div>
// <MasterUserLevelTable />
// </div>
// <div>
// <MappingUserLevel />
// </div>
<div className="overflow-x-hidden overflow-y-scroll">
<div className="px-2 md:px-4 md:py-4 w-full">
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl py-3">
<Link href="/admin/user-level/create" className="mx-3">
<Button
size="md"
color="primary"
className="bg-[#F07C00] text-white"
>
Pengguna Baru
<AddIcon />
</Button>
</Link>
<MasterUserLevelTable />
</div>
</div>
</div>
);
};
export default AdminMasterUserLevel;

View File

@ -29,6 +29,7 @@ import {
import ReactSelect from "react-select"; import ReactSelect from "react-select";
import makeAnimated from "react-select/animated"; import makeAnimated from "react-select/animated";
import { import {
Calendar,
Checkbox, Checkbox,
Chip, Chip,
Modal, Modal,
@ -36,13 +37,16 @@ import {
ModalContent, ModalContent,
ModalFooter, ModalFooter,
ModalHeader, ModalHeader,
Popover,
PopoverContent,
PopoverTrigger,
Select, Select,
SelectItem, SelectItem,
SelectSection, SelectSection,
useDisclosure, useDisclosure,
} from "@heroui/react"; } from "@heroui/react";
import GenerateSingleArticleForm from "./generate-ai-single-form"; import GenerateSingleArticleForm from "./generate-ai-single-form";
import { htmlToString } from "@/utils/global"; import { convertDateFormatNoTime, htmlToString } from "@/utils/global";
import { close, error, loading } from "@/config/swal"; import { close, error, loading } from "@/config/swal";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import Link from "next/link"; import Link from "next/link";
@ -130,10 +134,7 @@ export default function CreateArticleForm() {
const [isScheduled, setIsScheduled] = useState(false); const [isScheduled, setIsScheduled] = useState(false);
const [timeValue, setTimeValue] = useState(""); const [timeValue, setTimeValue] = useState("");
const [startDateValue, setStartDateValue] = useState({ const [startDateValue, setStartDateValue] = useState<any>(null);
startDate: null,
endDate: null,
});
const { getRootProps, getInputProps } = useDropzone({ const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles) => { onDrop: (acceptedFiles) => {
@ -766,15 +767,15 @@ export default function CreateArticleForm() {
{errors?.tags && ( {errors?.tags && (
<p className="text-red-400 text-sm mb-3">{errors.tags?.message}</p> <p className="text-red-400 text-sm mb-3">{errors.tags?.message}</p>
)} )}
{/* <div className="flex flex-col gap-2 mt-3"> <div className="flex flex-col gap-2 mt-3">
<Switch isSelected={isScheduled} onValueChange={setIsScheduled}> <Switch isSelected={isScheduled} onValueChange={setIsScheduled}>
Publish dengan Jadwal <p className="text-black">Publish dengan Jadwal</p>
</Switch> </Switch>
{isScheduled && ( {isScheduled && (
<div className="flex flex-col lg:flex-row gap-3"> <div className="flex flex-col lg:flex-row gap-3">
<div className="w-full lg:w-[140px] flex flex-col gal-2 "> <div className="w-full lg:w-[140px] flex flex-col gal-2 ">
<p className="text-sm">Tanggal</p> <p className="text-sm">Tanggal</p>
<Datepicker {/* <Datepicker
value={startDateValue} value={startDateValue}
displayFormat="DD/MM/YYYY" displayFormat="DD/MM/YYYY"
popoverDirection="down" popoverDirection="down"
@ -782,9 +783,30 @@ export default function CreateArticleForm() {
useRange={false} useRange={false}
onChange={(e: any) => setStartDateValue(e)} onChange={(e: any) => setStartDateValue(e)}
inputClassName="z-50 w-full text-xs lg:text-sm bg-white dark bg-black border-1 border-gray-200 px-2 py-[6px] rounded-sm lg:rounded-lg h-[30px] lg:h-[40px] text-gray-600 dark:text-gray-300" inputClassName="z-50 w-full text-xs lg:text-sm bg-white dark bg-black border-1 border-gray-200 px-2 py-[6px] rounded-sm lg:rounded-lg h-[30px] lg:h-[40px] text-gray-600 dark:text-gray-300"
/> */}
<Popover
placement="bottom"
classNames={{ content: ["!bg-transparent", "p-0"] }}
>
<PopoverTrigger>
<Button
className="w-full !h-[30px] lg:h-[40px] border-1 rounded-lg text-black"
variant="bordered"
>
{startDateValue
? convertDateFormatNoTime(startDateValue)
: "-"}
</Button>
</PopoverTrigger>
<PopoverContent className="bg-transparent">
<Calendar
value={startDateValue}
onChange={setStartDateValue}
/> />
</PopoverContent>
</Popover>
</div> </div>
<div className="w-[140px] flex flex-col gal-2 "> {/* <div className="w-[140px] flex flex-col gal-2 ">
<p className="text-sm">Waktu</p> <p className="text-sm">Waktu</p>
<Input <Input
type="time" type="time"
@ -802,10 +824,10 @@ export default function CreateArticleForm() {
], ],
}} }}
/> />
</div> </div> */}
</div> </div>
)} )}
</div> */} </div>
</div> </div>
<div className="flex flex-row justify-end gap-3"> <div className="flex flex-row justify-end gap-3">
@ -813,7 +835,7 @@ export default function CreateArticleForm() {
color="primary" color="primary"
type="submit" type="submit"
isDisabled={ isDisabled={
(isScheduled && startDateValue.startDate == null) || (isScheduled && startDateValue == null) ||
(isScheduled && timeValue == "") (isScheduled && timeValue == "")
} }
onClick={() => onClick={() =>

View File

@ -0,0 +1,358 @@
"use client";
import {
Button,
Input,
Radio,
RadioGroup,
Select,
SelectItem,
SelectSection,
Slider,
Switch,
Tab,
Table,
Tabs,
Textarea,
User,
} from "@heroui/react";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { TimesIcon } from "@/components/icons";
import Link from "next/link";
import { useParams, useRouter } from "next/navigation";
import { close, error, loading } from "@/config/swal";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import dynamic from "next/dynamic";
import { Controller, useForm } from "react-hook-form";
import * as z from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import {
createUserLevels,
editUserLevels,
getAllUserLevels,
getUserLevels,
} from "@/services/user-levels/user-levels-service";
import ReactSelect from "react-select";
import makeAnimated from "react-select/animated";
const createArticleSchema = z.object({
name: z.string().min(2, {
message: "Required",
}),
aliasName: z.string().optional(),
group: z.string().min(2, {
message: "Required",
}),
});
const groups = [
{ id: 1, name: "Mabes", value: "mabes", level: 1 },
{ id: 2, name: "Polda", value: "polda", level: 2 },
{ id: 3, name: "Satker", value: "satker", level: 2 },
{ id: 4, name: "Polres", value: "polres", level: 3 },
{ id: 5, name: "Subdiv", value: "subdiv", level: 3 },
];
export default function EditUserLevelForm() {
const router = useRouter();
const MySwal = withReactContent(Swal);
const animatedComponents = makeAnimated();
const [needApproval, setNeedApproval] = useState(false);
const [parentList, setParentList] = useState<any>([]);
const [selectedParent, setSelectedParent] = useState<any>();
const params = useParams();
const id = params?.id;
const formOptions = {
resolver: zodResolver(createArticleSchema),
defaultValues: { name: "", group: "" },
};
type UserSettingSchema = z.infer<typeof createArticleSchema>;
const {
register,
control,
handleSubmit,
formState: { errors, isValid },
setValue,
getValues,
watch,
setError,
clearErrors,
} = useForm<UserSettingSchema>(formOptions);
const watchName = watch("name");
const generateSlug = (title: string) => {
return title
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, "")
.replace(/\s+/g, "-");
};
useEffect(() => {
setValue("aliasName", generateSlug(watchName));
}, [watchName]);
const findSelectedLevel = (group: string) => {
const selectedLevel = groups.find((a) => a.value === group);
return selectedLevel ? selectedLevel : null;
};
const save = async (data: any) => {
const findParent = (group: string) => {
if (group === "mabes") {
return 0;
} else if (group === "polda" || group === "satker") {
if (group === "polda") {
return 1;
} else {
return 555;
}
} else {
return selectedParent?.id;
}
};
const request = {
name: data.name,
aliasName: data.aliasName,
levelNumber: findSelectedLevel(data.group)?.level,
isActive: true,
group: data.group,
parentLevelId: findParent(data.group),
provinceId: 0,
isApprovalActive: needApproval,
};
loading();
const res = await editUserLevels(String(id), request);
if (res.error) {
error(res.message);
return false;
}
close();
successSubmit("/admin/user-level");
};
async function onSubmit(data: any) {
MySwal.fire({
title: "Save Data",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Save",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
}
function successSubmit(redirect: string) {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push(redirect);
}
});
}
useEffect(() => {
fetchCategory();
}, []);
const fetchCategory = async () => {
loading();
const request = {
limit: 10,
};
const res = await getAllUserLevels(request);
close();
if (res?.data?.data) {
setupParent(res?.data?.data);
}
};
const setupParent = (data: any) => {
const temp = [];
for (const element of data) {
temp.push({
id: element.id,
label: element.name,
value: element.aliasName,
});
}
setParentList(temp);
};
const selectedGroup = watch("group");
useEffect(() => {
initFetch();
}, [parentList]);
const initFetch = async () => {
const res = await getUserLevels(String(id));
const data = res?.data?.data;
setValue("name", data?.name);
setValue("aliasName", data?.aliasName);
setValue("group", data?.group);
const parentId = parentList.find((a: any) => a.id === data?.parentLevelId);
setSelectedParent(parentId);
};
return (
<form
className="flex flex-col lg:flex-row gap-8 text-black p-4 lg:p-8"
onSubmit={handleSubmit(onSubmit)}
>
<div className="w-full lg:w-1/2 bg-white rounded-lg p-4 lg:p-8 flex flex-col gap-1 shadow-lg">
<p className="text-sm">
Name<span className="text-danger">*</span>
</p>
<Controller
control={control}
name="name"
render={({ field: { onChange, value } }) => (
<Input
type="text"
id="name"
placeholder=""
label=""
value={value}
onChange={onChange}
labelPlacement="outside"
className="w-full "
classNames={{
inputWrapper: [
"border-1 rounded-lg",
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
],
}}
variant="bordered"
/>
)}
/>
{errors?.name && (
<p className="text-red-400 text-sm mb-3">{errors.name?.message}</p>
)}
<p className="text-sm mt-3">Alias Name</p>
<Controller
control={control}
name="aliasName"
render={({ field: { onChange, value } }) => (
<Input
type="text"
id="alias"
placeholder=""
label=""
isReadOnly
value={value}
onChange={onChange}
labelPlacement="outside"
className="w-full "
classNames={{
inputWrapper: [
"border-1 rounded-lg",
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
],
}}
variant="bordered"
/>
)}
/>
{errors?.aliasName && (
<p className="text-red-400 text-sm mb-3">
{errors.aliasName?.message}
</p>
)}
<p className="text-sm mt-3">
Group<span className="text-danger">*</span>
</p>
<Controller
control={control}
name="group"
render={({ field: { onChange, value } }) => (
<Select
variant="bordered"
label=""
labelPlacement="outside"
selectedKeys={[value]}
onChange={onChange}
classNames={{
trigger: [
"border-1 rounded-lg",
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
],
}}
>
{groups.map((group) => (
<SelectItem key={group.value}>{group.name}</SelectItem>
))}
</Select>
)}
/>
{errors?.name && (
<p className="text-red-400 text-sm mb-3">{errors.name?.message}</p>
)}
<p className="text-sm mt-3">Need Approval</p>
<Switch isSelected={needApproval} onValueChange={setNeedApproval}>
<p className="text-sm text-black">
{needApproval ? "Active" : "Inactive"}
</p>
</Switch>
{findSelectedLevel(selectedGroup)?.level === 3 && (
<>
<p className="text-sm mt-3">Parent</p>
<ReactSelect
className="basic-single text-black z-50"
classNames={{
control: (state: any) =>
"!rounded-lg bg-white !border-1 !border-gray-200 dark:!border-stone-500",
}}
classNamePrefix="select"
value={selectedParent}
onChange={setSelectedParent}
closeMenuOnSelect={false}
components={animatedComponents}
isClearable={true}
isSearchable={true}
isMulti={false}
placeholder=""
name="sub-module"
options={parentList}
/>
</>
)}
<div className="flex flex-row gap-3 mt-5">
<Button
color="primary"
type="submit"
isDisabled={
findSelectedLevel(selectedGroup)?.level === 3 && !selectedParent
}
>
Simpan
</Button>
<Link href="/admin/master-user-level">
<Button color="danger">Kembali</Button>
</Link>
</div>
</div>
</form>
);
}

View File

@ -1,118 +1,131 @@
'use client' "use client";
import { Button } from "@heroui/button"; import {
import { Card, Checkbox, CheckboxGroup, Divider, Image, Input, Radio, RadioGroup, Select, SelectItem, SelectSection, Slider, Switch, Tab, Table, Tabs, Textarea, User } from "@heroui/react"; Button,
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; Input,
Radio,
RadioGroup,
Select,
SelectItem,
SelectSection,
Slider,
Switch,
Tab,
Table,
Tabs,
Textarea,
User,
} from "@heroui/react";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { TimesIcon } from "@/components/icons"; import { TimesIcon } from "@/components/icons";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { close, error, loading } from "@/config/swal"; import { close, error, loading } from "@/config/swal";
import Swal from 'sweetalert2'; import Swal from "sweetalert2";
import withReactContent from 'sweetalert2-react-content'; import withReactContent from "sweetalert2-react-content";
import dynamic from 'next/dynamic'; import dynamic from "next/dynamic";
import { useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import * as z from "zod"; import * as z from "zod";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { createUserLevels } from "@/services/user-levels/user-levels-service"; import {
createUserLevels,
getAllUserLevels,
} from "@/services/user-levels/user-levels-service";
import ReactSelect from "react-select";
import makeAnimated from "react-select/animated";
const LevelList = [ const createArticleSchema = z.object({
{ label: "Admin", value: "admin", id: 1 }, name: z.string().min(2, {
{ label: "Super-Admin", value: "super-admin", id: 2 }, message: "Required",
{ label: "Kurator", value: "kurator", id: 3 }, }),
{ label: "Supervisor", value: "supervisor", id: 4 }, aliasName: z.string().optional(),
]; group: z.string().min(2, {
message: "Required",
}),
});
const moduleList = [ const groups = [
{ label: "Article", value: "article", id: 1 }, { id: 1, name: "Mabes", value: "mabes", level: 1 },
{ label: "Caption", value: "caption", id: 2 }, { id: 2, name: "Polda", value: "polda", level: 2 },
{ label: "Meme", value: "meme", id: 3 }, { id: 3, name: "Satker", value: "satker", level: 2 },
{ label: "Video", value: "video", id: 4 }, { id: 4, name: "Polres", value: "polres", level: 3 },
{ label: "Master Data", value: "master-data", id: 5 }, { id: 5, name: "Subdiv", value: "subdiv", level: 3 },
];
const provinceList = [
{ label: "NTT", value: "1", id: 1 },
{ label: "NTB", value: "2", id: 2 },
{ label: "Aceh", value: "3", id: 3 },
{ label: "Jawa Timur", value: "4", id: 4 },
{ label: "Jawa Barat", value: "5", id: 5 },
]; ];
export default function CreateMasterUserLevelForm() { export default function CreateMasterUserLevelForm() {
const router = useRouter(); const router = useRouter();
const JoditEditor = dynamic(() => import('jodit-react'), { ssr: false });
const MySwal = withReactContent(Swal); const MySwal = withReactContent(Swal);
const [isVisible, setIsVisible] = useState(false); const animatedComponents = makeAnimated();
const [tabs, setTabs] = useState<string>("personal-info") const [needApproval, setNeedApproval] = useState(false);
const editor = useRef(null); const [parentList, setParentList] = useState<any>([]);
const [content, setContent] = useState(''); const [selectedParent, setSelectedParent] = useState<any>();
const [haveChildren, setHaveChildren] = useState("no");
const [active, setTrue] = useState("true"); const formOptions = {
const [levelValue, setLevelValue] = useState<any>(""); resolver: zodResolver(createArticleSchema),
const handleTab = (tab: any) => { defaultValues: { name: "", group: "" },
setTabs(tab); };
type UserSettingSchema = z.infer<typeof createArticleSchema>;
const {
register,
control,
handleSubmit,
formState: { errors, isValid },
setValue,
getValues,
watch,
setError,
clearErrors,
} = useForm<UserSettingSchema>(formOptions);
const watchName = watch("name");
const generateSlug = (title: string) => {
return title
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, "")
.replace(/\s+/g, "-");
}; };
const handleActive = (e: React.ChangeEvent<HTMLInputElement>) => { useEffect(() => {
setTrue(e.target.value); setValue("aliasName", generateSlug(watchName));
}, [watchName]);
const findSelectedLevel = (group: string) => {
const selectedLevel = groups.find((a) => a.value === group);
return selectedLevel ? selectedLevel : null;
}; };
const handleHaveChildren = (e: React.ChangeEvent<HTMLInputElement>) => {
setHaveChildren(e.target.value);
};
let [files, setFiles] = useState<File[]>([]);
const removeFile = (name: string) => {
const arrayFile: File[] = [];
for (const element of files) {
if (element.name !== name) {
arrayFile.push(element);
}
}
setFiles(arrayFile);
};
const handleFileChange = (event: any) => {
const newFiles: FileList | null = event.target.files;
if (newFiles) {
const allowedExtensions = ['.doc', '.docx', '.pdf', '.ppt', '.pptx', '.xlsx', '.csv'];
let temp: File[] = [...files]; // Salin file-file yang sudah ada
for (let i = 0; i < newFiles.length; i++) {
const file = newFiles[i];
const fileExtension = file.name.split('.').pop()?.toLowerCase();
if (fileExtension && allowedExtensions.includes(`.${fileExtension}`)) {
temp.push(file);
} else {
alert('Format file tidak valid. Hanya file .doc, .docx, .ppt, .pptx, .xlsx, .csv atau .pdf yang diperbolehkan.');
}
}
setFiles(temp);
}
};
const toggleVisibility = () => setIsVisible(!isVisible);
const validationSchema = z.object({
name: z.string().min(1, { message: "Required" }),
aliasName: z.string().min(1, { message: "Required" }),
// levelNumber: z.string().min(1, { message: "Required" }),
});
const formOptions = { resolver: zodResolver(validationSchema) };
type ArticleSchema = z.infer<typeof validationSchema>;
const { register, handleSubmit, formState: { errors }, formState, setValue } = useForm<ArticleSchema>(formOptions);
const save = async (data: any) => { const save = async (data: any) => {
const selectedLevel = Number(Array.from(levelValue).pop()); const findParent = (group: string) => {
if (group === "mabes") {
return 0;
} else if (group === "polda" || group === "satker") {
if (group === "polda") {
return 1;
} else {
return 555;
}
} else {
return selectedParent?.id;
}
};
const request = { const request = {
name: data.name, name: data.name,
aliasName: data.aliasName, aliasName: data.aliasName,
levelNumber: selectedLevel, levelNumber: findSelectedLevel(data.group)?.level,
isActive: active, isActive: true,
group: data.group,
parentLevelId: findParent(data.group),
provinceId: 0,
isApprovalActive: needApproval,
}; };
console.log(request);
loading(); loading();
const res = await createUserLevels(request); const res = await createUserLevels(request);
if (res.error) { if (res.error) {
@ -120,8 +133,8 @@ export default function CreateMasterUserLevelForm() {
return false; return false;
} }
close(); close();
successSubmit("/admin/master/master-user-level") successSubmit("/admin/user-level");
} };
async function onSubmit(data: any) { async function onSubmit(data: any) {
MySwal.fire({ MySwal.fire({
@ -141,10 +154,10 @@ export default function CreateMasterUserLevelForm() {
function successSubmit(redirect: string) { function successSubmit(redirect: string) {
MySwal.fire({ MySwal.fire({
title: 'Sukses', title: "Sukses",
icon: 'success', icon: "success",
confirmButtonColor: '#3085d6', confirmButtonColor: "#3085d6",
confirmButtonText: 'OK', confirmButtonText: "OK",
}).then((result) => { }).then((result) => {
if (result.isConfirmed) { if (result.isConfirmed) {
router.push(redirect); router.push(redirect);
@ -152,262 +165,175 @@ export default function CreateMasterUserLevelForm() {
}); });
} }
useEffect(() => {
fetchCategory();
}, []);
const fetchCategory = async () => {
loading();
const request = {
limit: 10,
};
const res = await getAllUserLevels(request);
close();
if (res?.data?.data) {
setupParent(res?.data?.data);
}
};
const setupParent = (data: any) => {
const temp = [];
for (const element of data) {
temp.push({
id: element.id,
label: element.name,
value: element.aliasName,
});
}
setParentList(temp);
};
const selectedGroup = watch("group");
return ( return (
<div className="mx-3 my-5"> <form
<div className="flex flex-col gap-3 mb-4"> className="flex flex-col lg:flex-row gap-8 text-black p-8"
<Card className="w-full bg-white"> onSubmit={handleSubmit(onSubmit)}
<div className="w-full mr-2 p-5 "> >
<form method="POST" onSubmit={handleSubmit(onSubmit)}> <div className="w-full lg:w-1/2 bg-white rounded-lg p-8 flex flex-col gap-1 shadow-lg">
<> <p className="text-sm">
<div className="flex flex-row gap-1"> Name<span className="text-danger">*</span>
<div className="w-6/12 justify-start items-start mt-2 gap-3">
<div className="flex flex-col ">
<Input
type="text"
label="Nama"
id="name"
{...register("name")}
placeholder="Input Name"
labelPlacement="outside"
className=" font-semibold"
classNames={{
label: "!text-black",
input: "!text-black hover:!text-white focus:!text-white",
inputWrapper: "max-h-[40px] bg-transparant border text-white",
}}
startContent={
<div className="pointer-events-none flex items-center">
<span className="text-default-400 text-small"></span>
</div>
}
/>
{errors.name?.message && (
<p className="text-red-400 text-sm">
{errors.name?.message}
</p> </p>
)} <Controller
</div> control={control}
<div className="flex flex-col mt-3"> name="name"
render={({ field: { onChange, value } }) => (
<Input <Input
type="text" type="text"
label="Alias Name" id="name"
id="aliasName" placeholder=""
{...register("aliasName")} label=""
placeholder="Input Name" value={value}
onChange={onChange}
labelPlacement="outside" labelPlacement="outside"
className=" font-semibold" className="w-full "
classNames={{ classNames={{
label: "!text-black", inputWrapper: [
input: "!text-black hover:!text-white focus:!text-white", "border-1 rounded-lg",
inputWrapper: "max-h-[40px] bg-transparant border text-white", "dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
],
}} }}
startContent={ variant="bordered"
<div className="pointer-events-none flex items-center">
<span className="text-default-400 text-small"></span>
</div>
}
/> />
{errors.aliasName?.message && ( )}
<p className="text-red-400 text-sm"> />
{errors?.name && (
<p className="text-red-400 text-sm mb-3">{errors.name?.message}</p>
)}
<p className="text-sm mt-3">Alias Name</p>
<Controller
control={control}
name="aliasName"
render={({ field: { onChange, value } }) => (
<Input
type="text"
id="alias"
placeholder=""
label=""
isReadOnly
value={value}
onChange={onChange}
labelPlacement="outside"
className="w-full "
classNames={{
inputWrapper: [
"border-1 rounded-lg",
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
],
}}
variant="bordered"
/>
)}
/>
{errors?.aliasName && (
<p className="text-red-400 text-sm mb-3">
{errors.aliasName?.message} {errors.aliasName?.message}
</p> </p>
)} )}
</div> <p className="text-sm mt-3">
<div className="mt-3 "> Group<span className="text-danger">*</span>
<p className="text-black text-sm mb-1 font-semibold">Level Number</p> </p>
<Controller
control={control}
name="group"
render={({ field: { onChange, value } }) => (
<Select <Select
variant="bordered" variant="bordered"
label=""
labelPlacement="outside" labelPlacement="outside"
placeholder="Select Polda" selectedKeys={[value]}
selectedKeys={levelValue} onChange={onChange}
onSelectionChange={setLevelValue}
className="w-full"
classNames={{ classNames={{
mainWrapper: "rounded", trigger: [
listboxWrapper: "border-1 rounded-lg",
"bg-white w-full !text-indigo-500 text-center font-bold", "dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
popoverContent: "bg-white !text-indigo-500",
trigger:
"border-1 border-gray-200 hover:!bg-gray-100 !text-black",
}}
listboxProps={{
itemClasses: {
base: [
"!text-left",
"!bg-white",
"text-indigo-500 ",
"data-[selectable=true]:!text-indigo-500",
"data-[pressed=true]:text-indigo-500",
"data-[hover=true]:!text-indigo-300",
], ],
wrapper: ["!bg-white border-none"],
},
}} }}
> >
<SelectSection showDivider title="List Level"> {groups.map((group) => (
{LevelList.map((list: any) => ( <SelectItem key={group.value}>{group.name}</SelectItem>
<SelectItem key={list.id} value={list.id}>
{list.label}
</SelectItem>
))} ))}
</SelectSection>
</Select> </Select>
</div> )}
{/* <div className="mt-3 ">
<p className="text-black text-sm mb-1 font-semibold">Parent LevelId</p>
<Select
variant="bordered"
selectionMode="single"
labelPlacement="outside"
placeholder="Select"
className="font-semibold"
items={moduleList}
classNames={{
mainWrapper: "rounded",
listboxWrapper:
"bg-white w-full !text-indigo-500 text-center font-bold",
popoverContent: "bg-white !text-indigo-500",
trigger:
"border-1 border-gray-200 hover:!bg-gray-100 !text-black",
}}
listboxProps={{
itemClasses: {
base: [
"!text-left",
"!bg-white",
"text-indigo-500 ",
"data-[selectable=true]:!text-indigo-500",
"data-[pressed=true]:text-indigo-500",
"data-[hover=true]:!text-indigo-300",
],
wrapper: ["!bg-white border-none"],
},
}}
renderValue={(items) => {
return items.map((item) => (
<span key={item.props?.value} className="text-black text-xs">
{item.textValue}
</span>
));
}}
>
<SelectSection showDivider title="Module">
{moduleList.map((list) => (
<SelectItem key={list.id} value={list.id}>
{list.label}
</SelectItem>
))}
</SelectSection>
</Select>
</div>
<div className="mt-3 ">
<p className="text-black text-sm mb-1 font-semibold">Provinsi</p>
<Select
variant="bordered"
selectionMode="single"
labelPlacement="outside"
placeholder="Select"
className="font-semibold"
items={provinceList}
classNames={{
mainWrapper: "rounded",
listboxWrapper:
"bg-white w-full !text-indigo-500 text-center font-bold",
popoverContent: "bg-white !text-indigo-500",
trigger:
"border-1 border-gray-200 hover:!bg-gray-100 !text-black",
}}
listboxProps={{
itemClasses: {
base: [
"!text-left",
"!bg-white",
"text-indigo-500 ",
"data-[selectable=true]:!text-indigo-500",
"data-[pressed=true]:text-indigo-500",
"data-[hover=true]:!text-indigo-300",
],
wrapper: ["!bg-white border-none"],
},
}}
renderValue={(items) => {
return items.map((item) => (
<span key={item.props?.value} className="text-black text-xs">
{item.textValue}
</span>
));
}}
>
<SelectSection showDivider title="Provinsi">
{provinceList.map((list) => (
<SelectItem key={list.id} value={list.id}>
{list.label}
</SelectItem>
))}
</SelectSection>
</Select>
</div>
<div className="text-black">
<p className=" text-black ">Status</p>
<RadioGroup
orientation="horizontal"
id="radio-banned"
className="text-gray-950 mb-2"
onChange={handleActive}
defaultValue={active}
>
<Radio
classNames={{
label: "!text-black",
}}
value="true"
>
Active
</Radio>
<Radio
classNames={{
label: "!text-black",
}}
value="false"
>
Inactive
</Radio>
</RadioGroup>
</div> */}
<div className="flex flex-row gap-3 my-3">
<Link href="/admin/master/master-user-level">
<Button
variant="bordered"
className="bg-white border-grey-100 rounded-full text-[#8E5C18] mr-5"
>
Cancel
</Button>{" "}
</Link>
<Button
className="w-[50px]"
color="primary"
size="md"
type="submit">
Submit
</Button>
</div>
</div>
<div className="w-6/12 ">
<div className="flex items-center justify-center justify-items-center h-full">
<Image
width={400}
alt="NextUI hero Image"
src="/account-category.jpg"
/> />
</div> {errors?.name && (
</div> <p className="text-red-400 text-sm mb-3">{errors.name?.message}</p>
</div> )}
</>
</form>
</div>
</Card>
</div>
</div >
) <p className="text-sm mt-3">Need Approval</p>
<Switch isSelected={needApproval} onValueChange={setNeedApproval}>
<p className="text-sm text-black">
{needApproval ? "Active" : "Inactive"}
</p>
</Switch>
{findSelectedLevel(selectedGroup)?.level === 3 && (
<>
<p className="text-sm mt-3">Parent</p>
<ReactSelect
className="basic-single text-black z-50"
classNames={{
control: (state: any) =>
"!rounded-lg bg-white !border-1 !border-gray-200 dark:!border-stone-500",
}}
classNamePrefix="select"
onChange={setSelectedParent}
closeMenuOnSelect={false}
components={animatedComponents}
isClearable={true}
isSearchable={true}
isMulti={false}
placeholder=""
name="sub-module"
options={parentList}
/>
</>
)}
<div className="flex flex-row gap-3 mt-5">
<Button
color="primary"
type="submit"
isDisabled={
findSelectedLevel(selectedGroup)?.level === 3 && !selectedParent
}
>
Simpan
</Button>
<Link href="/admin/master-user-level">
<Button color="danger">Kembali</Button>
</Link>
</div>
</div>
</form>
);
} }

View File

@ -385,6 +385,26 @@ export const MasterRoleIcon = ({
/> />
</svg> </svg>
); );
export const MasterUserLevelIcon = ({
size,
height = 24,
width = 24,
fill = "currentColor",
...props
}: IconSvgProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size || width}
height={size || height}
viewBox="0 0 640 512"
{...props}
>
<path
fill="currentColor"
d="M192 256c61.9 0 112-50.1 112-112S253.9 32 192 32S80 82.1 80 144s50.1 112 112 112m76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C51.6 288 0 339.6 0 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2M480 256c53 0 96-43 96-96s-43-96-96-96s-96 43-96 96s43 96 96 96m48 32h-3.8c-13.9 4.8-28.6 8-44.2 8s-30.3-3.2-44.2-8H432c-20.4 0-39.2 5.9-55.7 15.4c24.4 26.3 39.7 61.2 39.7 99.8v38.4c0 2.2-.5 4.3-.6 6.4H592c26.5 0 48-21.5 48-48c0-61.9-50.1-112-112-112"
/>
</svg>
);
export const MasterCategoryIcon = ({ export const MasterCategoryIcon = ({
size, size,
height = 24, height = 24,

View File

@ -20,6 +20,7 @@ import {
MagazineIcon, MagazineIcon,
MasterCategoryIcon, MasterCategoryIcon,
MasterRoleIcon, MasterRoleIcon,
MasterUserLevelIcon,
MasterUsersIcon, MasterUsersIcon,
MinusCircleIcon, MinusCircleIcon,
StaticPageIcon, StaticPageIcon,
@ -243,6 +244,20 @@ const sideBarDummyData = [
statusName: "Active", statusName: "Active",
childModule: null, childModule: null,
}, },
{
id: 23,
name: "Master User Level",
moduleId: 656,
moduleName: "Form Validation",
modulePathUrl: "/admin/user-level",
parentId: -1,
icon: <MasterUserLevelIcon />,
position: 1,
statusId: 1,
childMenu: [],
statusName: "Active",
childModule: null,
},
]; ];
const SidebarMobile: React.FC<SidebarProps> = ({ updateSidebarData }) => { const SidebarMobile: React.FC<SidebarProps> = ({ updateSidebarData }) => {

View File

@ -20,6 +20,7 @@ import {
MagazineIcon, MagazineIcon,
MasterCategoryIcon, MasterCategoryIcon,
MasterRoleIcon, MasterRoleIcon,
MasterUserLevelIcon,
MasterUsersIcon, MasterUsersIcon,
MinusCircleIcon, MinusCircleIcon,
StaticPageIcon, StaticPageIcon,
@ -319,6 +320,20 @@ const sideBarDummyData = [
statusName: "Active", statusName: "Active",
childModule: null, childModule: null,
}, },
{
id: 23,
name: "Master User Level",
moduleId: 656,
moduleName: "Form Validation",
modulePathUrl: "/admin/user-level",
parentId: -1,
icon: <MasterUserLevelIcon />,
position: 1,
statusId: 1,
childMenu: [],
statusName: "Active",
childModule: null,
},
]; ];
const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => { const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {

View File

@ -30,6 +30,7 @@ import {
} from "@/components/icons"; } from "@/components/icons";
import Link from "next/link"; import Link from "next/link";
import { getAllUserLevels } from "@/services/user-levels/user-levels-service"; import { getAllUserLevels } from "@/services/user-levels/user-levels-service";
import { close, loading } from "@/config/swal";
type UserObject = { type UserObject = {
id: number; id: number;
@ -41,12 +42,6 @@ type UserObject = {
status: string; status: string;
}; };
const statusColorMap = {
active: "success",
paused: "danger",
vacation: "warning",
};
export default function MasterUserLevelTable() { export default function MasterUserLevelTable() {
const [totalPage, setTotalPage] = useState(1); const [totalPage, setTotalPage] = useState(1);
const [masterUserLevelTable, setmasterUserLevel] = useState<UserObject[]>([]); const [masterUserLevelTable, setmasterUserLevel] = useState<UserObject[]>([]);
@ -56,11 +51,11 @@ export default function MasterUserLevelTable() {
const columns = [ const columns = [
{ name: "No", uid: "no" }, { name: "No", uid: "no" },
{ name: "Name", uid: "name" }, { name: "Name", uid: "name" },
{ name: "User Name", uid: "alias_name" }, { name: "User Name", uid: "aliasName" },
{ name: "Level Number", uid: "level_number" }, { name: "Level Number", uid: "levelNumber" },
{ name: "Parent Level", uid: "parent_level_id" }, { name: "Parent", uid: "parentLevelId" },
{ name: "Province", uid: "province_id" }, // { name: "Province", uid: "province_id" },
{ name: "Status", uid: "is_active" }, // { name: "Status", uid: "is_active" },
{ name: "Action", uid: "actions" }, { name: "Action", uid: "actions" },
]; ];
@ -71,6 +66,7 @@ export default function MasterUserLevelTable() {
}, []); }, []);
async function fetchData() { async function fetchData() {
loading();
const request = { const request = {
page: page, page: page,
limit: 10, limit: 10,
@ -78,10 +74,11 @@ export default function MasterUserLevelTable() {
const res = await getAllUserLevels(request); const res = await getAllUserLevels(request);
const data = res?.data?.data; const data = res?.data?.data;
setTotalPage(Math.ceil(res?.data?.total / 10)); setTotalPage(Math.ceil(res?.data?.total / 10));
initUserData(10, data); await initUserData(10, data);
close();
} }
function initUserData(limit: number, data?: any) { async function initUserData(limit: number, data?: any) {
if (data) { if (data) {
console.log(data); console.log(data);
const startIndex = limit * (page - 1); const startIndex = limit * (page - 1);
@ -91,43 +88,34 @@ export default function MasterUserLevelTable() {
value.no = startIndex + iterate; value.no = startIndex + iterate;
return value; return value;
}); });
console.log("Data ::", newData);
setmasterUserLevel(newData); setmasterUserLevel(newData);
} }
} }
const findParentName = (data: string | number) => {
let name = "-";
for (let i = 9; i < masterUserLevelTable?.length; i++) {
const temp = masterUserLevelTable[i];
if (temp.id === Number(data)) {
name = temp.name;
break;
}
}
return name;
};
const renderCell = useCallback( const renderCell = useCallback(
(masterUserLevel: TableRow, columnKey: Key) => { (masterUserLevel: TableRow, columnKey: Key) => {
const cellValue = masterUserLevel[columnKey as keyof UserObject]; const cellValue = masterUserLevel[columnKey as keyof UserObject];
const statusColorMap: Record<string, ChipProps["color"]> = {
active: "success",
cancel: "danger",
pending: "warning",
};
switch (columnKey) { switch (columnKey) {
case "no": case "no":
return <div>{masterUserLevel.id}</div>; return <div>{masterUserLevel.id}</div>;
case "name": case "parentLevelId":
return <div className="w-[150px]">{masterUserLevel.name}</div>; return <p className="text-black">{findParentName(cellValue)}</p>;
case "alias_Name":
return <div className="">{masterUserLevel.aliasName}</div>;
case "is_active":
return (
<Chip
className="capitalize "
color={statusColorMap[masterUserLevel.status]}
size="lg"
variant="flat"
>
<div className="flex flex-row items-center gap-2 justify-center">
{cellValue}
</div>
</Chip>
);
case "actions": case "actions":
return ( return (
<div className="relative flex justify-star items-center gap-2"> <div className="relative flex justify-star items-center gap-2">
@ -138,14 +126,14 @@ export default function MasterUserLevelTable() {
</Button> </Button>
</DropdownTrigger> </DropdownTrigger>
<DropdownMenu> <DropdownMenu>
<DropdownItem key="Detail"> {/* <DropdownItem key="Detail">
<Link href={`/admin/magazine/detail`}> <Link href={`/admin/magazine/detail`}>
<EyeIconMdi className="inline mr-2 mb-1" /> <EyeIconMdi className="inline mr-2 mb-1" />
Detail Detail
</Link> </Link>
</DropdownItem> </DropdownItem> */}
<DropdownItem key="Edit"> <DropdownItem key="Edit">
<Link href={`#`}> <Link href={`/admin/user-level/edit/${masterUserLevel.id}`}>
<CreateIconIon className="inline mr-2 mb-1" /> <CreateIconIon className="inline mr-2 mb-1" />
Edit Edit
</Link> </Link>
@ -169,18 +157,18 @@ export default function MasterUserLevelTable() {
return cellValue; return cellValue;
} }
}, },
[] [masterUserLevelTable]
); );
return ( return (
<> <>
<div className="mx-3 my-5"> <div className="mx-3 my-5">
<Link href="/admin/master/master-user-level/create"> {/* <Link href="/admin/master/master-user-level/create">
<Button className="my-3 bg-blue-600 text-white"> <Button className="my-3 bg-blue-600 text-white">
<CreateIconIon /> <CreateIconIon />
Create New User Level Create New User Level
</Button> </Button>
</Link> </Link> */}
<div className="flex flex-col items-center rounded-2xl"> <div className="flex flex-col items-center rounded-2xl">
<Table <Table
// selectionMode="multiple" // selectionMode="multiple"

View File

@ -1,12 +1,21 @@
import { httpGet, httpPost } from "../http-config/http-base-services"; import {
httpDeleteInterceptor,
httpGet,
httpPost,
httpPut,
} from "@/service/http-config/axios-base-service";
export async function getAllUserLevels(data: any) { export async function getAllUserLevels(data: any) {
const headers = { const headers = {
"content-type": "application/json", "content-type": "application/json",
}; };
return await httpGet(`user-levels`, headers,); return await httpGet(`user-levels?limit=${data.limit || ""}`, headers);
}
export async function getUserLevels(id: string) {
const headers = {
"content-type": "application/json",
};
return await httpGet(`user-levels/${id}`, headers);
} }
export async function getAccountById(id: string) { export async function getAccountById(id: string) {
@ -14,7 +23,6 @@ export async function getAccountById(id: string) {
"content-type": "application/json", "content-type": "application/json",
}; };
return await httpGet(`user-account/findById/${id}`, headers); return await httpGet(`user-account/findById/${id}`, headers);
} }
export async function createUserLevels(request: any) { export async function createUserLevels(request: any) {
@ -24,3 +32,9 @@ export async function createUserLevels(request: any) {
return await httpPost(`user-levels`, headers, request); return await httpPost(`user-levels`, headers, request);
} }
export async function editUserLevels(id: string, request: any) {
const headers = {
"content-type": "application/json",
};
return await httpPut(`user-levels/${id}`, headers, request);
}