From a7c67cb77ffcf954f8b2ddbbd75c9486cb816a7c Mon Sep 17 00:00:00 2001 From: Hafid Nur Date: Mon, 7 Apr 2025 02:19:07 +0700 Subject: [PATCH] feat: add permissions and roles page - Tambah halaman permissions & roles beserta modal - Update Backoffice Sidebar - Update styling label pada input field --- .../_components/modal-add-permission.tsx | 132 ++++++++++++ .../_components/modal-delete-permission.tsx | 72 +++++++ .../_components/modal-update-permission.tsx | 62 ++++++ .../src/app/permissions/_hook/use-item.ts | 61 ++++++ .../backoffice/src/app/permissions/layout.tsx | 18 ++ apps/backoffice/src/app/permissions/page.tsx | 202 +++++++++++++++++ .../app/roles/_components/modal-add-role.tsx | 185 ++++++++++++++++ .../roles/_components/modal-delete-role.tsx | 72 +++++++ .../roles/_components/modal-update-role.tsx | 111 ++++++++++ .../src/app/roles/_hook/use-item.ts | 61 ++++++ apps/backoffice/src/app/roles/layout.tsx | 18 ++ apps/backoffice/src/app/roles/page.tsx | 203 ++++++++++++++++++ .../src/molecules/input-field/input-field.tsx | 4 +- .../backoffice-sidebar/backoffice-sidebar.tsx | 26 +++ 14 files changed, 1225 insertions(+), 2 deletions(-) create mode 100644 apps/backoffice/src/app/permissions/_components/modal-add-permission.tsx create mode 100644 apps/backoffice/src/app/permissions/_components/modal-delete-permission.tsx create mode 100644 apps/backoffice/src/app/permissions/_components/modal-update-permission.tsx create mode 100644 apps/backoffice/src/app/permissions/_hook/use-item.ts create mode 100644 apps/backoffice/src/app/permissions/layout.tsx create mode 100644 apps/backoffice/src/app/permissions/page.tsx create mode 100644 apps/backoffice/src/app/roles/_components/modal-add-role.tsx create mode 100644 apps/backoffice/src/app/roles/_components/modal-delete-role.tsx create mode 100644 apps/backoffice/src/app/roles/_components/modal-update-role.tsx create mode 100644 apps/backoffice/src/app/roles/_hook/use-item.ts create mode 100644 apps/backoffice/src/app/roles/layout.tsx create mode 100644 apps/backoffice/src/app/roles/page.tsx diff --git a/apps/backoffice/src/app/permissions/_components/modal-add-permission.tsx b/apps/backoffice/src/app/permissions/_components/modal-add-permission.tsx new file mode 100644 index 0000000..a3b6243 --- /dev/null +++ b/apps/backoffice/src/app/permissions/_components/modal-add-permission.tsx @@ -0,0 +1,132 @@ +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { Modal } from '@imphnen-frontend-service/ui/molecules'; +import { ControlledInputField } from '@imphnen-frontend-service/ui/organisms'; +import { useItem, useConfirmItem } from '../_hook/use-item'; + +interface IModalAddPermission { + isOpen: boolean; + onClose: () => void; + handleAddItem?: () => Promise; + currentStep?: number; + nextStep: () => void; + prevStep: () => void; + resetStep: () => void; +} + +const ModalAddPermission = ({ + isOpen, + onClose, + currentStep, + nextStep, + resetStep, + handleAddItem, +}: IModalAddPermission) => { + return ( + { + onClose(); + resetStep(); + }} + disableEscapeKeyDown={true} + > + {currentStep === 1 && } + {currentStep === 2 && ( + + )} + + ); +}; + +interface IStepOneProps { + nextStep: () => void; + onClose: () => void; +} + +const StepOne = ({ nextStep }: IStepOneProps) => { + const { form, onSubmit } = useItem(nextStep); + + return ( + <> + +

+ Tambah Permissions +

+
+ +
+ + + + +
+ + ); +}; + +interface IStepTwoProps { + onClose: () => void; + handleAddItem?: () => Promise; + resetStep: () => void; +} + +const StepTwo = ({ onClose, handleAddItem, resetStep }: IStepTwoProps) => { + const { onConfirm, onCancel } = useConfirmItem( + onClose, + resetStep, + handleAddItem, + { + success: 'Data permissions berhasil ditambahkan', + error: 'Data permissions gagal ditambahkan', + } + ); + + return ( + <> + +

+ Tambah Permissions +

+

+ Apakah kamu yakin ingin +
menambahkan permission ini? +

+
+ + + + + + ); +}; + +export default ModalAddPermission; diff --git a/apps/backoffice/src/app/permissions/_components/modal-delete-permission.tsx b/apps/backoffice/src/app/permissions/_components/modal-delete-permission.tsx new file mode 100644 index 0000000..ea96c09 --- /dev/null +++ b/apps/backoffice/src/app/permissions/_components/modal-delete-permission.tsx @@ -0,0 +1,72 @@ +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { Modal } from '@imphnen-frontend-service/ui/molecules'; +import { useConfirmItem } from '../_hook/use-item'; + +interface IModalDeletePermission { + isOpen: boolean; + onClose: () => void; + handleDelete?: () => Promise; + currentStep?: number; + nextStep: () => void; + prevStep: () => void; + resetStep: () => void; +} + +const ModalDeletePermission = ({ + isOpen, + onClose, + resetStep, + handleDelete, +}: IModalDeletePermission) => { + const { onConfirm } = useConfirmItem(onClose, resetStep, handleDelete, { + success: 'Data permissions berhasil dihapus', + error: 'Data permissions gagal dihapus', + }); + + return ( + + + Delete? +
+

+ Delete Permissions +

+

+ Apakah kamu yakin untuk menghapus permission ini? Menghapus data ini + mungkin akan mempengaruhi fungsional sistem +

+
+
+ + + + +
+ ); +}; + +export default ModalDeletePermission; diff --git a/apps/backoffice/src/app/permissions/_components/modal-update-permission.tsx b/apps/backoffice/src/app/permissions/_components/modal-update-permission.tsx new file mode 100644 index 0000000..985b125 --- /dev/null +++ b/apps/backoffice/src/app/permissions/_components/modal-update-permission.tsx @@ -0,0 +1,62 @@ +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { InputField, Modal } from '@imphnen-frontend-service/ui/molecules'; +import { useConfirmItem } from '../_hook/use-item'; + +interface IModalUpdatePermission { + isOpen: boolean; + onClose: () => void; + handleUpdate?: () => Promise; + currentStep?: number; + nextStep: () => void; + prevStep: () => void; + resetStep: () => void; +} + +const ModalUpdatePermission = ({ + isOpen, + onClose, + resetStep, + handleUpdate, +}: IModalUpdatePermission) => { + const { onConfirm } = useConfirmItem(onClose, resetStep, handleUpdate, { + success: 'Perubahan permissions berhasil dilakukan', + error: 'Perubahan permissions gagal dilakukan', + }); + + return ( + + +

+ Update Permissions +

+
+ + + + + +
+ ); +}; + +export default ModalUpdatePermission; diff --git a/apps/backoffice/src/app/permissions/_hook/use-item.ts b/apps/backoffice/src/app/permissions/_hook/use-item.ts new file mode 100644 index 0000000..7b9bcf0 --- /dev/null +++ b/apps/backoffice/src/app/permissions/_hook/use-item.ts @@ -0,0 +1,61 @@ +import { useForm } from 'react-hook-form'; +// import { zodResolver } from '@hookform/resolvers/zod'; +// import { +// gachaRollItemSchema, +// TGachaRollItem +// } from '@imphnen-frontend-service/service'; +import { toast } from 'sonner'; + +export const useItem = ( + nextStep: () => void, + initialValues?: any +) => { + const form = useForm({ + // resolver: zodResolver(), + mode: 'all', + defaultValues: initialValues, + }); + + const onSubmit = form.handleSubmit((data) => { + console.log('Form data:', data); + nextStep(); + }); + + return { + form, + onSubmit, + }; +}; + +export const useConfirmItem = ( + onClose: () => void, + resetStep: () => void, + actionFunction?: () => Promise, + messages?: { + success?: string; + error?: string; + } +) => { + const onConfirm = async () => { + try { + // const result = await actionFunction?.(); + // result ? toast.success(messages?.success) : toast.error(messages?.error); + toast.success(messages?.success); + onClose(); + resetStep(); + } catch (error) { + console.log(error); + toast.error(messages?.error); + } + }; + + const onCancel = () => { + onClose(); + resetStep(); + }; + + return { + onConfirm, + onCancel, + }; +}; diff --git a/apps/backoffice/src/app/permissions/layout.tsx b/apps/backoffice/src/app/permissions/layout.tsx new file mode 100644 index 0000000..a770c99 --- /dev/null +++ b/apps/backoffice/src/app/permissions/layout.tsx @@ -0,0 +1,18 @@ +import { FC, ReactElement } from 'react'; +import { Outlet } from 'react-router-dom'; +import { BackofficeSidebar } from '@imphnen-frontend-service/ui/organisms'; + +export const AppLayout: FC = (): ReactElement => { + return ( +
+
+ +
+ +
+
+
+ ); +}; + +export default AppLayout; diff --git a/apps/backoffice/src/app/permissions/page.tsx b/apps/backoffice/src/app/permissions/page.tsx new file mode 100644 index 0000000..95e7fb5 --- /dev/null +++ b/apps/backoffice/src/app/permissions/page.tsx @@ -0,0 +1,202 @@ +import { FC, Fragment, ReactElement, useState } from 'react'; +import { + SearchOutlined, + EditOutlined, + DeleteOutlined, + PlusOutlined, +} from '@ant-design/icons'; +import { Button, Input } from '@imphnen-frontend-service/ui/atoms'; +import { DataTable } from '@imphnen-frontend-service/ui/organisms'; +import { + ColumnDef, + getCoreRowModel, + getPaginationRowModel, + PaginationState, + RowSelectionState, + useReactTable, +} from '@tanstack/react-table'; +import ModalAddPermission from './_components/modal-add-permission'; +import ModalUpdatePermission from './_components/modal-update-permission'; +import ModalDeletePermission from './_components/modal-delete-permission'; +import { useQueryState } from '@imphnen-frontend-service/utils'; +import React from 'react'; + +interface Permission { + id: number; + name: string; +} + +const mockData: Permission[] = [ + { id: 1, name: 'Read' }, + { id: 2, name: 'Create' }, + { id: 3, name: 'Update' }, + { id: 4, name: 'Delete' }, +]; + +export const Components: FC = (): ReactElement => { + const [showModalAddItem, setShowModalAddItem] = useState(false); + const [showModalUpdateItem, setShowModalUpdateItem] = useState(false); + const [showModalDeleteItem, setShowModalDeleteItem] = useState(false); + + const { + step: currentStep, + nextStep, + prevStep, + resetStep, + } = useQueryState('step', { + defaultValue: 1, + maxValue: 2, + minValue: 1, + }); + + const [pagination, setPagination] = React.useState({ + pageIndex: 0, + pageSize: 9, + }); + + const [rowSelection, setRowSelection] = React.useState({}); + + const columns: ColumnDef[] = [ + { + id: 'select', + header: ({ table }) => ( + + ), + cell: ({ row }) => ( + + ), + }, + { + header: 'No', + accessorKey: 'id', + }, + { + header: 'Name', + accessorKey: 'name', + }, + { + header: 'Action', + cell: () => ( +
+ + +
+ ), + }, + ]; + + const table = useReactTable({ + data: mockData, + columns, + state: { + pagination, + rowSelection, + }, + enableRowSelection: true, + onRowSelectionChange: setRowSelection, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + onPaginationChange: setPagination, + pageCount: Math.ceil(mockData.length / pagination.pageSize), + manualPagination: false, + }); + + return ( + +
+
+

Permissions

+
+ +
+
+
+ +
+ +
+
+
+ +
+
+ + +
+
+ + setShowModalAddItem(false)} + nextStep={nextStep} + prevStep={prevStep} + resetStep={resetStep} + /> + setShowModalUpdateItem(false)} + nextStep={nextStep} + prevStep={prevStep} + resetStep={resetStep} + /> + setShowModalDeleteItem(false)} + nextStep={nextStep} + prevStep={prevStep} + resetStep={resetStep} + /> +
+ ); +}; + +export default Components; diff --git a/apps/backoffice/src/app/roles/_components/modal-add-role.tsx b/apps/backoffice/src/app/roles/_components/modal-add-role.tsx new file mode 100644 index 0000000..da66e16 --- /dev/null +++ b/apps/backoffice/src/app/roles/_components/modal-add-role.tsx @@ -0,0 +1,185 @@ +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { Modal } from '@imphnen-frontend-service/ui/molecules'; +import { ControlledInputField } from '@imphnen-frontend-service/ui/organisms'; +import { useItem, useConfirmItem } from '../_hook/use-item'; + +interface IModalAddRole { + isOpen: boolean; + onClose: () => void; + handleAdd?: () => Promise; + currentStep?: number; + nextStep: () => void; + prevStep: () => void; + resetStep: () => void; +} + +const ModalAddRole = ({ + isOpen, + onClose, + currentStep, + nextStep, + resetStep, + handleAdd, +}: IModalAddRole) => { + return ( + { + onClose(); + resetStep(); + }} + disableEscapeKeyDown={true} + > + {currentStep === 1 && } + {currentStep === 2 && ( + + )} + + ); +}; + +interface IStepOneProps { + nextStep: () => void; + onClose: () => void; +} + +const StepOne = ({ nextStep }: IStepOneProps) => { + const { form, onSubmit } = useItem(nextStep); + + return ( + <> + +

+ Tambah Roles +

+
+ +
+
+ +
+ +
+ + Permissions + +
+ {[ + 'Gacha Items', + 'Gacha Roll', + 'Roll', + 'Users', + 'Gacha Claim', + ].map((title) => ( +
+ {title} +
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ ))} +
+
+ + +
+
+ + ); +}; + +interface IStepTwoProps { + onClose: () => void; + handleAdd?: () => Promise; + resetStep: () => void; +} + +const StepTwo = ({ onClose, handleAdd, resetStep }: IStepTwoProps) => { + const { onConfirm, onCancel } = useConfirmItem( + onClose, + resetStep, + handleAdd, + { + success: 'Data role berhasil ditambahkan', + error: 'Data role gagal ditambahkan', + } + ); + + return ( + <> + +

+ Tambah Roles +

+

+ Apakah kamu yakin ingin +
menambahkan role ini? +

+
+ + + + + + ); +}; + +export default ModalAddRole; diff --git a/apps/backoffice/src/app/roles/_components/modal-delete-role.tsx b/apps/backoffice/src/app/roles/_components/modal-delete-role.tsx new file mode 100644 index 0000000..68c45e9 --- /dev/null +++ b/apps/backoffice/src/app/roles/_components/modal-delete-role.tsx @@ -0,0 +1,72 @@ +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { Modal } from '@imphnen-frontend-service/ui/molecules'; +import { useConfirmItem } from '../_hook/use-item'; + +interface IModalDeletePermission { + isOpen: boolean; + onClose: () => void; + handleDelete?: () => Promise; + currentStep?: number; + nextStep: () => void; + prevStep: () => void; + resetStep: () => void; +} + +const ModalDeletePermission = ({ + isOpen, + onClose, + resetStep, + handleDelete, +}: IModalDeletePermission) => { + const { onConfirm } = useConfirmItem(onClose, resetStep, handleDelete, { + success: 'Data roles berhasil dihapus', + error: 'Data roles gagal dihapus', + }); + + return ( + + + Delete? +
+

+ Delete Roles +

+

+ Apakah kamu yakin untuk menghapus role ini? Menghapus data ini + mungkin akan mempengaruhi fungsional sistem +

+
+
+ + + + +
+ ); +}; + +export default ModalDeletePermission; diff --git a/apps/backoffice/src/app/roles/_components/modal-update-role.tsx b/apps/backoffice/src/app/roles/_components/modal-update-role.tsx new file mode 100644 index 0000000..f2605c7 --- /dev/null +++ b/apps/backoffice/src/app/roles/_components/modal-update-role.tsx @@ -0,0 +1,111 @@ +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { InputField, Modal } from '@imphnen-frontend-service/ui/molecules'; +import { useConfirmItem } from '../_hook/use-item'; + +interface IModalUpdatePermission { + isOpen: boolean; + onClose: () => void; + handleUpdate?: () => Promise; + currentStep?: number; + nextStep: () => void; + prevStep: () => void; + resetStep: () => void; +} + +const ModalUpdatePermission = ({ + isOpen, + onClose, + resetStep, + handleUpdate, +}: IModalUpdatePermission) => { + const { onConfirm } = useConfirmItem(onClose, resetStep, handleUpdate, { + success: 'Perubahan roles berhasil dilakukan', + error: 'Perubahan roles gagal dilakukan', + }); + + return ( + + +

+ Update Roles +

+
+ +
+ +
+ +
+ + Permissions + +
+ {['Gacha Items', 'Gacha Roll', 'Roll', 'Users', 'Gacha Claim'].map( + (title) => ( +
+ {title} +
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ ) + )} +
+
+ + +
+
+ ); +}; + +export default ModalUpdatePermission; diff --git a/apps/backoffice/src/app/roles/_hook/use-item.ts b/apps/backoffice/src/app/roles/_hook/use-item.ts new file mode 100644 index 0000000..7b9bcf0 --- /dev/null +++ b/apps/backoffice/src/app/roles/_hook/use-item.ts @@ -0,0 +1,61 @@ +import { useForm } from 'react-hook-form'; +// import { zodResolver } from '@hookform/resolvers/zod'; +// import { +// gachaRollItemSchema, +// TGachaRollItem +// } from '@imphnen-frontend-service/service'; +import { toast } from 'sonner'; + +export const useItem = ( + nextStep: () => void, + initialValues?: any +) => { + const form = useForm({ + // resolver: zodResolver(), + mode: 'all', + defaultValues: initialValues, + }); + + const onSubmit = form.handleSubmit((data) => { + console.log('Form data:', data); + nextStep(); + }); + + return { + form, + onSubmit, + }; +}; + +export const useConfirmItem = ( + onClose: () => void, + resetStep: () => void, + actionFunction?: () => Promise, + messages?: { + success?: string; + error?: string; + } +) => { + const onConfirm = async () => { + try { + // const result = await actionFunction?.(); + // result ? toast.success(messages?.success) : toast.error(messages?.error); + toast.success(messages?.success); + onClose(); + resetStep(); + } catch (error) { + console.log(error); + toast.error(messages?.error); + } + }; + + const onCancel = () => { + onClose(); + resetStep(); + }; + + return { + onConfirm, + onCancel, + }; +}; diff --git a/apps/backoffice/src/app/roles/layout.tsx b/apps/backoffice/src/app/roles/layout.tsx new file mode 100644 index 0000000..a770c99 --- /dev/null +++ b/apps/backoffice/src/app/roles/layout.tsx @@ -0,0 +1,18 @@ +import { FC, ReactElement } from 'react'; +import { Outlet } from 'react-router-dom'; +import { BackofficeSidebar } from '@imphnen-frontend-service/ui/organisms'; + +export const AppLayout: FC = (): ReactElement => { + return ( +
+
+ +
+ +
+
+
+ ); +}; + +export default AppLayout; diff --git a/apps/backoffice/src/app/roles/page.tsx b/apps/backoffice/src/app/roles/page.tsx new file mode 100644 index 0000000..d01581d --- /dev/null +++ b/apps/backoffice/src/app/roles/page.tsx @@ -0,0 +1,203 @@ +import { FC, Fragment, ReactElement, useState } from 'react'; +import { + SearchOutlined, + EditOutlined, + DeleteOutlined, + PlusOutlined, +} from '@ant-design/icons'; +import { Button, Input } from '@imphnen-frontend-service/ui/atoms'; +import { DataTable } from '@imphnen-frontend-service/ui/organisms'; +import { + ColumnDef, + getCoreRowModel, + getPaginationRowModel, + PaginationState, + RowSelectionState, + useReactTable, +} from '@tanstack/react-table'; +import ModalAddRole from './_components/modal-add-role'; +import ModalUpdateRole from './_components/modal-update-role'; +import ModalDeleteRole from './_components/modal-delete-role'; +import { useQueryState } from '@imphnen-frontend-service/utils'; +import React from 'react'; + +interface Role { + id: number; + name: string; +} + +const mockData: Role[] = [ + { id: 1, name: 'Admin' }, + { id: 2, name: 'Admin Pembayaran' }, + { id: 3, name: 'Staff' }, + { id: 4, name: 'Staff Aktivasi User' }, + { id: 5, name: 'User' }, +]; + +export const Components: FC = (): ReactElement => { + const [showModalAddItem, setShowModalAddItem] = useState(false); + const [showModalUpdateItem, setShowModalUpdateItem] = useState(false); + const [showModalDeleteItem, setShowModalDeleteItem] = useState(false); + + const { + step: currentStep, + nextStep, + prevStep, + resetStep, + } = useQueryState('step', { + defaultValue: 1, + maxValue: 2, + minValue: 1, + }); + + const [pagination, setPagination] = React.useState({ + pageIndex: 0, + pageSize: 9, + }); + + const [rowSelection, setRowSelection] = React.useState({}); + + const columns: ColumnDef[] = [ + { + id: 'select', + header: ({ table }) => ( + + ), + cell: ({ row }) => ( + + ), + }, + { + header: 'ID', + accessorKey: 'id', + }, + { + header: 'Roles Name', + accessorKey: 'name', + }, + { + header: 'Action', + cell: () => ( +
+ + +
+ ), + }, + ]; + + const table = useReactTable({ + data: mockData, + columns, + state: { + pagination, + rowSelection, + }, + enableRowSelection: true, + onRowSelectionChange: setRowSelection, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + onPaginationChange: setPagination, + pageCount: Math.ceil(mockData.length / pagination.pageSize), + manualPagination: false, + }); + + return ( + +
+
+

Roles

+
+ +
+
+
+ +
+ +
+
+
+ +
+
+ + +
+
+ + setShowModalAddItem(false)} + nextStep={nextStep} + prevStep={prevStep} + resetStep={resetStep} + /> + setShowModalUpdateItem(false)} + nextStep={nextStep} + prevStep={prevStep} + resetStep={resetStep} + /> + setShowModalDeleteItem(false)} + nextStep={nextStep} + prevStep={prevStep} + resetStep={resetStep} + /> +
+ ); +}; + +export default Components; diff --git a/libs/ui/src/molecules/input-field/input-field.tsx b/libs/ui/src/molecules/input-field/input-field.tsx index 463be6c..08460ea 100644 --- a/libs/ui/src/molecules/input-field/input-field.tsx +++ b/libs/ui/src/molecules/input-field/input-field.tsx @@ -54,7 +54,7 @@ export const InputField: FC = ({