diff --git a/.gitignore b/.gitignore index edc2fb7..baee0cd 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ lerna-debug.log* node_modules dist dist-ssr +yarn.lock *.local # Editor directories and files diff --git a/src/pages/master/aldeias/AldeiasMaster.tsx b/src/pages/master/aldeias/AldeiasMaster.tsx index 851f5f1..7ec7c77 100644 --- a/src/pages/master/aldeias/AldeiasMaster.tsx +++ b/src/pages/master/aldeias/AldeiasMaster.tsx @@ -1,10 +1,22 @@ +import { Container, DataGridInner } from '@/components'; +import { ManageAldeiasContextProvider } from './hooks/ManageAldeiasContext'; +import AddDialog from './blocks/AddDialog'; +import EditDialog from './blocks/EditDialog'; +import DeleteDialog from './blocks/DeleteDialog'; + const AldeiasMaster = () => { return ( -
-
-

Aldeias Master Data

-
-
+ + +

Aldeias

+
+ +
+ + + +
+
); }; diff --git a/src/pages/master/aldeias/blocks/AddDialog.tsx b/src/pages/master/aldeias/blocks/AddDialog.tsx new file mode 100644 index 0000000..66286dd --- /dev/null +++ b/src/pages/master/aldeias/blocks/AddDialog.tsx @@ -0,0 +1,152 @@ +import { apiConfig } from '@/config/api.config'; +import { useManageAldeiasContext } from '../hooks/useManageAldeiasContext'; +import { Alert, useDataGrid } from '@/components'; +import { useCallApi } from '@/hooks'; +import { getAuth } from '@/auth'; +import React, { useCallback, useEffect, useRef, 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 AddDialog = () => { + const parentRef = useRef(null); + const { showAddDialog, handleAddDialog } = useManageAldeiasContext(); + const { reload } = useDataGrid(); + const { PostData } = useCallApi(); + const parsedUser = getAuth()?.user; + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + const initialState = { + name: '', + sucos: 0, + 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 doCreateAldeias = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + + const response = await PostData(`${API_URL}/aldeias/create`, formField); + + if (response?.status) { + resetForm(); + handleAddDialog(false); + toast.success('Success Create Aldeias'); + reload(); + } else { + toast.error('Error Create Aldeias'); + setAlert({ show: true, message: 'Failed to create Aldeias. Please try again.' }); + } + }, + [formField] + ); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if (formField.name === '' || formField.sucos === 0) { + setAlert({ show: true, message: 'Please fill in all required fields.' }); + return; + } + + // doCreateAldeias(e); + console.log(formField); + setAlert({ show: false, message: '' }); + }; + + useEffect(() => { + if (showAddDialog) { + setFormField({ + ...formField, + created_by: parsedUser.username, + created_at: formattedTime + }); + } + }, [formattedTime]); + + return ( + handleAddDialog(open)}> + + + Aldeias - Create + + + +
+ {alert.show && ( + +

{alert.message}

+
+ )} + +
+
+
+
+ + setFormField({ ...formField, name: e.target.value })} + /> +
+
+ +
+
+ + { + const value = parseInt(e.target.value, 10); + setFormField({ ...formField, sucos: isNaN(value) ? 0 : value }); + }} + /> +
+
+ +
+ + +
+
+
+
+
+
+
+ ); +}; + +export default AddDialog; diff --git a/src/pages/master/aldeias/blocks/DeleteDialog.tsx b/src/pages/master/aldeias/blocks/DeleteDialog.tsx new file mode 100644 index 0000000..44fc4c1 --- /dev/null +++ b/src/pages/master/aldeias/blocks/DeleteDialog.tsx @@ -0,0 +1,74 @@ +import { apiConfig } from '@/config/api.config'; +import { useManageAldeiasContext } from '../hooks/useManageAldeiasContext'; +import { Alert, useDataGrid } from '@/components'; +import { useCallApi } from '@/hooks'; +import { ChangeEvent, useCallback, useState } from 'react'; +import { toast } from 'sonner'; +import { Dialog, DialogContent, DialogFooter, DialogHeader } 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, selectedAldeias } = useManageAldeiasContext(); + const { reload } = useDataGrid(); + const { DeleteData } = useCallApi(); + const [enforce, setEnforce] = useState(false); + const [alert, setAlert] = useState({ + show: false, + message: '' + }) + + const doDeleteAldeias = useCallback(async () => { + const response = await DeleteData(`${API_URL}/aldeias/delete/${selectedAldeias}/${enforce}`, { + id: selectedAldeias + }); + + if (response?.status) { + setAlert((prev) => ({ ...prev, show: false, message: '' })); + handleDeleteDialog(false, null); + toast.success('Success Delete Aldeias'); + reload(); + } else { + toast.error('Failed Delete Aldeias'); + setAlert((prev) => ({ ...prev, show: true, message: response?.message })); + } + }, [selectedAldeias, 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/aldeias/blocks/EditDialog.tsx b/src/pages/master/aldeias/blocks/EditDialog.tsx new file mode 100644 index 0000000..d34e30c --- /dev/null +++ b/src/pages/master/aldeias/blocks/EditDialog.tsx @@ -0,0 +1,148 @@ +import { apiConfig } from '@/config/api.config'; +import { useManageAldeiasContext } from '../hooks/useManageAldeiasContext'; +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, selectedAldeias } = useManageAldeiasContext(); + const { reload } = useDataGrid(); + const { PutData } = useCallApi(); + const parsedUser = getAuth()?.user; + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + const initialState = { + name: '', + sucos: 0, + updated_by: '', + updated_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 doUpdateAldeias = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + + const response = await PutData(`${API_URL}/aldeias/update/${selectedAldeias}`, formField); + + if (response?.status) { + resetForm(); + handleEditDialog(false, null); + toast.success('Success Update Aldeias'); + reload(); + } else { + toast.error('Error Update Aldeias'); + setAlert({ show: true, message: 'Error Update Aldeias' }); + } + }, + [selectedAldeias, formField] + ); + + const handleUpdate = (e: React.FormEvent) => { + e.preventDefault(); + + if (formField.name === '' || formField.sucos === 0) { + setAlert({ show: true, message: 'Please fill in all required fields.' }); + return; + } + + // doUpdateAldeias(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)}> + + + Aldeias - Update + + + +
+ {alert.show && ( + +

{alert.message}

+
+ )} + +
+
+
+
+ + setFormField({ ...formField, name: e.target.value })} + /> +
+
+ +
+
+ + { + const value = parseInt(e.target.value, 10); + setFormField({ ...formField, sucos: isNaN(value) ? 0 : value }); + }} + /> +
+
+ +
+ +
+
+
+
+
+
+
+ ); +}; + +export default EditDialog; diff --git a/src/pages/master/aldeias/blocks/ListToolbar.tsx b/src/pages/master/aldeias/blocks/ListToolbar.tsx index e69de29..7cc5369 100644 --- a/src/pages/master/aldeias/blocks/ListToolbar.tsx +++ b/src/pages/master/aldeias/blocks/ListToolbar.tsx @@ -0,0 +1,62 @@ +import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components'; +import { Button } from '@/components/ui/button'; +import { useManageAldeiasContext } from '../hooks/useManageAldeiasContext'; + +const ListToolbar = () => { + const { table, reload } = useDataGrid(); + const { handleAddDialog, handleSearchDialog } = useManageAldeiasContext(); + + return ( +
+
+
+
+ + + + + +
+
+ + + + +
+
+
+
+ ); +}; + +export default ListToolbar; diff --git a/src/pages/master/aldeias/hooks/ManageAldeiasContext.tsx b/src/pages/master/aldeias/hooks/ManageAldeiasContext.tsx new file mode 100644 index 0000000..d4cf6d3 --- /dev/null +++ b/src/pages/master/aldeias/hooks/ManageAldeiasContext.tsx @@ -0,0 +1,196 @@ +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 AldeiasProps { + id: number; + name: string; +} + +interface ContextProps { + aldeias: AldeiasProps[]; + showSearchDialog: boolean; + handleSearchDialog: (show: boolean) => void; + showEditDialog: boolean; + handleEditDialog: (show: boolean, selected_postoAdms: string | null) => void; + showAddDialog: boolean; + handleAddDialog: (show: boolean) => void; + showDeleteDialog: boolean; + handleDeleteDialog: (show: boolean, selected_postoAdms: string | null) => void; + selectedAldeias: string | null; + getAldeiasLists: ( + limit: number, + page: number, + with_deleted: boolean, + order_field: any, + order_direction: any + ) => Promise<{ data: AldeiasProps[]; totalCount: number } | undefined>; +} + +const initialProps: ContextProps = { + aldeias: [], + showSearchDialog: false, + handleSearchDialog: () => {}, + showEditDialog: false, + handleEditDialog: () => {}, + showAddDialog: false, + handleAddDialog: () => {}, + showDeleteDialog: false, + handleDeleteDialog: () => {}, + selectedAldeias: null, + getAldeiasLists: async () => undefined +}; + +const ManageAldeiasContext = createContext(initialProps); +const API_URL = apiConfig.service_master_data; + +const ManageAldeiasContextProvider = ({ children }: { children: React.ReactNode }) => { + const [aldeias, setAldeias] = useState([]); + const [showSearchDialog, setShowSearchDialog] = useState(false); + const [showAddDialog, setShowAddDialog] = useState(false); + const [showEditDialog, setShowEditDialog] = useState(false); + const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const [selectedAldeias, setSelectedAldeias] = useState(null); + const { GetData } = useCallApi(); + + const handleAddDialog = useCallback((show: boolean) => { + setShowAddDialog(show); + }, []); + + const handleEditDialog = useCallback((show: boolean, selected_aldeias: string | null) => { + setSelectedAldeias(show ? selected_aldeias : null); + setShowEditDialog(show); + }, []); + + const handleDeleteDialog = useCallback((show: boolean, selected_aldeias: string | null) => { + setSelectedAldeias(show ? selected_aldeias : null); + setShowDeleteDialog(show); + }, []); + + const handleSearchDialog = useCallback((show: boolean) => { + setShowSearchDialog(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]' + } + }, + { + accessorFn: (row) => row.sucos.name, + id: 'sucos', + 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' + } + } + ], + [] + ); + + const getAldeiasLists = async (page: number, limit: number, sorting: any, filter: any) => { + try { + const response = await GetData(`${API_URL}/aldeias/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); + setAldeias(response?.data.list); + return { data: response?.data.list, totalCount: response?.data.total_count }; + } catch (error) { + console.error('Error fetching Aldeias', error); + } + }; + + return ( + + + } + layout={{ card: true }} + sorting={[{ id: 'id', desc: false }]} + serverSide={true} + onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => + getAldeiasLists(pageIndex, pageSize, sorting, columnFilters) + } + > + {children} + + + ); +}; + +export { ManageAldeiasContext, ManageAldeiasContextProvider }; +export type { AldeiasProps }; diff --git a/src/pages/master/aldeias/hooks/useManageAldeiasContext.tsx b/src/pages/master/aldeias/hooks/useManageAldeiasContext.tsx new file mode 100644 index 0000000..c042a04 --- /dev/null +++ b/src/pages/master/aldeias/hooks/useManageAldeiasContext.tsx @@ -0,0 +1,12 @@ +import { useContext } from 'react'; +import { ManageAldeiasContext } from './ManageAldeiasContext'; + +const useManageAldeiasContext = () => { + const context = useContext(ManageAldeiasContext); + + if (!context) throw new Error('useManageAldeiasContext must be used within AuthProvider'); + + return context; +}; + +export { useManageAldeiasContext }; diff --git a/src/pages/master/municipios/blocks/AddDialog.tsx b/src/pages/master/municipios/blocks/AddDialog.tsx index 9a9a342..234bd0c 100644 --- a/src/pages/master/municipios/blocks/AddDialog.tsx +++ b/src/pages/master/municipios/blocks/AddDialog.tsx @@ -37,6 +37,9 @@ const AddDialog = () => { }; const [formField, setFormField] = useState(initialState); + const created_time = new Date(); + const formattedTime = created_time.toISOString().slice(0, 19).replace('T', ' '); + const resetForm = () => { setFormField(initialState); }; @@ -49,8 +52,8 @@ const AddDialog = () => { if (response?.status) { handleAddDialog(false); resetForm(); - toast.success('Municipio created successfully!'); reload(); + toast.success('Municipio created successfully!'); // const createActivity = { // module: 'Manage Municipio', // description: `Create Municipio => ${selectedMunicipios}`, @@ -80,27 +83,26 @@ const AddDialog = () => { // created_at: formattedTime // }); - // doCreateMunicipio(e); + doCreateMunicipio(e); console.log(parsedUser.email); console.log(formField); setAlert({ show: false, message: '' }); }; const handleReset = () => { - setFormField(initialState); + resetForm(); + setAlert({ show: false, message: '' }); }; useEffect(() => { - const created_time = new Date(); - const formattedTime = created_time.toISOString().slice(0, 19).replace('T', ' '); if (showAddDialog) { setFormField({ name: formField.name, - created_by: parsedUser.email, + created_by: parsedUser?.username, created_at: formattedTime }); } - }, [showAddDialog]); + }, [formattedTime]); return ( handleAddDialog(open)}> diff --git a/src/pages/master/municipios/blocks/EditDialog.tsx b/src/pages/master/municipios/blocks/EditDialog.tsx index afb9c3c..0579d28 100644 --- a/src/pages/master/municipios/blocks/EditDialog.tsx +++ b/src/pages/master/municipios/blocks/EditDialog.tsx @@ -39,6 +39,8 @@ const EditDialog = () => { }; const [formField, setFormField] = useState(initialState); + const created_time = new Date(); + const formattedTime = created_time.toISOString().slice(0, 19).replace('T', ' '); const resetForm = () => { setFormField(initialState); @@ -96,8 +98,6 @@ const EditDialog = () => { // }, []); useEffect(() => { - const created_time = new Date(); - const formattedTime = created_time.toISOString().slice(0, 19).replace('T', ' '); if (selectedMunicipios) { setFormField({ name: formField.name, @@ -105,7 +105,7 @@ const EditDialog = () => { updated_at: formattedTime }); } - }, [selectedMunicipios]); + }, [formattedTime]); // console.log(selectedMunicipios); return ( diff --git a/src/pages/master/postoadms/PostoAdmsMaster.tsx b/src/pages/master/postoadms/PostoAdmsMaster.tsx index 829f5dd..3ff1138 100644 --- a/src/pages/master/postoadms/PostoAdmsMaster.tsx +++ b/src/pages/master/postoadms/PostoAdmsMaster.tsx @@ -1,3 +1,6 @@ +import AddDialog from './blocks/AddDialog'; +import DeleteDialog from './blocks/DeleteDialog'; +import EditDialog from './blocks/EditDialog'; import SearchDialog from './blocks/SearchDialog'; import { ManagePostoAdmsContextProvider } from './hooks/ManagePostoAdmsContext'; import { Container, DataGridInner } from '@/components'; @@ -6,10 +9,15 @@ const PostoAdmsMaster = () => { return ( -

Postu Administrativo

+

+ Postu Administrativo +

+ + +
diff --git a/src/pages/master/postoadms/blocks/AddDialog.tsx b/src/pages/master/postoadms/blocks/AddDialog.tsx new file mode 100644 index 0000000..6e47f25 --- /dev/null +++ b/src/pages/master/postoadms/blocks/AddDialog.tsx @@ -0,0 +1,155 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { useManagePostoAdmsContext } from '../hooks/useManagePostoAdmsContext'; +import { Alert, KeenIcon, useDataGrid } from '@/components'; +import { useCallApi } from '@/hooks'; +import { getAuth, useAuthContext } from '@/auth'; +import { apiConfig } from '@/config/api.config'; +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 { showAddDialog, handleAddDialog } = useManagePostoAdmsContext(); + const { reload } = useDataGrid(); + const { PostData } = useCallApi(); + const parsedUser = getAuth()?.user; + + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + + const initialState = { + name: '', + municipio_id: 0, + 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 doCreatePostoAdm = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + + const response = await PostData(`${API_URL}/postoadms/create`, formField); + + if (response?.status) { + handleAddDialog(false); + resetForm(); + reload(); + toast.success('Posto Adm created successfully!'); + } else { + toast.error('Failed to create Posto Adm. Please try again.'); + setAlert({ show: true, message: 'Failed to create Posto Adm. Please try again.' }); + } + }, + [formField] + ); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if (formField.name === '' || formField.municipio_id === 0) { + setAlert({ show: true, message: 'Please fill in all required fields.' }); + return; + } + + doCreatePostoAdm(e); + console.log(formField); + setAlert({ show: false, message: '' }); + }; + + useEffect(() => { + if (showAddDialog) { + setFormField({ + ...formField, + created_by: parsedUser?.username, + created_at: formattedTime + }); + } + }, [formattedTime]); + + return ( + handleAddDialog(open)}> + + + Postu Administrativo - Create + + + +
+ {alert.show && ( + +

{alert.message}

+
+ )} + +
+
+
+
+ + setFormField({ ...formField, name: e.target.value })} + /> +
+
+ +
+
+ + { + const value = parseInt(e.target.value, 10); + setFormField({ ...formField, municipio_id: isNaN(value) ? 0 : value }); + }} + /> +
+
+ +
+ + +
+
+
+
+
+
+
+ ); +}; + +export default AddDialog; diff --git a/src/pages/master/postoadms/blocks/DeleteDialog.tsx b/src/pages/master/postoadms/blocks/DeleteDialog.tsx new file mode 100644 index 0000000..f969485 --- /dev/null +++ b/src/pages/master/postoadms/blocks/DeleteDialog.tsx @@ -0,0 +1,77 @@ +import { Alert, useDataGrid } from '@/components'; +import { useManagePostoAdmsContext } from '../hooks/useManagePostoAdmsContext'; +import { useCallApi } from '@/hooks'; +import { ChangeEvent, useCallback, useState } from 'react'; +import { apiConfig } from '@/config/api.config'; +import { toast } from 'sonner'; +import { Dialog, DialogContent, DialogFooter, DialogHeader } 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, selectedPostoAdms } = useManagePostoAdmsContext(); + const { reload } = useDataGrid(); + const { DeleteData } = useCallApi(); + const [enforce, setEnforce] = useState(false); + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + + const doDeletePostoAdm = useCallback(async () => { + const response = await DeleteData( + `${API_URL}/postoadms/delete/${selectedPostoAdms}/${enforce}`, + { + id: selectedPostoAdms + } + ); + + if (response?.status) { + setAlert((prev) => ({ ...prev, show: false, message: '' })); + handleDeleteDialog(false, null); + toast.success('Success Delete Posto Adm'); + reload(); + } else { + setAlert((prev) => ({ ...prev, show: true, message: response?.message })); + } + }, [selectedPostoAdms, 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/postoadms/blocks/EditDialog.tsx b/src/pages/master/postoadms/blocks/EditDialog.tsx new file mode 100644 index 0000000..8513d83 --- /dev/null +++ b/src/pages/master/postoadms/blocks/EditDialog.tsx @@ -0,0 +1,151 @@ +import { useCallback, useEffect, useRef, useState } from 'react'; +import { useManagePostoAdmsContext } from '../hooks/useManagePostoAdmsContext'; +import { Alert, useDataGrid } from '@/components'; +import { useCallApi } from '@/hooks'; +import { getAuth } from '@/auth'; +import { apiConfig } from '@/config/api.config'; +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 parentRef = useRef(null); + const { showEditDialog, handleEditDialog, selectedPostoAdms } = useManagePostoAdmsContext(); + const { reload } = useDataGrid(); + const { PutData } = useCallApi(); + const parsedUser = getAuth()?.user; + + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + const initialState = { + name: '', + municipio_id: 0, + updated_by: '', + updated_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 doUpdatePostoAdm = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + + const response = await PutData(`${API_URL}/postoadms/update/${selectedPostoAdms}`, formField); + + if (response?.status) { + handleEditDialog(false, null); + resetForm(); + toast.success('Success Update Posto Adm'); + reload(); + } else { + toast.error('Error Update Posto Adm'); + setAlert({ show: true, message: 'Failed to update posto adm. Please try again.' }); + } + }, + [selectedPostoAdms, formField] + ); + + const handleUpdate = (e: React.FormEvent) => { + e.preventDefault(); + + if (formField.name === '' || formField.municipio_id === 0) { + setAlert({ show: true, message: 'Please fill in all required fields.' }); + return; + } + + doUpdatePostoAdm(e); + console.log(formField); + setAlert({ show: false, message: '' }); + }; + + useEffect(() => { + if (selectedPostoAdms) { + setFormField({ + ...formField, + updated_by: parsedUser?.username, + updated_at: formattedTime + }); + } + }, [formattedTime]); + + return ( + handleEditDialog(open, null)}> + + + Posto Adm - Update + + + +
+ {alert.show && ( + +

{alert.message}

+
+ )} + +
+
+
+
+ + setFormField({ ...formField, name: e.target.value })} + /> +
+
+ +
+
+ + { + const value = parseInt(e.target.value, 10); + setFormField({ ...formField, municipio_id: isNaN(value) ? 0 : value }); + }} + /> +
+
+ +
+ +
+
+
+
+
+
+
+ ); +}; + +export default EditDialog; diff --git a/src/pages/master/postoadms/hooks/ManagePostoAdmsContext.tsx b/src/pages/master/postoadms/hooks/ManagePostoAdmsContext.tsx index 973817c..971470b 100644 --- a/src/pages/master/postoadms/hooks/ManagePostoAdmsContext.tsx +++ b/src/pages/master/postoadms/hooks/ManagePostoAdmsContext.tsx @@ -1,4 +1,4 @@ -import { DataGridColumnHeader, DataGridProvider } from '@/components'; +import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components'; import { Button } from '@/components/ui/button'; import { Toaster } from '@/components/ui/sonner'; import { apiConfig } from '@/config/api.config'; @@ -106,18 +106,29 @@ const ManagePostoAdmsContextProvider = ({ children }: { children: React.ReactNod header: ({ column }) => , enableSorting: false, enableHiding: false, - meta: { - headerClassName: 'w-[100px], text-center', - cellClassName: 'text-center' + cell: (data) => { + const row = data.row.original; + return ( + <> + + + + ); }, - cell: (info) => ( - - ) + meta: { + headerClassName: 'w-[100px]', + cellClassName: 'text-center' + } } ], [handleEditDialog, handleDeleteDialog] diff --git a/src/pages/master/sucos/SucosMaster.tsx b/src/pages/master/sucos/SucosMaster.tsx index 6feede3..62911a4 100644 --- a/src/pages/master/sucos/SucosMaster.tsx +++ b/src/pages/master/sucos/SucosMaster.tsx @@ -1,10 +1,20 @@ +import { Container, DataGridInner } from '@/components'; +import { ManageSucosContextProvider } from './hooks/ManageSucosContext'; +import AddDialog from './blocks/AddDialog'; +import EditDialog from './blocks/EditDialog'; + const SucosMaster = () => { return ( -
-
-

Sucos Master Data

-
-
+ + +

Sucos

+
+ +
+ + +
+
); }; diff --git a/src/pages/master/sucos/blocks/AddDialog.tsx b/src/pages/master/sucos/blocks/AddDialog.tsx new file mode 100644 index 0000000..51909e6 --- /dev/null +++ b/src/pages/master/sucos/blocks/AddDialog.tsx @@ -0,0 +1,149 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { useManageSucosContext } from '../hooks/useManageSucosContext'; +import { Alert, useDataGrid } from '@/components'; +import { useCallApi } from '@/hooks'; +import { getAuth } from '@/auth'; +import { apiConfig } from '@/config/api.config'; +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 { showAddDialog, handleAddDialog } = useManageSucosContext(); + const { reload } = useDataGrid(); + const { PostData } = useCallApi(); + const parsedUser = getAuth()?.user; + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + const initialState = { + name: '', + posto_adm_id: 0, + 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 doCreateSucos = useCallback(async (e: React.FormEvent) => { + e.preventDefault(); + const response = await PostData(`${API_URL}/sucos/create`, formField); + + if (response?.status) { + resetForm(); + handleAddDialog(false); + reload(); + toast.success('Success Create Sucos'); + } else { + toast.error('Failed Create Sucos'); + setAlert({ show: true, message: response?.message }); + } + }, []); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if (formField.name === '' || formField.posto_adm_id === 0) { + setAlert({ show: true, message: 'Please fill in all required fields.' }); + return; + } + + // doCreateSucos(e); + console.log(formField); + setAlert({ show: false, message: '' }); + }; + + useEffect(() => { + if (showAddDialog) { + setFormField({ + ...formField, + created_by: parsedUser.username, + created_at: formattedTime + }); + } + }, [formattedTime]); + + return ( + handleAddDialog(open)}> + + + Sucos - Create + + + +
+ {alert.show && ( + +

{alert.message}

+
+ )} + +
+
+
+
+ + setFormField({ ...formField, name: e.target.value })} + /> +
+
+ +
+
+ + { + const value = parseInt(e.target.value, 10); + setFormField({ ...formField, posto_adm_id: isNaN(value) ? 0 : value }); + }} + /> +
+
+ +
+ + +
+
+
+
+
+
+
+ ); +}; + +export default AddDialog; diff --git a/src/pages/master/sucos/blocks/DeleteDialog.tsx b/src/pages/master/sucos/blocks/DeleteDialog.tsx new file mode 100644 index 0000000..d5aa4ce --- /dev/null +++ b/src/pages/master/sucos/blocks/DeleteDialog.tsx @@ -0,0 +1,74 @@ +import { apiConfig } from '@/config/api.config'; +import { useManageSucosContext } from '../hooks/useManageSucosContext'; +import { ChangeEvent, useCallback, useState } from 'react'; +import { useCallApi } from '@/hooks'; +import { Alert, useDataGrid } from '@/components'; +import { toast } from 'sonner'; +import { Dialog, DialogContent, DialogFooter, DialogHeader } 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, selectedSucos } = useManageSucosContext(); + const { reload } = useDataGrid(); + const { DeleteData } = useCallApi(); + const [enforce, setEnforce] = useState(false); + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + + const doDeleteSucos = useCallback(async () => { + const response = await DeleteData(`${API_URL}/sucos/delete/${selectedSucos}/${enforce}`, { + id: selectedSucos + }); + + if (response?.status) { + setAlert((prev) => ({ ...prev, show: false, message: '' })); + handleDeleteDialog(false, null); + toast.success('Success Delete Sucos'); + reload(); + } else { + setAlert((prev) => ({ ...prev, show: true, message: response?.message })); + } + }, [selectedSucos, 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/sucos/blocks/EditDialog.tsx b/src/pages/master/sucos/blocks/EditDialog.tsx new file mode 100644 index 0000000..f655658 --- /dev/null +++ b/src/pages/master/sucos/blocks/EditDialog.tsx @@ -0,0 +1,153 @@ +import { Alert, useDataGrid } from '@/components'; +import { useManageSucosContext } from '../hooks/useManageSucosContext'; +import { useCallApi } from '@/hooks'; +import { getAuth } from '@/auth'; +import React, { useCallback, useEffect, useState } from 'react'; +import { apiConfig } from '@/config/api.config'; +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, selectedSucos } = useManageSucosContext(); + const { reload } = useDataGrid(); + const { PutData } = useCallApi(); + const parsedUser = getAuth()?.user; + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + const initialState = { + name: '', + posto_adm_id: 0, + updated_by: '', + updated_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 doUpdateSucos = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + + const response = await PutData(`${API_URL}/sucos/update/${selectedSucos}`, formField); + + if (response?.status) { + resetForm(); + handleEditDialog(false, null); + toast.success('Success Update Sucos'); + reload(); + } else { + toast.error('Failed Update Sucos'); + setAlert({ show: true, message: 'Failed Update Sucos. Please try again' }); + } + }, + [selectedSucos, formField] + ); + + const handleUpdate = (e: React.FormEvent) => { + e.preventDefault(); + + if (formField.name === '' || formField.posto_adm_id === 0) { + setAlert({ show: true, message: 'Please fill in all required fields.' }); + return; + } + + // doUpdateSucos(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)}> + + + Sucos - Update + + + +
+ {alert.show && ( + +

{alert.message}

+
+ )} + +
+
+
+
+ + setFormField({ ...formField, name: e.target.value })} + /> +
+
+ +
+
+ + { + const value = parseInt(e.target.value, 10); + setFormField({ ...formField, posto_adm_id: isNaN(value) ? 0 : value }); + }} + /> +
+
+ +
+ +
+
+
+
+
+
+
+ ); +}; + +export default EditDialog; diff --git a/src/pages/master/sucos/blocks/ListToolbar.tsx b/src/pages/master/sucos/blocks/ListToolbar.tsx index e69de29..f0a23b8 100644 --- a/src/pages/master/sucos/blocks/ListToolbar.tsx +++ b/src/pages/master/sucos/blocks/ListToolbar.tsx @@ -0,0 +1,64 @@ +import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components'; +import { useManageSucosContext } from '../hooks/useManageSucosContext'; +import { Button } from '@/components/ui/button'; + +const ListToolbar = () => { + const { table, reload } = useDataGrid(); + const { handleAddDialog, handleSearchDialog } = useManageSucosContext(); + + return ( +
+
+
+
+ + + + + +
+
+ + + + +
+
+
+
+ ); +}; + +export default ListToolbar; diff --git a/src/pages/master/sucos/hooks/ManageSucosContext.tsx b/src/pages/master/sucos/hooks/ManageSucosContext.tsx new file mode 100644 index 0000000..4e8628b --- /dev/null +++ b/src/pages/master/sucos/hooks/ManageSucosContext.tsx @@ -0,0 +1,200 @@ +import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components'; +import { Toaster } from '@/components/ui/sonner'; +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 ListToolbar from '../blocks/ListToolbar'; + +interface SucosProps { + id: number; + name: string; +} + +interface ContextProps { + sucos: SucosProps[]; + showSearchDialog: boolean; + handleSearchDialog: (show: boolean) => void; + 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; + selectedSucos: string | null; + getSucosLists: ( + limit: number, + page: number, + with_deleted: boolean, + order_field: any, + order_direction: any + ) => Promise<{ data: SucosProps[]; totalCount: number } | undefined>; +} + +const initialProps: ContextProps = { + sucos: [], + showSearchDialog: false, + handleSearchDialog: () => {}, + showEditDialog: false, + handleEditDialog: () => {}, + showAddDialog: false, + handleAddDialog: () => {}, + showDeleteDialog: false, + handleDeleteDialog: () => {}, + selectedSucos: null, + getSucosLists: async () => undefined +}; + +const ManageSucosContext = createContext(initialProps); +const API_URL = apiConfig.service_master_data; + +const ManageSucosContextProvider = ({ children }: { children: React.ReactNode }) => { + const [sucos, setSucos] = useState([]); + const [showSearchDialog, setShowSearchDialog] = useState(false); + const [showEditDialog, setShowEditDialog] = useState(false); + const [showAddDialog, setShowAddDialog] = useState(false); + const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const [selectedSucos, setSelectedSucos] = useState(null); + const { GetData } = useCallApi(); + + const handleSearchDialog = useCallback((show: boolean) => { + setShowSearchDialog(show); + }, []); + + const handleAddDialog = useCallback((show: boolean) => { + setShowAddDialog(show); + }, []); + + const handleEditDialog = useCallback((show: boolean, selected_sucos: string | null) => { + setSelectedSucos(show ? selected_sucos : null); + setShowEditDialog(show); + }, []); + + const handleDeleteDialog = useCallback((show: boolean, selected_sucos: string | null) => { + setSelectedSucos(show ? selected_sucos : null); + setShowDeleteDialog(show); + }, []); + + const columns = useMemo[]>( + () => [ + { + accessorFn: (row) => row.sucos_id, + id: 'id', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[100px]' + } + }, + { + accessorFn: (row) => row.sucos_name, + id: 'sucos_name', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + accessorFn: (row) => row.posto_name, + id: 'posto_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 getSucosLists = 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}/sucos/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); + setSucos(response?.data.list); + // console.log(sucos); + return { data: response?.data.list, totalCount: response?.data.total_count }; + } catch (error) { + console.error('Error fetching Sucos', error); + } + }; + + return ( + + + + } + layout={{ card: true }} + sorting={[{ id: 'id', desc: false }]} + serverSide={true} + onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => + getSucosLists(pageIndex, pageSize, sorting, columnFilters) + } + > + {children} + + + ); +}; + +export { ManageSucosContextProvider, ManageSucosContext }; +export type { SucosProps }; diff --git a/src/pages/master/sucos/hooks/useManageSucosContext.tsx b/src/pages/master/sucos/hooks/useManageSucosContext.tsx new file mode 100644 index 0000000..194575a --- /dev/null +++ b/src/pages/master/sucos/hooks/useManageSucosContext.tsx @@ -0,0 +1,12 @@ +import { useContext } from 'react'; +import { ManageSucosContext } from './ManageSucosContext'; + +const useManageSucosContext = () => { + const context = useContext(ManageSucosContext); + + if (!context) throw new Error('useManageSucosContext must be used within AuthProvider'); + + return context; +}; + +export { useManageSucosContext }; diff --git a/src/pages/menu/manage-menu/ManageMenu.tsx b/src/pages/menu/manage-menu/ManageMenu.tsx index e9febec..75e0925 100644 --- a/src/pages/menu/manage-menu/ManageMenu.tsx +++ b/src/pages/menu/manage-menu/ManageMenu.tsx @@ -1,10 +1,22 @@ +import { Container, DataGridInner } from '@/components'; +import { ManageMenusContextProvider } from './hooks/ManageMenusContext'; +import AddDialog from './blocks/AddDIalog'; +import EditDialog from './blocks/EditDialog'; +import DeleteDialog from './blocks/DeleteDialog'; + const ManageMenu = () => { return ( -
-
-

Manage Menu

-
-
+ + +

Manage Menus

+
+ +
+ + + +
+
); }; diff --git a/src/pages/menu/manage-menu/blocks/AddDIalog.tsx b/src/pages/menu/manage-menu/blocks/AddDIalog.tsx new file mode 100644 index 0000000..72e4209 --- /dev/null +++ b/src/pages/menu/manage-menu/blocks/AddDIalog.tsx @@ -0,0 +1,212 @@ +import { apiConfig } from '@/config/api.config'; +import React, { useCallback, useRef, useState } from 'react'; +import { useManageMenusContext } from '../hooks/useManageMenusContext'; +import { Alert, useDataGrid } from '@/components'; +import { useCallApi } from '@/hooks'; +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_dashboard; +const AddDialog = () => { + const parentRef = useRef(null); + const { showAddDialog, handleAddDialog } = useManageMenusContext(); + const { reload } = useDataGrid(); + const { PostData } = useCallApi(); + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + const initialState = { + module: '', + name: '', + link: '', + id_parent: '', + order_number: 0, + icon: '', + status: '' + }; + const [formField, setFormField] = useState(initialState); + + const doCreateMenu = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + + const response = await PostData(`${API_URL}/menus/create`, formField); + + if (response?.status) { + handleAddDialog(false); + resetForm(); + toast.success('Success Create Menu'); + reload(); + } else { + toast.error('Failed Create Menu'); + setAlert({ show: true, message: 'Failed Create Menu' }); + } + }, + [formField] + ); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if ( + formField.module === '' || + formField.name === '' || + formField.link === '' || + formField.order_number === 0 || + formField.status === '' + ) { + setAlert({ show: true, message: 'Please fill in all required fields.' }); + return; + } + + doCreateMenu(e); + console.log(formField); + setAlert({ show: false, message: '' }); + }; + + const resetForm = () => { + setFormField(initialState); + setAlert({ show: false, message: '' }); + }; + + return ( + handleAddDialog(open)}> + + + Menu - Create + + + +
+ {alert.show && ( + +

{alert.message}

+
+ )} + +
+
+
+
+ + setFormField({ ...formField, module: e.target.value })} + /> +
+
+ +
+
+ + setFormField({ ...formField, name: e.target.value })} + /> +
+
+ +
+
+ + setFormField({ ...formField, link: e.target.value })} + /> +
+
+ +
+
+ + setFormField({ ...formField, id_parent: e.target.value })} + /> +
+
+ +
+
+ + { + const value = parseInt(e.target.value, 10); + setFormField({ ...formField, order_number: isNaN(value) ? 0 : value }); + }} + /> +
+
+ +
+
+ + setFormField({ ...formField, name: e.target.value })} + /> +
+
+ +
+
+ + setFormField({ ...formField, status: e.target.value })} + /> +
+
+ +
+ + +
+
+
+
+
+
+
+ ); +}; + +export default AddDialog; diff --git a/src/pages/menu/manage-menu/blocks/DeleteDialog.tsx b/src/pages/menu/manage-menu/blocks/DeleteDialog.tsx new file mode 100644 index 0000000..f0baf0e --- /dev/null +++ b/src/pages/menu/manage-menu/blocks/DeleteDialog.tsx @@ -0,0 +1,76 @@ +import { apiConfig } from '@/config/api.config'; +import { useManageMenusContext } from '../hooks/useManageMenusContext'; +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_dashboard; +const DeleteDialog = () => { + const { showDeleteDialog, handleDeleteDialog, selectedMenu } = useManageMenusContext(); + const { reload } = useDataGrid(); + const { DeleteData } = useCallApi(); + const [enforce, setEnforce] = useState(false); + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + + const doDeleteMenu = useCallback(async () => { + const response = await DeleteData(`${API_URL}/menus/delete/${selectedMenu}/${enforce}`, { + id: selectedMenu + }); + + if (response?.status) { + setAlert((prev) => ({ ...prev, show: false, message: '' })); + handleDeleteDialog(false, null); + toast.success('Success Delete Menu'); + reload(); + } else { + toast.error('Failed Delete Menu'); + setAlert((prev) => ({ ...prev, show: true, message: response?.message })); + } + }, [selectedMenu, 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/menu/manage-menu/blocks/EditDialog.tsx b/src/pages/menu/manage-menu/blocks/EditDialog.tsx new file mode 100644 index 0000000..7dff544 --- /dev/null +++ b/src/pages/menu/manage-menu/blocks/EditDialog.tsx @@ -0,0 +1,205 @@ +import { Alert, useDataGrid } from '@/components'; +import { useManageMenusContext } from '../hooks/useManageMenusContext'; +import { useCallApi } from '@/hooks'; +import { apiConfig } from '@/config/api.config'; +import React, { useCallback, 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_dashboard; +const EditDialog = () => { + const { showEditDialog, handleEditDialog, selectedMenu } = useManageMenusContext(); + const { reload } = useDataGrid(); + const { PutData } = useCallApi(); + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + const initialState = { + module: '', + name: '', + link: '', + id_parent: '', + order_number: 0, + icon: '', + status: '' + }; + const [formField, setFormField] = useState(initialState); + + const doUpdateMenu = useCallback(async (e: React.FormEvent) => { + e.preventDefault(); + + const response = await PutData(`${API_URL}/menus/update/${selectedMenu}`, formField); + + if (response?.status) { + handleEditDialog(false, null); + resetForm(); + toast.success('Success Update Menu'); + reload(); + } else { + toast.error('Failed Update Menu'); + setAlert({ show: true, message: 'Failed Update Menu' }); + } + }, []); + + const handleUpdate = (e: React.FormEvent) => { + e.preventDefault(); + + if ( + formField.module === '' || + formField.name === '' || + formField.link === '' || + formField.order_number === 0 || + formField.status === '' + ) { + setAlert({ show: true, message: 'Please fill in all required fields.' }); + return; + } + + doUpdateMenu(e); + console.log(formField); + setAlert({ show: false, message: '' }); + }; + + const resetForm = () => { + setFormField(initialState); + setAlert({ show: false, message: '' }); + }; + + return ( + handleEditDialog(open, null)}> + + + Menu - Update + + + +
+ {alert.show && ( + +

{alert.message}

+
+ )} + +
+
+
+
+ + setFormField({ ...formField, module: e.target.value })} + /> +
+
+ +
+
+ + setFormField({ ...formField, name: e.target.value })} + /> +
+
+ +
+
+ + setFormField({ ...formField, link: e.target.value })} + /> +
+
+ +
+
+ + setFormField({ ...formField, id_parent: e.target.value })} + /> +
+
+ +
+
+ + { + const value = parseInt(e.target.value, 10); + setFormField({ ...formField, order_number: isNaN(value) ? 0 : value }); + }} + /> +
+
+ +
+
+ + setFormField({ ...formField, name: e.target.value })} + /> +
+
+ +
+
+ + setFormField({ ...formField, status: e.target.value })} + /> +
+
+ +
+ +
+
+
+
+
+
+
+ ); +}; + +export default EditDialog; diff --git a/src/pages/menu/manage-menu/blocks/ListToolbar.tsx b/src/pages/menu/manage-menu/blocks/ListToolbar.tsx new file mode 100644 index 0000000..784c705 --- /dev/null +++ b/src/pages/menu/manage-menu/blocks/ListToolbar.tsx @@ -0,0 +1,55 @@ +import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components'; +import { useManageMenusContext } from '../hooks/useManageMenusContext'; +import { Button } from '@/components/ui/button'; + +const ListToolbar = () => { + const { table, reload } = useDataGrid(); + const { handleAddDialog } = useManageMenusContext(); + + return ( +
+
+
+
+ + + + +
+
+ + + + +
+
+
+
+ ); +}; + +export default ListToolbar; diff --git a/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx b/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx new file mode 100644 index 0000000..69f43b1 --- /dev/null +++ b/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx @@ -0,0 +1,250 @@ +// interface SelectedMenu { +// id: string; +// module: string; +// name: string; +// id_parent: string; +// order_number: number; +// icon: string; +// application: string; +// status: string; +// } + +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 MenuProps { + id: string; + module: string; + name: string; + id_parent: string; + order_number: number; + icon: string; + status: string; +} + +interface ContextProps { + menus: MenuProps[]; + showEditDialog: boolean; + handleEditDialog: (show: boolean, selected_postoAdms: string | null) => void; + showAddDialog: boolean; + handleAddDialog: (show: boolean) => void; + showDeleteDialog: boolean; + handleDeleteDialog: (show: boolean, selected_postoAdms: string | null) => void; + selectedMenu: string | null; + getMenusLists: ( + limit: number, + page: number, + with_deleted: boolean, + order_field: any, + order_direction: any + ) => Promise<{ data: MenuProps[]; totalCount: number } | undefined>; +} + +const initialProps: ContextProps = { + menus: [], + showAddDialog: false, + handleAddDialog: (show: boolean) => {}, + showEditDialog: false, + handleEditDialog: (show: boolean, selected_menu: string | null) => {}, + showDeleteDialog: false, + handleDeleteDialog: (show: boolean, selected_menu: string | null) => {}, + selectedMenu: null, + getMenusLists: async () => ({ data: [], totalCount: 0 }) +}; + +const ManageMenusContext = createContext(initialProps); +const API_URL = apiConfig.service_dashboard; + +const ManageMenusContextProvider = ({ children }: { children: React.ReactNode }) => { + const [menus, setMenus] = useState([]); + const [showAddDialog, setShowAddDialog] = useState(false); + const [showEditDialog, setShowEditDialog] = useState(false); + const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const [selectedMenu, setSelectedMenu] = useState(null); + const { GetData } = useCallApi(); + + const handleAddDialog = useCallback((show: boolean) => { + setShowAddDialog(show); + }, []); + + const handleEditDialog = useCallback((show: boolean, selected_menu: string | null) => { + setShowEditDialog(show); + setSelectedMenu(selected_menu); + }, []); + + const handleDeleteDialog = useCallback((show: boolean, selected_menu: string | null) => { + setShowDeleteDialog(show); + setSelectedMenu(selected_menu); + }, []); + + const columns = useMemo[]>( + () => [ + { + accessorFn: (row) => row.module, + id: 'module', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[200px]' } + }, + { + accessorFn: (row) => row.parentName, + id: 'menu', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { headerClassName: 'w-[200px]' } + }, + { + accessorFn: (row) => row.name, + id: 'subMenu', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { headerClassName: 'w-[200px]' } + }, + { + accessorFn: (row) => row.link, + id: 'link', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { headerClassName: 'w-[250px]' } + }, + { + accessorFn: (row) => row.status, + id: 'status', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { headerClassName: 'w-[100px]', cellClassName: 'text-center' } + }, + { + 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 flattenChildren = (parent: any, parentIdx: number, depth = 0, parentName = '') => { + if (!parent.children || parent.children.length === 0) { + return []; // Jika tidak ada children, kembalikan array kosong + } + + return parent.children.flatMap((child: any, childIdx: number) => { + // Jika child masih punya children, lakukan rekursi lebih dalam + if (child.children && child.children.length > 0) { + return flattenChildren(child, parentIdx * 100 + childIdx, depth + 1, child.name); + } + + // Jika ini adalah child terakhir (leaf node), masukkan ke array hasil + return { + id: parentIdx * 100 + childIdx + 1, + module: parent.module, + parentName: parentName || parent.name, + name: child.name, + link: child.link, + id_parent: parent.id_parent, + status: parent.status + }; + }); + }; + + const getMenusLists = 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}/menus/list`, { + limit, + page: page + 1, + with_deleted: false, + order_field: sorting[0].id, + order_direction: sorting[0].desc ? 'DESC' : 'ASC' + }); + + console.log(response?.data); + if (!response?.data.list) return { data: [], totalCount: 0 }; + + // Gunakan rekursi untuk mencari children paling dalam + const transformedData = response.data.list.flatMap((row: any, parentIdx: number) => + flattenChildren(row, parentIdx) + ); + + const total_count = transformedData.length; + + setMenus(transformedData); + console.log(menus); + return { data: transformedData, totalCount: total_count }; + } catch (error) { + console.error('Error fetching Menus', error); + return { data: [], totalCount: 0 }; + } + }; + + return ( + + + + } + layout={{ card: true }} + sorting={[{ id: 'id', desc: false }]} + serverSide={true} + onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => + getMenusLists(pageIndex, pageSize, sorting, columnFilters) + } + > + {children} + + + ); +}; + +export { ManageMenusContextProvider, ManageMenusContext }; +export type { MenuProps }; diff --git a/src/pages/menu/manage-menu/hooks/useManageMenusContext.tsx b/src/pages/menu/manage-menu/hooks/useManageMenusContext.tsx new file mode 100644 index 0000000..1cf8f87 --- /dev/null +++ b/src/pages/menu/manage-menu/hooks/useManageMenusContext.tsx @@ -0,0 +1,12 @@ +import { useContext } from 'react'; +import { ManageMenusContext } from './ManageMenusContext'; + +const useManageMenusContext = () => { + const context = useContext(ManageMenusContext); + if (!context) { + throw new Error('useManageMenusContext must be used within a ManageMenusContextProvider'); + } + return context; +}; + +export { useManageMenusContext };