From 28090b726e0ea7d1284b10bcb0396a9b2b5f7f97 Mon Sep 17 00:00:00 2001 From: bagusajisaputroo Date: Fri, 4 Apr 2025 16:56:59 +0700 Subject: [PATCH] Add, edit, delete currency --- src/pages/master/currency/CurrencyMaster.tsx | 44 ++++ .../master/currency/blocks/AddDialog.tsx | 226 ++++++++++++++++++ .../master/currency/blocks/DeleteDialog.tsx | 78 ++++++ .../master/currency/blocks/EditDialog.tsx | 220 +++++++++++++++++ .../master/currency/blocks/ListToolbar.tsx | 55 +++++ .../currency/hooks/ManageCurrencyContext.tsx | 199 +++++++++++++++ .../hooks/useManageCurrencyContext.tsx | 13 + src/routing/AppRoutingSetup.tsx | 3 + 8 files changed, 838 insertions(+) create mode 100644 src/pages/master/currency/CurrencyMaster.tsx create mode 100644 src/pages/master/currency/blocks/AddDialog.tsx create mode 100644 src/pages/master/currency/blocks/DeleteDialog.tsx create mode 100644 src/pages/master/currency/blocks/EditDialog.tsx create mode 100644 src/pages/master/currency/blocks/ListToolbar.tsx create mode 100644 src/pages/master/currency/hooks/ManageCurrencyContext.tsx create mode 100644 src/pages/master/currency/hooks/useManageCurrencyContext.tsx diff --git a/src/pages/master/currency/CurrencyMaster.tsx b/src/pages/master/currency/CurrencyMaster.tsx new file mode 100644 index 0000000..be439cd --- /dev/null +++ b/src/pages/master/currency/CurrencyMaster.tsx @@ -0,0 +1,44 @@ +import { Container, DataGridInner } from '@/components'; +import { ManageCurrencyContextProvider } from './hooks/ManageCurrencyContext'; +import { Breadcrumbs, Link } from '@mui/material'; +import { Delete } from 'lucide-react'; +import AddDialog from './blocks/AddDialog'; +import DeleteDialog from './blocks/DeleteDialog'; +import EditDialog from './blocks/EditDialog'; + +// import EditDialog from './blocks/EditDialog'; + +const CurrencyMaster = () => { + return ( + + +

Currency

+ + + Dashboard + + + + Master Data + + + + Manage Currency + + + +
+ +
+ + + + + {/* + */} +
+
+ ); +}; + +export default CurrencyMaster; diff --git a/src/pages/master/currency/blocks/AddDialog.tsx b/src/pages/master/currency/blocks/AddDialog.tsx new file mode 100644 index 0000000..44e6a40 --- /dev/null +++ b/src/pages/master/currency/blocks/AddDialog.tsx @@ -0,0 +1,226 @@ +import { apiConfig } from '@/config/api.config'; +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'; +import { NumericFormat } from 'react-number-format'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList +} from '@/components/ui/command'; +import { set } from 'date-fns'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from '@/components/ui/select'; +import { useManageCurrencyContext } from '../hooks/useManageCurrencyContext'; +import { prefix } from 'stylis'; +interface CurrencyProps { + ID: string; + name: string; +} + +const API_URL = apiConfig.service_wallet; + +const AddDialog = () => { + const parentRef = useRef(null); + const { showAddDialog, handleAddDialog, selectedCurrency } = useManageCurrencyContext(); + const { reload } = useDataGrid(); + const { PostData, GetData } = useCallApi(); + const parsedUser = getAuth()?.user; + const [currencies, setCurrencies] = useState([]); + const [open, setOpen] = useState(false); + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + const initialState = { + code: '', + name: '', + prefix: '', + status: '', + 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 doCreateCurrency = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + + const response = await PostData(`${API_URL}/dashboard/currency/`, formField); + + if (response?.status) { + resetForm(); + handleAddDialog(false); + toast.success('Success Create Currency'); + reload(); + } else { + toast.error('Error Create Currency'); + setAlert({ show: true, message: 'Failed to create Currency. Please try again.' }); + } + }, + [formField] + ); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if ( + formField.code === '' || + formField.name === '' || + formField.prefix === '' || + formField.status === '' + ) { + setAlert({ show: true, message: 'Please fill in all required fields.' }); + return; + } + + doCreateCurrency(e); + // console.log(formField); + setAlert({ show: false, message: '' }); + }; + + useEffect(() => { + if (showAddDialog) { + setFormField({ + ...formField, + created_by: parsedUser.username, + created_at: formattedTime + }); + } + }, [formattedTime]); + + useEffect(() => { + if (showAddDialog === false) { + resetForm(); + } + }, [showAddDialog]); + + return ( + handleAddDialog(open)}> + + + Cuurency - Create + + + +
+ {alert.show && ( + +

{alert.message}

+
+ )} + +
+
+
+ + + + setFormField((prev) => ({ ...prev, code: target.value })) + } + /> +
+
+ + + + setFormField((prev) => ({ ...prev, name: target.value })) + } + /> +
+
+ + + + setFormField((prev) => ({ ...prev, prefix: target.value })) + } + /> +
+ +
+ + +
+ +
+
+
+ + +
+
+
+
+
+
+
+ ); +}; + +export default AddDialog; diff --git a/src/pages/master/currency/blocks/DeleteDialog.tsx b/src/pages/master/currency/blocks/DeleteDialog.tsx new file mode 100644 index 0000000..3a84e92 --- /dev/null +++ b/src/pages/master/currency/blocks/DeleteDialog.tsx @@ -0,0 +1,78 @@ +import { Alert, useDataGrid } from '@/components'; +import { useManageCurrencyContext } from '../hooks/useManageCurrencyContext'; +import { useCallback, useState } from 'react'; +import { apiConfig } from '@/config/api.config'; +import { useCallApi } from '@/hooks'; +import { toast } from 'sonner'; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { DialogDescription } from '@radix-ui/react-dialog'; + +const API_URL = apiConfig.service_wallet; + +const DeleteDialog = () => { + const { showDeleteDialog, handleDeleteDialog, selectedCurrency } = useManageCurrencyContext(); + const { reload } = useDataGrid(); + const { DeleteData } = useCallApi(); + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + + const doDeleteCurrency = useCallback(async () => { + if (!selectedCurrency) { + toast.error('No Currency selected'); + return; + } + // console.log(selectedCurrency); + const response = await DeleteData(`${API_URL}/dashboard/currency/${selectedCurrency}`, { + id: selectedCurrency + }); + + if (response?.status) { + setAlert({ show: false, message: '' }); + handleDeleteDialog(false, null); + toast.success('Success Delete Currency'); + reload(); + } else { + setAlert({ show: true, message: response?.message }); + toast.error('Failed Delete Currency'); + } + }, [selectedCurrency, DeleteData, handleDeleteDialog, reload]); + // console.log(selectedCurrency); + return ( + handleDeleteDialog(open, null)}> + + + + + +

Are you sure?

+ You will delete this data! +
+ {alert.show && ( + +

{alert.message}

+
+ )} +
+ + + + +
+
+ ); +}; + +export default DeleteDialog; diff --git a/src/pages/master/currency/blocks/EditDialog.tsx b/src/pages/master/currency/blocks/EditDialog.tsx new file mode 100644 index 0000000..14b3be6 --- /dev/null +++ b/src/pages/master/currency/blocks/EditDialog.tsx @@ -0,0 +1,220 @@ +import { apiConfig } from '@/config/api.config'; +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'; +import { NumericFormat } from 'react-number-format'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList +} from '@/components/ui/command'; +import { set } from 'date-fns'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from '@/components/ui/select'; +import { useManageCurrencyContext } from '../hooks/useManageCurrencyContext'; +interface CurrencyProps { + ID: string; + name: string; +} + +const API_URL = apiConfig.service_wallet; + +const EditDialog = () => { + const parentRef = useRef(null); + const { showEditDialog, handleEditDialog, selectedCurrency } = useManageCurrencyContext(); + const { reload } = useDataGrid(); + const { PostData, GetData, PutData } = useCallApi(); + const parsedUser = getAuth()?.user; + const [currencies, setCurrencies] = useState([]); + const [open, setOpen] = useState(false); + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + const initialState = { + code: '', + name: '', + prefix: '', + status: '', + created_by: '', + created_at: '' + }; + const [formField, setFormField] = useState(initialState); + const updated_time = new Date(); + const formattedTime = updated_time.toISOString().slice(0, 19).replace('T', ' '); + + const resetForm = () => { + setFormField(initialState); + setAlert({ show: false, message: '' }); + }; + + const doUpdateCurrency = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + if (!showEditDialog) return; + const response = await PutData(`${API_URL}/dashboard/currency/${selectedCurrency}`, { + ...formField + }); + + if (response?.status) { + resetForm(); + handleEditDialog(false, null); + toast.success('Success Update Currency'); + reload(); + } else { + toast.error('Error Create Currency'); + setAlert({ show: true, message: 'Failed to Update Currency. Please try again.' }); + } + }, + [formField] + ); + + const doGetCurrencyById = useCallback(async (id: string) => { + const response = await GetData(`${API_URL}/dashboard/currency/${id}`, { id }); + // console.log('Transaction Type: ', response?.data); + if (response?.status) { + setFormField((prev) => ({ + ...prev, + status: response.data.status, + code:response.data.code, + name:response.data.name, + prefix:response.data.prefix, + })); + } + // console.log('form fieldd Transaction Type: ', formField); + }, []); + + useEffect(() => { + if (selectedCurrency) { + doGetCurrencyById(selectedCurrency); + } + }, [selectedCurrency]); + + useEffect(() => { + if (showEditDialog) { + setFormField({ + ...formField, + created_by: parsedUser.username, + created_at: formattedTime + }); + } + }, [formattedTime]); + + useEffect(() => { + if (showEditDialog === false) { + resetForm(); + } + }, [showEditDialog]); + + return ( + handleEditDialog(open, null)}> + + + Currency - Update + + + +
+ {alert.show && ( + +

{alert.message}

+
+ )} + +
+
+
+ + setFormField((prev) => ({ ...prev, code: e.target.value }))} + /> +
+
+ + setFormField((prev) => ({ ...prev, name: e.target.value }))} + /> +
+ +
+ + setFormField((prev) => ({ ...prev, prefix: e.target.value }))} + /> +
+ +
+ + + +
+ +
+ + +
+
+
+
+
+
+
+ ); +}; + +export default EditDialog; diff --git a/src/pages/master/currency/blocks/ListToolbar.tsx b/src/pages/master/currency/blocks/ListToolbar.tsx new file mode 100644 index 0000000..9cf61a3 --- /dev/null +++ b/src/pages/master/currency/blocks/ListToolbar.tsx @@ -0,0 +1,55 @@ +import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components'; +import { Button } from '@/components/ui/button'; +import { useManageCurrencyContext } from '../hooks/useManageCurrencyContext'; + +const ListToolbar = () => { + const { table, reload } = useDataGrid(); + const { handleAddDialog } = useManageCurrencyContext(); + + return ( +
+
+
+
+ {/* */} + {/* + + */} +
+
+ + + + +
+
+
+
+ ); +}; + +export default ListToolbar; diff --git a/src/pages/master/currency/hooks/ManageCurrencyContext.tsx b/src/pages/master/currency/hooks/ManageCurrencyContext.tsx new file mode 100644 index 0000000..f025f87 --- /dev/null +++ b/src/pages/master/currency/hooks/ManageCurrencyContext.tsx @@ -0,0 +1,199 @@ +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 { createContext, useCallback, useEffect, useMemo, useState } from 'react'; +import ListToolbar from '../blocks/ListToolbar'; +import DeleteDialog from '../blocks/DeleteDialog'; + +interface Currency { + id: string; + code: string; + name: string; + prefix: string; + status: string; +} + +interface ContextProps { + showEditDialog: boolean; + handleEditDialog: (show: boolean, selected_currency: string | null) => void; + showAddDialog: boolean; + handleAddDialog: (show: boolean) => void; + showDeleteDialog: boolean; + handleDeleteDialog: (show: boolean, selected_currency: string | null) => void; + selectedCurrency: string | null; + currency: string | null; +} + +const initialProps: ContextProps = { + showEditDialog: false, + handleEditDialog: () => {}, + showAddDialog: false, + handleAddDialog: () => {}, + showDeleteDialog: false, + handleDeleteDialog: () => {}, + selectedCurrency: null, + currency: null +}; + +const ManageCurrencyContext = createContext(initialProps); +const API_URL = apiConfig.service_wallet; + +const ManageCurrencyContextProvider = ({ children }: { children: React.ReactNode }) => { + const [showEditDialog, setShowEditDialog] = useState(false); + const [showAddDialog, setShowAddDialog] = useState(false); + const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const { GetData } = useCallApi(); + const [selectedCurrency, setSelectedCurrency] = useState(null); + const [currency, setCurrency] = useState(null); + + const handleEditDialog = useCallback((show: boolean, selected_currency: string | null) => { + setSelectedCurrency(show ? selected_currency : null); + setShowEditDialog(show); + }, []); + + const handleAddDialog = useCallback((show: boolean) => { + setShowAddDialog(show); + }, []); + + const handleDeleteDialog = useCallback((show: boolean, selected_currency: string | null) => { + setShowDeleteDialog(show); + setSelectedCurrency(show ? selected_currency : null); + }, []); + + const columns = useMemo[]>( + () => [ + { + accessorFn: (row) => row.name, + id: 'name', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[150px]' } + }, + { + accessorFn: (row) => row.code, + id: 'code', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[150px]' } + }, + { + accessorFn: (row) => row.prefix, + id: 'prefix', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[150px]' } + }, + { + accessorFn: (row) => row.status, + id: 'status', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[150px]' }, + cell: ({ row }) => { + const isActive = row.original.status === 'Y'; + + return ( + + {isActive ? 'Active' : 'Inactive'} + + ); + } + }, + { + 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 doGetCurrency = async ( + page: number, + limit: number, + sorting: any + // filter: any + ) => { + // sorting = sorting.length == 0 ? [{ id: 'name', desc: true }] : sorting; + // filter = filter.length == 0 ? {} : { any: filter[0].value?.toLowerCase() }; + // console.log(sorting); + const response = await GetData(`${API_URL}/dashboard/currency/`, { + limit, + page: page + 1, + with_deleted: false, + order_field: sorting[0].id, + order_direction: sorting[0].desc ? 'ASC' : 'DESC' + // filter: JSON.stringify(filter) + }); + // console.log(response?.data); + return { data: response?.data.list, totalCount: response?.data.total_count }; + }; + + return ( +
+ + + +
+ } + sorting={[{ id: 'ID', desc: true }]} + serverSide={true} + onFetchData={({ pageIndex, pageSize, sorting }) => + doGetCurrency(pageIndex, pageSize, sorting) + } + > + {children} + +
+
+
+ ); +}; + +export { ManageCurrencyContext, ManageCurrencyContextProvider }; +export type { Currency }; diff --git a/src/pages/master/currency/hooks/useManageCurrencyContext.tsx b/src/pages/master/currency/hooks/useManageCurrencyContext.tsx new file mode 100644 index 0000000..6a9c80b --- /dev/null +++ b/src/pages/master/currency/hooks/useManageCurrencyContext.tsx @@ -0,0 +1,13 @@ +import { useContext } from 'react'; +import {ManageCurrencyContextProvider } from './ManageCurrencyContext'; +import { ManageCurrencyContext } from '../hooks/ManageCurrencyContext'; + +const useManageCurrencyContext = () => { + const context = useContext(ManageCurrencyContext); + + if (!context) throw new Error('useManageCurrencyContext must be used within AuthProvider'); + + return context; +}; + +export { useManageCurrencyContext }; diff --git a/src/routing/AppRoutingSetup.tsx b/src/routing/AppRoutingSetup.tsx index 1b0896b..ae1b8ed 100644 --- a/src/routing/AppRoutingSetup.tsx +++ b/src/routing/AppRoutingSetup.tsx @@ -36,6 +36,7 @@ import ConversionMaster from '@/pages/master/conversion/ConversionMaster'; import WalletRuleMaster from '@/pages/master/walletRule/WalletRuleMaster'; import WalletHistory from '@/pages/wallet/wallet-history/WalletHistory'; import WalletMaster from '@/pages/master/wallet/WalletMaster'; +import CurrencyMaster from '@/pages/master/currency/CurrencyMaster'; const AppRoutingSetup = (): ReactElement => { return ( @@ -58,6 +59,8 @@ const AppRoutingSetup = (): ReactElement => { /> } /> + } /> + } /> } />