From ecff71247ccb59d8518bce42a8601eca6010d126 Mon Sep 17 00:00:00 2001 From: Raja Oktafrianto Date: Tue, 8 Apr 2025 11:15:34 +0700 Subject: [PATCH] update manage reward --- .../master/currency/blocks/EditDialog.tsx | 6 +- .../master/currency/blocks/ListToolbar.tsx | 2 +- .../products/hooks/ManageProductsContext.tsx | 3 + src/pages/master/reward/Reward.tsx | 37 +++ src/pages/master/reward/blocks/AddDialog.tsx | 216 +++++++++++++++++ .../master/reward/blocks/DeleteDialog.tsx | 77 ++++++ src/pages/master/reward/blocks/EditDialog.tsx | 222 ++++++++++++++++++ .../master/reward/blocks/ListToolbar.tsx | 44 ++++ .../reward/hooks/ManageRewardContext.tsx | 213 +++++++++++++++++ .../reward/hooks/useManageRewardContext.tsx | 12 + src/pages/master/sucos/blocks/EditDialog.tsx | 2 + .../user/manage-user/blocks/DeleteDialog.tsx | 4 +- .../manage-user/hooks/ManageUserContext.tsx | 3 +- src/routing/AppRoutingSetup.tsx | 3 + 14 files changed, 837 insertions(+), 7 deletions(-) create mode 100644 src/pages/master/reward/Reward.tsx create mode 100644 src/pages/master/reward/blocks/AddDialog.tsx create mode 100644 src/pages/master/reward/blocks/DeleteDialog.tsx create mode 100644 src/pages/master/reward/blocks/EditDialog.tsx create mode 100644 src/pages/master/reward/blocks/ListToolbar.tsx create mode 100644 src/pages/master/reward/hooks/ManageRewardContext.tsx create mode 100644 src/pages/master/reward/hooks/useManageRewardContext.tsx diff --git a/src/pages/master/currency/blocks/EditDialog.tsx b/src/pages/master/currency/blocks/EditDialog.tsx index 14b3be6..30cb75b 100644 --- a/src/pages/master/currency/blocks/EditDialog.tsx +++ b/src/pages/master/currency/blocks/EditDialog.tsx @@ -97,9 +97,9 @@ const EditDialog = () => { setFormField((prev) => ({ ...prev, status: response.data.status, - code:response.data.code, - name:response.data.name, - prefix:response.data.prefix, + code: response.data.code, + name: response.data.name, + prefix: response.data.prefix })); } // console.log('form fieldd Transaction Type: ', formField); diff --git a/src/pages/master/currency/blocks/ListToolbar.tsx b/src/pages/master/currency/blocks/ListToolbar.tsx index 9cf61a3..0a71717 100644 --- a/src/pages/master/currency/blocks/ListToolbar.tsx +++ b/src/pages/master/currency/blocks/ListToolbar.tsx @@ -15,7 +15,7 @@ const ListToolbar = () => { table.getColumn('name')?.setFilterValue(event.target.value)} /> diff --git a/src/pages/master/products/hooks/ManageProductsContext.tsx b/src/pages/master/products/hooks/ManageProductsContext.tsx index 2de1f75..c3f1a9c 100644 --- a/src/pages/master/products/hooks/ManageProductsContext.tsx +++ b/src/pages/master/products/hooks/ManageProductsContext.tsx @@ -80,6 +80,9 @@ const ManageProductsContextProvider = ({ children }: { children: React.ReactNode enableHiding: false, meta: { headerClassName: 'w-[250px]' + }, + filterFn: (row, id, value) => { + return row.original.products_name.toLowerCase().includes(value.toLowerCase()); } }, { diff --git a/src/pages/master/reward/Reward.tsx b/src/pages/master/reward/Reward.tsx new file mode 100644 index 0000000..32e8691 --- /dev/null +++ b/src/pages/master/reward/Reward.tsx @@ -0,0 +1,37 @@ +import AddDialog from './blocks/AddDialog'; +import EditDialog from './blocks/EditDialog'; +import DeleteDialog from './blocks/DeleteDialog'; +import { ManageRewardContextProvider } from './hooks/ManageRewardContext'; +import { Container, DataGridInner } from '@/components'; +import { Breadcrumbs, Link } from '@mui/material'; + +const RewardMaster = () => { + return ( + + +

Manage Reward

+ + + Dashboard + + + + Master Data + + + + Manage Reward + + +
+ +
+ + + +
+
+ ); +}; + +export default RewardMaster; diff --git a/src/pages/master/reward/blocks/AddDialog.tsx b/src/pages/master/reward/blocks/AddDialog.tsx new file mode 100644 index 0000000..40df444 --- /dev/null +++ b/src/pages/master/reward/blocks/AddDialog.tsx @@ -0,0 +1,216 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { + Dialog, + DialogBody, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog'; +import { Alert, KeenIcon, useDataGrid } from '@/components'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { apiConfig } from '@/config/api.config'; +import { toast } from 'sonner'; +import { getAuth } from '@/auth'; +import { useCallApi } from '@/hooks'; +import { useManageRewardContext } from '../hooks/useManageRewardContext'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from '@/components/ui/select'; + +const API_URL = apiConfig.service_master_data; + +const AddDialog = () => { + const parentRef = useRef(null); + const { showAddDialog, handleAddDialog } = useManageRewardContext(); + const { reload } = useDataGrid(); + const { PostData } = useCallApi(); + const parsedUser = getAuth()?.user; + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + + const initialState = { + name: '', + type: '', + amount: 0, + 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 doCreateReward = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + console.log('Data yang akan dikirim:', formField); + const response = await PostData(`${API_URL}/reward/create`, formField); + + if (response?.status) { + handleAddDialog(false); + resetForm(); + reload(); + toast.success('Reward Create successfully!'); + } else { + toast.error('Failed to create reward.'); + setAlert({ show: true, message: 'Failed to create reward. Please try again.' }); + } + }, + [formField] + ); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if ( + formField.name.trim() === '' || + formField.type.trim() === '' || + formField.amount === 0 || + formField.status.trim() === '' + ) { + setAlert({ show: true, message: 'Please fill name field.' }); + return; + } + + doCreateReward(e); + // console.log(formField); + setAlert({ show: false, message: '' }); + }; + + const handleReset = () => { + resetForm(); + 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)}> + + + Reward - Create + + + +
+ {alert.show && {alert.message}} +
+
+
+ + + + setFormField((prev) => ({ ...prev, name: target.value })) + } + /> +
+
+ + + + setFormField((prev) => ({ ...prev, type: target.value })) + } + /> +
+
+ + + { + const value = parseFloat(e.target.value); + setFormField({ + ...formField, + amount: isNaN(value) ? 0 : value + }); + }} + /> +
+
+ + +
+ +
+
+
+ + +
+
+
+
+
+
+
+ ); +}; + +export default AddDialog; diff --git a/src/pages/master/reward/blocks/DeleteDialog.tsx b/src/pages/master/reward/blocks/DeleteDialog.tsx new file mode 100644 index 0000000..dd7639c --- /dev/null +++ b/src/pages/master/reward/blocks/DeleteDialog.tsx @@ -0,0 +1,77 @@ +import { Alert, useDataGrid } from '@/components'; +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'; +import { useManageRewardContext } from '../hooks/useManageRewardContext'; + +const API_URL = apiConfig.service_master_data; + +const DeleteDialog = () => { + const { showDeleteDialog, handleDeleteDialog, selectedReward } = useManageRewardContext(); + const { reload } = useDataGrid(); + const { DeleteData } = useCallApi(); + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + + const doDeleteReward = useCallback(async () => { + if (!selectedReward) { + toast.error('No Reward selected'); + return; + } + const response = await DeleteData(`${API_URL}/reward/delete/${selectedReward}`, { + id: selectedReward + }); + + if (response?.status) { + setAlert({ show: false, message: '' }); + handleDeleteDialog(false, null); + toast.success('Success Delete Reward'); + reload(); + } else { + setAlert({ show: true, message: response?.message }); + toast.error('Failed Delete Reward'); + } + }, [selectedReward, DeleteData, handleDeleteDialog, reload]); + + 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/reward/blocks/EditDialog.tsx b/src/pages/master/reward/blocks/EditDialog.tsx new file mode 100644 index 0000000..cb3695a --- /dev/null +++ b/src/pages/master/reward/blocks/EditDialog.tsx @@ -0,0 +1,222 @@ +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 { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from '@/components/ui/select'; +import { useManageRewardContext } from '../hooks/useManageRewardContext'; + +const API_URL = apiConfig.service_master_data; + +const EditDialog = () => { + const parentRef = useRef(null); + const { showEditDialog, handleEditDialog, selectedReward } = useManageRewardContext(); + const { reload } = useDataGrid(); + const { PutData, GetData } = useCallApi(); + const parsedUser = getAuth()?.user; + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + + const initialState = { + name: '', + type: '', + amount: 0, + status: '', + updated_by: '', + updated_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 doUpdateReward = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + + const response = await PutData(`${API_URL}/reward/update/${selectedReward}`, formField); + + if (response?.status) { + resetForm(); + handleEditDialog(false, null); + toast.success('Success Update Reward'); + reload(); + } else { + toast.error('Error Update Reward'); + setAlert({ show: true, message: 'Failed to Update Reward. Please try again.' }); + } + }, + [selectedReward, formField] + ); + + const doFetchData = useCallback(async (id: string) => { + const response = await GetData(`${API_URL}/reward/getdata/${id}`, { id }); + console.log('API Response:', response); + + if (response?.status) { + setFormField((prev) => ({ + ...prev, + name: response?.data.name, + type: response?.data.type, + amount: response?.data.amount, + status: response?.data.status + })); + } + }, []); + + // const handleUpdate = (e: React.FormEvent) => { + // e.preventDefault(); + + // if ( + // formField.name === '' || + // formField.type === '' || + // formField.amount === 0 || + // formField.status === '' + // ) { + // setAlert({ show: true, message: 'Please fill in all required fields.' }); + // return; + // } + + // doUpdateReward(e); + // // console.log(formField); + // setAlert({ show: false, message: '' }); + // }; + + useEffect(() => { + // console.log('Selected Reward:', selectedReward); + if (selectedReward) { + doFetchData(selectedReward); + } + }, [selectedReward]); + + useEffect(() => { + if (showEditDialog) { + setFormField({ + ...formField, + updated_by: parsedUser.username, + updated_at: formattedTime + }); + } + }, [formattedTime]); + + useEffect(() => { + if (showEditDialog === false) { + resetForm(); + } + }, [showEditDialog]); + + return ( + handleEditDialog(open, null)}> + + + Reward - Update + + + +
+ {alert.show && {alert.message}} + +
+
+
+ + + setFormField((prev) => ({ ...prev, name: e.target.value }))} + /> +
+
+ + + setFormField({ ...formField, type: e.target.value })} + /> +
+
+ + + { + const value = parseFloat(e.target.value); + setFormField({ + ...formField, + amount: isNaN(value) ? 0 : value + }); + }} + /> +
+
+ + + +
+
+ + +
+
+
+
+
+
+
+ ); +}; + +export default EditDialog; diff --git a/src/pages/master/reward/blocks/ListToolbar.tsx b/src/pages/master/reward/blocks/ListToolbar.tsx new file mode 100644 index 0000000..6f36328 --- /dev/null +++ b/src/pages/master/reward/blocks/ListToolbar.tsx @@ -0,0 +1,44 @@ +import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components'; +import { Button } from '@/components/ui/button'; +import { useManageRewardContext } from '../hooks/useManageRewardContext'; + +const ListToolbar = () => { + const { table, reload } = useDataGrid(); + const { handleAddDialog } = useManageRewardContext(); + + return ( +
+
+
+
+ +
+
+ + + + +
+
+
+
+ ); +}; + +export default ListToolbar; diff --git a/src/pages/master/reward/hooks/ManageRewardContext.tsx b/src/pages/master/reward/hooks/ManageRewardContext.tsx new file mode 100644 index 0000000..0a62967 --- /dev/null +++ b/src/pages/master/reward/hooks/ManageRewardContext.tsx @@ -0,0 +1,213 @@ +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 Reward { + id: number; + name: string; + type: string; + amount: number; + status: string; +} + +const initialState = { + id: '', + name: '', + type: '', + amount: '', + status: '' +}; + +interface ContextProps { + showAddDialog: boolean; + handleAddDialog: (show: boolean) => void; + showEditDialog: boolean; + handleEditDialog: (show: boolean, selected_reward: string | null) => void; + showDeleteDialog: boolean; + handleDeleteDialog: (show: boolean, selected_rewad: string | null) => void; + selectedReward: string | null; + reward: string | null; +} + +const initialProps: ContextProps = { + showAddDialog: false, + handleAddDialog: (show: boolean) => {}, + showEditDialog: false, + handleEditDialog: () => {}, + showDeleteDialog: false, + handleDeleteDialog: () => {}, + selectedReward: null, + reward: null +}; + +const ManageRewardContext = createContext(initialProps); +const API_URL_MASTER_DATA = apiConfig.service_master_data; + +const ManageRewardContextProvider = ({ children }: { children: React.ReactNode }) => { + const [reward, setRewards] = useState(null); + const [showAddDialog, setShowAddDialog] = useState(false); + const [showEditDialog, setShowEditDialog] = useState(false); + const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const [selectedReward, setSelectedReward] = useState(null); + const { GetData } = useCallApi(); + + const handleAddDialog = useCallback((show: boolean) => { + setShowAddDialog(show); + }, []); + + const handleEditDialog = useCallback((show: boolean, selected_reward: string | null) => { + setShowEditDialog(show); + setSelectedReward(show ? selected_reward : null); + }, []); + + const handleDeleteDialog = useCallback((show: boolean, selected_reward: string | null) => { + setShowDeleteDialog(show); + setSelectedReward(show ? selected_reward : null); + }, []); + + const columns = useMemo[]>( + () => [ + { + accessorKey: 'name', + id: 'name', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + accessorKey: 'type', + id: 'type', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + accessorKey: 'amount', + id: 'amount', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + accessorKey: 'status', + id: 'status', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + cell: ({ row }) => { + const isActive = row.original.status === 'Y'; + + return ( + + {isActive ? 'Active' : 'Inactive'} + + ); + }, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + id: 'actions', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + cell: (data) => { + const row = data.row.original; + return ( + <> + + + + ); + }, + meta: { + heaaderClassName: 'w-[100px]', + cellClassName: 'text-center' + } + } + ], + [] + ); + + const getRewardList = 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_MASTER_DATA}/reward/list`, { + limit, + page: page + 1, + with_deleted: false, + order_field: sorting[0].id, + order_direction: sorting[0].desc == false ? 'ASC' : 'DESC', + filter: JSON.stringify(filter) + }); + // console.log('API Response:', response?.data.list); // Cek data dari API + // console.log('reward list :', response); + setRewards(response?.data.list); + return { data: response?.data.list, totalCount: response?.data.total_count }; + } catch (error) { + console.error('Error fethcing reward', error); + } + }; + + return ( + + + } + layout={{ card: true }} + sorting={[{ id: 'id', desc: false }]} + serverSide={true} + onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => + getRewardList(pageIndex, pageSize, sorting, columnFilters) + } + > + {children} + + + ); +}; + +export { ManageRewardContext, ManageRewardContextProvider }; +export type { Reward }; diff --git a/src/pages/master/reward/hooks/useManageRewardContext.tsx b/src/pages/master/reward/hooks/useManageRewardContext.tsx new file mode 100644 index 0000000..7b8a822 --- /dev/null +++ b/src/pages/master/reward/hooks/useManageRewardContext.tsx @@ -0,0 +1,12 @@ +import { useContext } from 'react'; +import { ManageRewardContext } from './ManageRewardContext'; + +const useManageRewardContext = () => { + const context = useContext(ManageRewardContext); + + if (!context) throw new Error('useManageRewardContext must be used within AuthProvider'); + + return context; +}; + +export { useManageRewardContext }; diff --git a/src/pages/master/sucos/blocks/EditDialog.tsx b/src/pages/master/sucos/blocks/EditDialog.tsx index 68b7bcd..1ff451b 100644 --- a/src/pages/master/sucos/blocks/EditDialog.tsx +++ b/src/pages/master/sucos/blocks/EditDialog.tsx @@ -118,6 +118,8 @@ const EditDialog = () => { name: '' })); } + console.log('Responnya nih:', response); + console.log('Data Posto nya nih:', response?.data?.posto); }, []); const handleUpdate = (e: React.FormEvent) => { diff --git a/src/pages/settings/user/manage-user/blocks/DeleteDialog.tsx b/src/pages/settings/user/manage-user/blocks/DeleteDialog.tsx index 0dce581..e715d69 100644 --- a/src/pages/settings/user/manage-user/blocks/DeleteDialog.tsx +++ b/src/pages/settings/user/manage-user/blocks/DeleteDialog.tsx @@ -51,7 +51,7 @@ const DeleteDialog = () => {

Are you sure?

you will delete this data! -
+ {/*
{ setEnforce(e.target.checked); }} /> -
+
*/}
{alert.show && ( diff --git a/src/pages/settings/user/manage-user/hooks/ManageUserContext.tsx b/src/pages/settings/user/manage-user/hooks/ManageUserContext.tsx index 231cbc6..de16f67 100644 --- a/src/pages/settings/user/manage-user/hooks/ManageUserContext.tsx +++ b/src/pages/settings/user/manage-user/hooks/ManageUserContext.tsx @@ -120,7 +120,7 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode }) enableHiding: false, cell: (data: any) => { const { role } = data.row.original; - console.log('role :', role); + // console.log('role :', role); let html =

Unassigned

; if (role && role.name) html = role.name; return html; @@ -198,6 +198,7 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode }) order_direction: sorting[0].desc == false ? 'ASC' : 'DESC', filter: JSON.stringify(filter) }); + console.log('response api:', response); return { data: response?.data.list, totalCount: response?.data.total_count }; }; diff --git a/src/routing/AppRoutingSetup.tsx b/src/routing/AppRoutingSetup.tsx index ae1b8ed..b0331d0 100644 --- a/src/routing/AppRoutingSetup.tsx +++ b/src/routing/AppRoutingSetup.tsx @@ -33,6 +33,7 @@ import ProfessionMaster from '@/pages/master/profession/ProfessionMaster'; import ProductsMaster from '@/pages/master/products/ProductsMaster'; import ProviderMaster from '@/pages/master/provider/ProviderMaster'; import ConversionMaster from '@/pages/master/conversion/ConversionMaster'; +import RewardMaster from '@/pages/master/reward/Reward'; import WalletRuleMaster from '@/pages/master/walletRule/WalletRuleMaster'; import WalletHistory from '@/pages/wallet/wallet-history/WalletHistory'; import WalletMaster from '@/pages/master/wallet/WalletMaster'; @@ -61,6 +62,8 @@ const AppRoutingSetup = (): ReactElement => { } /> + } /> + } /> } />