update manage user & manage reward

This commit is contained in:
Raja Oktafrianto
2025-04-10 17:45:39 +07:00
parent 42e91f376b
commit 59d74d4304
10 changed files with 295 additions and 203 deletions

View File

@ -5,11 +5,14 @@ import { Delete } from 'lucide-react';
import AddDialog from './blocks/AddDialog'; import AddDialog from './blocks/AddDialog';
import DeleteDialog from './blocks/DeleteDialog'; import DeleteDialog from './blocks/DeleteDialog';
import EditDialog from './blocks/EditDialog'; import EditDialog from './blocks/EditDialog';
import { Helmet } from 'react-helmet';
// import EditDialog from './blocks/EditDialog';
const CurrencyMaster = () => { const CurrencyMaster = () => {
return ( return (
<>
<Helmet>
<title>TPAY | Manage Currency</title>
</Helmet>
<ManageCurrencyContextProvider> <ManageCurrencyContextProvider>
<Container> <Container>
<h1 className="text-xl font-medium leading-none text-gray-900 mb-5">Currency</h1> <h1 className="text-xl font-medium leading-none text-gray-900 mb-5">Currency</h1>
@ -38,6 +41,7 @@ const CurrencyMaster = () => {
<DeleteDialog/> */} <DeleteDialog/> */}
</Container> </Container>
</ManageCurrencyContextProvider> </ManageCurrencyContextProvider>
</>
); );
}; };

View File

@ -1,37 +0,0 @@
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 (
<ManageRewardContextProvider>
<Container>
<h1 className="text-xl font-medium leading-none text-gray-900 mb-3">Manage Reward</h1>
<Breadcrumbs sx={{ mb: 2 }}>
<Link underline="none" color="inherit" href="/">
<span className="text-sm hover:underline">Dashboard</span>
</Link>
<Link underline="none" color="inherit">
<span className="text-sm">Master Data</span>
</Link>
<Link underline="none" color="inherit">
<span className="text-sm">Manage Reward</span>
</Link>
</Breadcrumbs>
<div className="grid gap-5 lg:gap-7.5">
<DataGridInner />
</div>
<AddDialog />
<EditDialog />
<DeleteDialog />
</Container>
</ManageRewardContextProvider>
);
};
export default RewardMaster;

View File

@ -0,0 +1,43 @@
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';
import { Helmet } from 'react-helmet';
const RewardMaster = () => {
return (
<>
<Helmet>
<title>TPAY | Manage Reward</title>
</Helmet>
<ManageRewardContextProvider>
<Container>
<h1 className="text-xl font-medium leading-none text-gray-900 mb-3">Reward</h1>
<Breadcrumbs sx={{ mb: 2 }}>
<Link underline="none" color="inherit" href="/">
<span className="text-sm hover:underline">Dashboard</span>
</Link>
<Link underline="none" color="inherit">
<span className="text-sm">Master Data</span>
</Link>
<Link underline="none" color="inherit">
<span className="text-sm">Manage Reward</span>
</Link>
</Breadcrumbs>
<div className="grid gap-5 lg:gap-7.5">
<DataGridInner />
</div>
<AddDialog />
<EditDialog />
<DeleteDialog />
</Container>
</ManageRewardContextProvider>
</>
);
};
export default RewardMaster;

View File

@ -1,9 +1,14 @@
import { Container, DataGridInner } from '@/components'; import { Container, DataGridInner } from '@/components';
import { LogActivityContextProvider } from './hooks'; import { LogActivityContextProvider } from './hooks';
import { Breadcrumbs, Link } from '@mui/material'; import { Breadcrumbs, Link } from '@mui/material';
import { Helmet } from 'react-helmet';
export default function LogActivityPage() { export default function LogActivityPage() {
return ( return (
<>
<Helmet>
<title>TPAY | Log Activity</title>
</Helmet>
<LogActivityContextProvider> <LogActivityContextProvider>
<Container> <Container>
<h1 className="text-xl font-medium leading-none text-gray-900 mb-3">Log Activity</h1> <h1 className="text-xl font-medium leading-none text-gray-900 mb-3">Log Activity</h1>
@ -29,5 +34,6 @@ export default function LogActivityPage() {
</div> </div>
</Container> </Container>
</LogActivityContextProvider> </LogActivityContextProvider>
</>
); );
} }

View File

@ -4,9 +4,14 @@ import { ManageUserContextProvider } from './hooks';
import { AddDialog } from './blocks/AddDialog'; import { AddDialog } from './blocks/AddDialog';
import { DeleteDialog } from './blocks/DeleteDialog'; import { DeleteDialog } from './blocks/DeleteDialog';
import { Breadcrumbs, Link } from '@mui/material'; import { Breadcrumbs, Link } from '@mui/material';
import { Helmet } from 'react-helmet';
export default function ManageUserPage() { export default function ManageUserPage() {
return ( return (
<>
<Helmet>
<title>TPAY | Manage User</title>
</Helmet>
<ManageUserContextProvider> <ManageUserContextProvider>
<Container> <Container>
<h1 className="text-xl font-medium leading-none text-gray-900 mb-3">Manage User</h1> <h1 className="text-xl font-medium leading-none text-gray-900 mb-3">Manage User</h1>
@ -35,5 +40,6 @@ export default function ManageUserPage() {
<DeleteDialog /> <DeleteDialog />
</Container> </Container>
</ManageUserContextProvider> </ManageUserContextProvider>
</>
); );
} }

View File

@ -25,6 +25,13 @@ import { useCallApi } from '@/hooks';
import { doSaveLogActivity } from '@/actions/GlobalActions'; import { doSaveLogActivity } from '@/actions/GlobalActions';
import clsx from 'clsx'; import clsx from 'clsx';
interface RoleListProps {
id: string;
name: string;
roles: string;
status: string;
}
interface CreateUserParams { interface CreateUserParams {
email: string; email: string;
username: string; username: string;
@ -40,9 +47,10 @@ type PasswordType = 'password' | 'retype_password';
const AddDialog = () => { const AddDialog = () => {
const parentRef = useRef<any | null>(null); const parentRef = useRef<any | null>(null);
const { showAddDialog, handleAddDialog, roles } = useUserContext(); const { showAddDialog, handleAddDialog } = useUserContext();
const { reload } = useDataGrid(); const { reload } = useDataGrid();
const { PostData, PutData } = useCallApi(); const { PostData, GetData } = useCallApi();
const [roles, setRoles] = useState<RoleListProps[]>([]);
const [alert, setAlert] = useState({ const [alert, setAlert] = useState({
show: false, show: false,
message: '' message: ''
@ -57,10 +65,6 @@ const AddDialog = () => {
status: '' status: ''
}; };
const [formField, setFormField] = useState(initialState); const [formField, setFormField] = useState(initialState);
const resetForm = () => {
setFormField(initialState);
setAlert({ show: false, message: '' });
};
const [showPassword, setShowPassword] = useState({ const [showPassword, setShowPassword] = useState({
password: false, password: false,
retype_password: false retype_password: false
@ -97,6 +101,87 @@ const AddDialog = () => {
}; };
}; };
const resetForm = () => {
setFormField(initialState);
setAlert({ show: false, message: '' });
};
/* actions */
const doCreateUser = useCallback(
async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const response = await PostData(`${API_URL}/user/create`, formField);
if (response?.status) {
handleAddDialog(false);
resetForm();
reload();
const createActivity = {
module: 'Manage User',
description: `Create New User => ${formField.username}`,
action: 'C'
};
doSaveLogActivity(createActivity);
toast.success('Success Create User');
} else {
toast.error('Failed to create user');
setAlert({ show: true, message: 'Failed to create user. Please try again.' });
}
},
[formField]
);
const fetchRoles = useCallback(async () => {
const params = {
limit: 100,
page: 1,
with_deleted: false,
order_field: 'name',
order_direction: 'ASC',
filter: JSON.stringify({
status: 'Y'
})
};
const response = await GetData(`${API_URL}/user_role/list`, params);
// console.log('ini data:', response);
if (response?.status) {
const roleList = response.data?.list || [];
setRoles(roleList);
} else {
setRoles(() => []);
}
// console.log('ini data user_role:', response?.data);
}, []);
useEffect(() => {
fetchRoles();
}, [fetchRoles]);
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
console.log('Form data before submit:', formField);
if (
formField.email.trim() === '' ||
formField.username.trim() === '' ||
formField.password.trim() === '' ||
formField.retype_password.trim() === '' ||
formField.name.trim() === '' ||
formField.id_role.trim() === '' ||
formField.status.trim() === ''
) {
setAlert({ show: true, message: 'Please fill name field.' });
return;
}
doCreateUser(e);
// console.log(formField);
setAlert({ show: false, message: '' });
};
useEffect(() => { useEffect(() => {
const validation = validatePassword(formField.password, formField.retype_password); const validation = validatePassword(formField.password, formField.retype_password);
setMessagePassword(validation.isValid); setMessagePassword(validation.isValid);
@ -111,38 +196,6 @@ const AddDialog = () => {
} }
}, [showAddDialog]); }, [showAddDialog]);
/* actions */
const doCreateUser = useCallback(
async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const response = await PostData(`${API_URL}/user/create`, {
...formField,
id_role: undefined
});
if (response?.status) {
const responseUserAddRole = await PutData(
`${API_URL}/user/add_role/${response?.message?.id}/${formField.id_role}`,
{}
);
resetForm();
handleAddDialog(false);
toast.success('Success Create User');
reload();
const createActivity = {
module: 'Manage User',
description: `Create New User => ${formField.username}`,
action: 'C'
};
doSaveLogActivity(createActivity);
} else {
setAlert((prev) => ({ ...prev, show: true, message: response?.message }));
}
},
[formField]
);
const togglePassword = useCallback((event: MouseEvent<HTMLButtonElement>, key: string) => { const togglePassword = useCallback((event: MouseEvent<HTMLButtonElement>, key: string) => {
event.preventDefault(); event.preventDefault();
setShowPassword((prev) => ({ ...prev, [key]: !prev[key as PasswordType] })); setShowPassword((prev) => ({ ...prev, [key]: !prev[key as PasswordType] }));
@ -177,7 +230,7 @@ const AddDialog = () => {
<h3>{alert.message}</h3> <h3>{alert.message}</h3>
</Alert> </Alert>
)} )}
<form action="" onSubmit={doCreateUser}> <form action="" onSubmit={handleSubmit}>
<div className="card-body grid gap-5 p-0"> <div className="card-body grid gap-5 p-0">
<div className="w-full"> <div className="w-full">
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> <div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
@ -231,7 +284,10 @@ const AddDialog = () => {
<div className="grow"> <div className="grow">
<Select <Select
value={formField.id_role} value={formField.id_role}
onValueChange={(id_role) => setFormField((prev) => ({ ...prev, id_role }))} onValueChange={(value) => {
console.log('Role selected:', value);
setFormField((prev) => ({ ...prev, id_role: value }));
}}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Select" /> <SelectValue placeholder="Select" />

View File

@ -1,4 +1,11 @@
import { Dialog, DialogContent, DialogFooter, DialogHeader } from '@/components/ui/dialog'; import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogDescription
} from '@/components/ui/dialog';
import { useUserContext } from '../hooks'; import { useUserContext } from '../hooks';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Alert, useDataGrid } from '@/components'; import { Alert, useDataGrid } from '@/components';
@ -42,12 +49,14 @@ const DeleteDialog = () => {
} else { } else {
setAlert((prev) => ({ ...prev, show: true, message: response?.message })); setAlert((prev) => ({ ...prev, show: true, message: response?.message }));
} }
}, [selectedUser, enforce]); }, [selectedUser, DeleteData, handleDeleteDialog, reload, enforce]);
return ( return (
<Dialog open={showDeleteDialog} onOpenChange={(open) => handleDeleteDialog(open, null)}> <Dialog open={showDeleteDialog} onOpenChange={(open) => handleDeleteDialog(open, null)}>
<DialogContent className="container-fixed max-w-md flex flex-col p-5 overflow-hidden [&>button]:hidden"> <DialogContent className="container-fixed max-w-md flex flex-col p-5 overflow-hidden [&>button]:hidden">
<DialogHeader className="p-0 border-0 block"> <DialogHeader className="p-0 border-0 block">
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
<Alert variant="warning"> <Alert variant="warning">
<h3 className="text-lg">Are you sure?</h3> <h3 className="text-lg">Are you sure?</h3>
<span className="text-sm">you will delete this data!</span> <span className="text-sm">you will delete this data!</span>

View File

@ -24,6 +24,12 @@ import { toast } from 'sonner';
import { useCallApi } from '@/hooks'; import { useCallApi } from '@/hooks';
import { doSaveLogActivity } from '@/actions/GlobalActions'; import { doSaveLogActivity } from '@/actions/GlobalActions';
interface RoleListProps {
id: string;
name: string;
status: string;
}
const API_URL = apiConfig.service_dashboard; const API_URL = apiConfig.service_dashboard;
const initialState = { const initialState = {
@ -31,15 +37,15 @@ const initialState = {
username: '', username: '',
email: '', email: '',
id_role: '', id_role: '',
id_role_old: '',
status: '' status: ''
}; };
const EditDialog = () => { const EditDialog = () => {
const parentRef = useRef<any | null>(null); const parentRef = useRef<any | null>(null);
const { showEditDialog, selectedUser, handleEditDialog, roles } = useUserContext(); const { showEditDialog, selectedUser, handleEditDialog } = useUserContext();
const { reload } = useDataGrid(); const { reload } = useDataGrid();
const { GetData, PutData } = useCallApi(); const { GetData, PutData } = useCallApi();
const [roles, setRoles] = useState<RoleListProps[]>([]);
const [alert, setAlert] = useState({ const [alert, setAlert] = useState({
show: false, show: false,
message: '' message: ''
@ -53,17 +59,11 @@ const EditDialog = () => {
}; };
/* actions */ /* actions */
const doResetForm = () => {
setAlert({ show: false, message: '' });
setFormField(initialState);
};
const doUpdateUser = useCallback( const doUpdateUser = useCallback(
async (e: React.FormEvent<HTMLFormElement>) => { async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); e.preventDefault();
const response = await PutData(`${API_URL}/user/update/${selectedUser}`, { const response = await PutData(`${API_URL}/user/update/${selectedUser}`, {
...formField, ...formField
id_role_old: undefined
}); });
if (formField.name.trim() === '') { if (formField.name.trim() === '') {
@ -90,18 +90,36 @@ const EditDialog = () => {
[selectedUser, formField] [selectedUser, formField]
); );
const doFetchUserRole = useCallback(async (sorting: any) => {
try {
sorting = sorting.length == 0 ? [{ id: 'name', desc: false }] : sorting;
const response = await GetData(`${API_URL}/user_role/list`, {
limit: 100,
page: 1,
with_deleted: false,
order_field: sorting[0].id,
order_direction: sorting[0].desc == false ? 'ASC' : 'DESC'
});
// console.log('User ID_ROLE:', response?.data.id_role);
// console.log('Role: ', response?.data.list);
setRoles(response?.data.list);
} catch (error) {
console.error('Error fetching role', error);
}
}, []);
const doFetchUserData = useCallback(async (id: string) => { const doFetchUserData = useCallback(async (id: string) => {
const response = await GetData(`${API_URL}/user/detail/${id}`, { id }); const response = await GetData(`${API_URL}/user/detail/${id}`, { id });
if (response?.status) { // console.log('User detail response:', response);
let id_role = response.data.role.id || '';
if (response?.status) {
setFormField((prev) => ({ setFormField((prev) => ({
...prev, ...prev,
name: response.data.name, name: response.data.name,
username: response.data.username, username: response.data.username,
email: response.data.email, email: response.data.email,
id_role: id_role, id_role: response.data.idRole,
id_role_old: id_role,
status: response.data.status status: response.data.status
})); }));
} else { } else {
@ -111,10 +129,10 @@ const EditDialog = () => {
username: '', username: '',
email: '', email: '',
id_role: '0', id_role: '0',
id_role_old: '',
status: '' status: ''
})); }));
} }
// console.log('Fetched ID Role:', response?.data.id_role);
}, []); }, []);
useEffect(() => { useEffect(() => {
@ -129,6 +147,23 @@ const EditDialog = () => {
} }
}, [showEditDialog]); }, [showEditDialog]);
useEffect(() => {
const fetchAllData = async () => {
await doFetchUserRole([{ id: 'name', desc: false }]);
if (selectedUser) {
await doFetchUserData(selectedUser);
}
};
fetchAllData();
}, [selectedUser]);
// console.log('ini role: ', roles);
// useEffect(() => {
// console.log('Selected User ID Role:', formField.id_role);
// // console.log('Available Roles:', roles);
// }, [formField.id_role, roles]);
return ( return (
<Dialog open={showEditDialog} onOpenChange={(open) => handleEditDialog(open, null)}> <Dialog open={showEditDialog} onOpenChange={(open) => handleEditDialog(open, null)}>
<DialogContent className="container-fixed max-w-[768px] flex flex-col p-5 overflow-hidden [&>button]:hidden"> <DialogContent className="container-fixed max-w-[768px] flex flex-col p-5 overflow-hidden [&>button]:hidden">
@ -206,7 +241,10 @@ const EditDialog = () => {
<div className="grow"> <div className="grow">
<Select <Select
value={formField.id_role} value={formField.id_role}
onValueChange={(id_role) => setFormField((prev) => ({ ...prev, id_role }))} onValueChange={(id_role) => {
// console.log('Role changed to:', id_role);
setFormField((prev) => ({ ...prev, id_role }));
}}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Select" /> <SelectValue placeholder="Select" />

View File

@ -17,7 +17,6 @@ interface ContextProps {
showDeleteDialog: boolean; showDeleteDialog: boolean;
handleDeleteDialog: (show: boolean, selected_user: string | null) => void; handleDeleteDialog: (show: boolean, selected_user: string | null) => void;
selectedUser: string | null; selectedUser: string | null;
roles: RoleListProps[];
} }
interface SelectedUser { interface SelectedUser {
@ -30,12 +29,6 @@ interface SelectedUser {
check_new_password: string; check_new_password: string;
} }
interface RoleListProps {
id: string;
name: string;
status: string;
}
const initialProps: ContextProps = { const initialProps: ContextProps = {
showSearchDialog: false, showSearchDialog: false,
handleSearchDialog: (show: boolean) => {}, handleSearchDialog: (show: boolean) => {},
@ -45,8 +38,7 @@ const initialProps: ContextProps = {
handleAddDialog: () => {}, handleAddDialog: () => {},
showDeleteDialog: false, showDeleteDialog: false,
handleDeleteDialog: () => {}, handleDeleteDialog: () => {},
selectedUser: null, selectedUser: null
roles: []
}; };
const ManageUserContext = createContext<ContextProps>(initialProps); const ManageUserContext = createContext<ContextProps>(initialProps);
@ -60,7 +52,6 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode })
const [showAddDialog, setShowAddDialog] = useState(false); const [showAddDialog, setShowAddDialog] = useState(false);
const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const [selectedUser, setSelectedUser] = useState<string | null>(null); const [selectedUser, setSelectedUser] = useState<string | null>(null);
const [roles, setRoles] = useState<RoleListProps[]>([]);
const { GetData } = useCallApi(); const { GetData } = useCallApi();
/* action */ /* action */
@ -193,39 +184,16 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode })
const response = await GetData(`${API_URL}/user/list`, { const response = await GetData(`${API_URL}/user/list`, {
limit: limit, limit: limit,
page: page + 1, page: page + 1,
with_deleted: true, with_deleted: false,
order_field: sorting[0].id, order_field: sorting[0].id,
order_direction: sorting[0].desc == false ? 'ASC' : 'DESC', order_direction: sorting[0].desc == false ? 'ASC' : 'DESC',
filter: JSON.stringify(filter) filter: JSON.stringify(filter)
}); });
console.log('response api:', response); // console.log('response api:', response);
return { data: response?.data.list, totalCount: response?.data.total_count }; return { data: response?.data.list, totalCount: response?.data.total_count };
}; };
const fetchRoles = useCallback(async () => {
const params = {
limit: 100,
page: 1,
with_deleted: false,
order_field: 'name',
order_direction: 'ASC',
filter: JSON.stringify({
status: 'Y'
})
};
const response = await GetData(`${API_URL}/user_role/list`, params);
if (response?.status) {
setRoles(() => [...response.data.list]);
} else {
setRoles(() => []);
}
}, []);
useEffect(() => {
fetchRoles();
}, [fetchRoles]);
return ( return (
<ManageUserContext.Provider <ManageUserContext.Provider
value={{ value={{
@ -236,7 +204,6 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode })
selectedUser, selectedUser,
showAddDialog, showAddDialog,
handleAddDialog, handleAddDialog,
roles,
showDeleteDialog, showDeleteDialog,
handleDeleteDialog handleDeleteDialog
}} }}

View File

@ -34,7 +34,7 @@ import ProfessionMaster from '@/pages/master/profession/ProfessionMaster';
import ProductsMaster from '@/pages/master/products/ProductsMaster'; import ProductsMaster from '@/pages/master/products/ProductsMaster';
import ProviderMaster from '@/pages/master/provider/ProviderMaster'; import ProviderMaster from '@/pages/master/provider/ProviderMaster';
import ConversionMaster from '@/pages/master/conversion/ConversionMaster'; import ConversionMaster from '@/pages/master/conversion/ConversionMaster';
import RewardMaster from '@/pages/master/reward/Reward'; import RewardMaster from '@/pages/master/reward/RewardMaster';
import WalletRuleMaster from '@/pages/master/walletRule/WalletRuleMaster'; import WalletRuleMaster from '@/pages/master/walletRule/WalletRuleMaster';
import WalletHistory from '@/pages/wallet/wallet-history/WalletHistory'; import WalletHistory from '@/pages/wallet/wallet-history/WalletHistory';
import WalletMaster from '@/pages/master/wallet/WalletMaster'; import WalletMaster from '@/pages/master/wallet/WalletMaster';