diff --git a/src/pages/master/profession/ProfessionMaster.tsx b/src/pages/master/profession/ProfessionMaster.tsx new file mode 100644 index 0000000..68fd122 --- /dev/null +++ b/src/pages/master/profession/ProfessionMaster.tsx @@ -0,0 +1,23 @@ +import { Container, DataGridInner } from '@/components'; +import { ManageProfessionContextProvider } from './hooks/ManageProfessionContext'; +import AddDialog from './blocks/AddDialog'; +import EditDialog from './blocks/EditDialog'; +import DeleteDialog from './blocks/DeleteDialog'; + +const ProfessionMaster = () => { + return ( + + +

Profession

+
+ +
+ + + +
+
+ ); +}; + +export default ProfessionMaster; diff --git a/src/pages/master/profession/blocks/AddDialog.tsx b/src/pages/master/profession/blocks/AddDialog.tsx new file mode 100644 index 0000000..2c71af7 --- /dev/null +++ b/src/pages/master/profession/blocks/AddDialog.tsx @@ -0,0 +1,130 @@ +import { getAuth } from '@/auth'; +import { Alert, useDataGrid } from '@/components'; +import { apiConfig } from '@/config/api.config'; +import { useCallApi } from '@/hooks'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { useManageProfessionContext } from '../hooks/useManageProfessionContext'; +import { toast } from 'sonner'; +import { + Dialog, + DialogBody, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; + +const API_URL = apiConfig.service_master_data; +const AddDialog = () => { + const parentRef = useRef(null); + const { reload } = useDataGrid(); + const { PostData } = useCallApi(); + const { showAddDialog, handleAddDialog } = useManageProfessionContext(); + const parsedUser = getAuth()?.user; + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + + const initialState = { + name: '', + created_by: '', + created_at: '' + }; + const [formField, setFormField] = useState(initialState); + const created_time = new Date(); + const formattedTime = created_time.toISOString().slice(0, 19).replace('T', ' '); + + const resetForm = () => { + setFormField(initialState); + setAlert({ show: false, message: '' }); + }; + + const doCreateProfession = useCallback(async (e: React.FormEvent) => { + e.preventDefault(); + + const response = await PostData(`${API_URL}/profession/create`, formField); + + if (response?.status) { + resetForm(); + handleAddDialog(false); + toast.success('Success Create Profession'); + reload(); + } else { + toast.error('Failed Create Profession'); + setAlert({ show: true, message: response?.message }); + } + }, []); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if (formField.name === '') { + setAlert({ show: true, message: 'Please fill name field.' }); + return; + } + + // doCreateProfession(e); + console.log(formField); + setAlert({ show: false, message: '' }); + }; + + useEffect(() => { + if (showAddDialog) { + setFormField({ + ...formField, + created_by: parsedUser.username, + created_at: formattedTime + }); + } + }, [formattedTime]); + + return ( + handleAddDialog(open)}> + + + Profession - Create + + + +
+ {alert.show && ( + +

{alert.message}

+
+ )} + +
+
+
+
+ + setFormField({ ...formField, name: e.target.value })} + /> +
+
+ +
+ + +
+
+
+
+
+
+
+ ); +}; + +export default AddDialog; diff --git a/src/pages/master/profession/blocks/DeleteDialog.tsx b/src/pages/master/profession/blocks/DeleteDialog.tsx new file mode 100644 index 0000000..0a74fc7 --- /dev/null +++ b/src/pages/master/profession/blocks/DeleteDialog.tsx @@ -0,0 +1,86 @@ +import { apiConfig } from '@/config/api.config'; +import { useManageProfessionContext } from '../hooks/useManageProfessionContext'; +import { Alert, useDataGrid } from '@/components'; +import { useCallApi } from '@/hooks'; +import { ChangeEvent, useCallback, useState } from 'react'; +import { toast } from 'sonner'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog'; +import { EnforceSwitch } from '@/components/switch'; +import { Button } from '@/components/ui/button'; + +const API_URL = apiConfig.service_master_data; +const DeleteDialog = () => { + const { showDeleteDialog, handleDeleteDialog, selectedProfession } = useManageProfessionContext(); + const { reload } = useDataGrid(); + const { DeleteData } = useCallApi(); + const [enforce, setEnforce] = useState(false); + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + + const doDeleteProfession = useCallback(async () => { + const response = await DeleteData( + `${API_URL}/profession/delete/${selectedProfession}/${enforce}`, + { + id: selectedProfession + } + ); + + if (response?.status) { + setAlert((prev) => ({ ...prev, show: false, message: '' })); + handleDeleteDialog(false, null); + toast.success('Success Delete Profession'); + reload(); + } else { + toast.error('Failed Delete Profession'); + setAlert((prev) => ({ ...prev, show: true, message: response?.message })); + } + }, [selectedProfession, enforce]); + + return ( + handleDeleteDialog(open, null)}> + + + + + +

Are you sure?

+ you will delete this data! +
+ + ) => { + setEnforce(e.target.checked); + }} + /> +
+
+ {alert.show && ( + +

{alert.message}

+
+ )} +
+ + + + +
+
+ ); +}; + +export default DeleteDialog; diff --git a/src/pages/master/profession/blocks/EditDialog.tsx b/src/pages/master/profession/blocks/EditDialog.tsx new file mode 100644 index 0000000..b8fe390 --- /dev/null +++ b/src/pages/master/profession/blocks/EditDialog.tsx @@ -0,0 +1,131 @@ +import { apiConfig } from '@/config/api.config'; +import { useManageProfessionContext } from '../hooks/useManageProfessionContext'; +import { Alert, useDataGrid } from '@/components'; +import { useCallApi } from '@/hooks'; +import { getAuth } from '@/auth'; +import React, { useCallback, useEffect, useState } from 'react'; +import { toast } from 'sonner'; +import { + Dialog, + DialogBody, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; + +const API_URL = apiConfig.service_master_data; +const EditDialog = () => { + const { showEditDialog, handleEditDialog, selectedProfession } = useManageProfessionContext(); + const { reload } = useDataGrid(); + const { PutData } = useCallApi(); + const parsedUser = getAuth()?.user; + const created_time = new Date(); + const formattedTime = created_time.toISOString().slice(0, 19).replace('T', ' '); + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + const initialState = { + name: '', + updated_by: '', + updated_at: '' + }; + const [formField, setFormField] = useState(initialState); + + const resetForm = () => { + setFormField(initialState); + setAlert({ show: false, message: '' }); + }; + + const doUpdateProfession = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + + const response = await PutData( + `${API_URL}/profession/update/${selectedProfession}`, + formField + ); + + if (response?.status) { + resetForm(); + handleEditDialog(false, null); + toast.success('Success Update Profession'); + reload(); + } else { + toast.error('Failed Update Profession'); + setAlert({ show: true, message: 'Failed Update Profession' }); + } + }, + [selectedProfession, formField] + ); + + const handleUpdate = (e: React.FormEvent) => { + e.preventDefault(); + + if (formField.name === '') { + setAlert({ show: true, message: 'Please fill name field.' }); + return; + } + + // doUpdateProfession(e); + console.log(formField); + setAlert({ show: false, message: '' }); + }; + + useEffect(() => { + if (showEditDialog) { + setFormField({ + ...formField, + updated_by: parsedUser.username, + updated_at: formattedTime + }); + } + }, [formattedTime]); + + return ( + handleEditDialog(open, null)}> + + + Profession - Update + + + +
+ {alert.show && ( + +

{alert.message}

+
+ )} + +
+
+
+
+ + setFormField({ ...formField, name: e.target.value })} + /> +
+
+ +
+ +
+
+
+
+
+
+
+ ); +}; + +export default EditDialog; diff --git a/src/pages/master/profession/blocks/ListToolbar.tsx b/src/pages/master/profession/blocks/ListToolbar.tsx new file mode 100644 index 0000000..b578b42 --- /dev/null +++ b/src/pages/master/profession/blocks/ListToolbar.tsx @@ -0,0 +1,57 @@ +import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components'; +import { useManageProfessionContext } from '../hooks/useManageProfessionContext'; +import { Button } from '@/components/ui/button'; + +const ListToolbar = () => { + const { reload, table } = useDataGrid(); + const { handleAddDialog } = useManageProfessionContext(); + + return ( +
+
+
+
+ + + + +
+
+ + + + +
+
+
+
+ ); +}; + +export default ListToolbar; diff --git a/src/pages/master/profession/hooks/ManageProfessionContext.tsx b/src/pages/master/profession/hooks/ManageProfessionContext.tsx new file mode 100644 index 0000000..d77eabe --- /dev/null +++ b/src/pages/master/profession/hooks/ManageProfessionContext.tsx @@ -0,0 +1,177 @@ +import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components'; +import { apiConfig } from '@/config/api.config'; +import { useCallApi } from '@/hooks'; +import { ColumnDef } from '@tanstack/react-table'; +import React, { createContext, useCallback, useMemo, useState } from 'react'; +import { Toaster } from 'sonner'; +import ListToolbar from '../blocks/ListToolbar'; + +interface ProfessionProps { + name: string; +} + +interface ContextProps { + profession: ProfessionProps[]; + showEditDialog: boolean; + handleEditDialog: (show: boolean, selected_sucos: string | null) => void; + showAddDialog: boolean; + handleAddDialog: (show: boolean) => void; + showDeleteDialog: boolean; + handleDeleteDialog: (show: boolean, selected_sucos: string | null) => void; + selectedProfession: string | null; + getProfessionLists: ( + limit: number, + page: number, + with_deleted: boolean, + order_field: any, + order_direction: any + ) => Promise<{ data: ProfessionProps[]; totalCount: number } | undefined>; +} + +const initialProps: ContextProps = { + profession: [], + showEditDialog: false, + handleEditDialog: () => {}, + showAddDialog: false, + handleAddDialog: () => {}, + showDeleteDialog: false, + handleDeleteDialog: () => {}, + selectedProfession: null, + getProfessionLists: async () => undefined +}; + +const ManageProfessionContext = createContext(initialProps); +const API_URL = apiConfig.service_master_data; + +const ManageProfessionContextProvider = ({ children }: { children: React.ReactNode }) => { + const [profession, setProfession] = useState([]); + const [showAddDialog, setShowAddDialog] = useState(false); + const [showEditDialog, setShowEditDialog] = useState(false); + const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const [selectedProfession, setSelectedProfession] = useState(null); + const { GetData } = useCallApi(); + + const handleAddDialog = useCallback((show: boolean) => { + setShowAddDialog(show); + }, []); + + const handleEditDialog = useCallback((show: boolean, selected_profession: string | null) => { + setSelectedProfession(show ? selected_profession : null); + setShowEditDialog(show); + }, []); + + const handleDeleteDialog = useCallback((show: boolean, selected_profession: string | null) => { + setSelectedProfession(show ? selectedProfession : null); + setShowDeleteDialog(show); + }, []); + + const columns = useMemo[]>( + () => [ + { + accessorFn: (row) => row.id, + id: 'id', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[100px]' + } + }, + { + accessorFn: (row) => row.name, + id: 'name', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + id: 'actions', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + cell: (data) => { + const row = data.row.original; + return ( + <> + + + + ); + }, + meta: { + headerClassName: 'w-[100px]', + cellClassName: 'text-center' + } + } + ], + [handleEditDialog, handleDeleteDialog] + ); + + const getProfessionLists = async (page: number, limit: number, sorting: any, filter: any) => { + try { + sorting = sorting.length == 0 ? [{ id: 'name', desc: false }] : sorting; + filter = filter.length == 0 ? {} : { any: filter[0].value?.toLowerCase() }; + const response = await GetData(`${API_URL}/profession/list`, { + limit, + page: page + 1, + with_deleted: true, + order_field: sorting[0].id, + order_direction: sorting[0].desc == false ? 'ASC' : 'DESC', + filter: JSON.stringify(filter) + }); + console.log(response?.data); + setProfession(response?.data.list); + return { data: response?.data.list, totalCount: response?.data.total_count }; + } catch (error) { + console.error('Error fetching Profession', error); + } + }; + + return ( + + + + } + layout={{ card: true }} + sorting={[{ id: 'id', desc: false }]} + serverSide={true} + onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => + getProfessionLists(pageIndex, pageSize, sorting, columnFilters) + } + > + {children} + + + ); +}; + +export { ManageProfessionContextProvider, ManageProfessionContext }; +export type { ProfessionProps }; diff --git a/src/pages/master/profession/hooks/useManageProfessionContext.tsx b/src/pages/master/profession/hooks/useManageProfessionContext.tsx new file mode 100644 index 0000000..8fe04af --- /dev/null +++ b/src/pages/master/profession/hooks/useManageProfessionContext.tsx @@ -0,0 +1,12 @@ +import { useContext } from 'react'; +import { ManageProfessionContext } from './ManageProfessionContext'; + +const useManageProfessionContext = () => { + const context = useContext(ManageProfessionContext); + + if (!context) throw new Error('useManageProfessionContext must be used within AuthProvider'); + + return context; +}; + +export { useManageProfessionContext };