fixing manage user & add field customer

This commit is contained in:
Raja Oktafrianto
2025-04-12 10:54:56 +07:00
parent 211392c7b3
commit 115fb9c001
6 changed files with 231 additions and 12 deletions

View File

@ -23,6 +23,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue SelectValue
} from '@/components/ui/select'; } from '@/components/ui/select';
import { doSaveLogActivity } from '@/actions/GlobalActions';
const API_URL = apiConfig.service_master_data; const API_URL = apiConfig.service_master_data;
@ -75,6 +76,13 @@ const AddDialog = () => {
resetForm(); resetForm();
reload(); reload();
toast.success('Reward Create successfully!'); toast.success('Reward Create successfully!');
const createActivity = {
module: 'Manage Reward',
description: `Create New Reward => ${formField.name}`,
action: 'C'
};
doSaveLogActivity(createActivity);
} else { } else {
toast.error('Failed to create reward.'); toast.error('Failed to create reward.');
setAlert({ show: true, message: 'Failed to create reward. Please try again.' }); setAlert({ show: true, message: 'Failed to create reward. Please try again.' });

View File

@ -13,6 +13,7 @@ import {
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { DialogDescription } from '@radix-ui/react-dialog'; import { DialogDescription } from '@radix-ui/react-dialog';
import { useManageRewardContext } from '../hooks/useManageRewardContext'; import { useManageRewardContext } from '../hooks/useManageRewardContext';
import { doSaveLogActivity } from '@/actions/GlobalActions';
const API_URL = apiConfig.service_master_data; const API_URL = apiConfig.service_master_data;
@ -42,6 +43,13 @@ const DeleteDialog = () => {
handleDeleteDialog(false, null); handleDeleteDialog(false, null);
toast.success('Success Delete Reward'); toast.success('Success Delete Reward');
reload(); reload();
const deleteActivity = {
module: 'Manage Reward',
description: `Delete Reward => ${selectedReward}`,
action: 'D'
};
doSaveLogActivity(deleteActivity);
} else { } else {
setAlert({ show: true, message: response?.message }); setAlert({ show: true, message: response?.message });
toast.error('Failed Delete Reward'); toast.error('Failed Delete Reward');

View File

@ -23,6 +23,7 @@ import {
SelectValue SelectValue
} from '@/components/ui/select'; } from '@/components/ui/select';
import { useManageRewardContext } from '../hooks/useManageRewardContext'; import { useManageRewardContext } from '../hooks/useManageRewardContext';
import { doSaveLogActivity } from '@/actions/GlobalActions';
const API_URL = apiConfig.service_master_data; const API_URL = apiConfig.service_master_data;
@ -74,6 +75,13 @@ const EditDialog = () => {
handleEditDialog(false, null); handleEditDialog(false, null);
toast.success('Success Update Reward'); toast.success('Success Update Reward');
reload(); reload();
const editActivity = {
module: 'Manage Reward',
description: `Edit Reward => ${formField.name}`,
action: 'U'
};
doSaveLogActivity(editActivity);
} else { } else {
toast.error('Error Update Reward'); toast.error('Error Update Reward');
setAlert({ show: true, message: 'Failed to Update Reward. Please try again.' }); setAlert({ show: true, message: 'Failed to Update Reward. Please try again.' });

View File

@ -15,6 +15,16 @@ import {
DialogHeader, DialogHeader,
DialogTitle DialogTitle
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList
} from '@/components/ui/command';
import { ChevronDown } from 'lucide-react';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { useUserContext } from '../hooks'; import { useUserContext } from '../hooks';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@ -25,6 +35,14 @@ import { useCallApi } from '@/hooks';
import { doSaveLogActivity } from '@/actions/GlobalActions'; import { doSaveLogActivity } from '@/actions/GlobalActions';
import clsx from 'clsx'; import clsx from 'clsx';
export interface CustomerProps {
id: string;
msisdn: string;
email: string;
fullname: string;
username: string;
}
interface RoleListProps { interface RoleListProps {
id: string; id: string;
name: string; name: string;
@ -42,7 +60,9 @@ interface CreateUserParams {
status: string; status: string;
} }
const API_URL_CUSTOMER = apiConfig.service_customer;
const API_URL = apiConfig.service_dashboard; const API_URL = apiConfig.service_dashboard;
type PasswordType = 'password' | 'retype_password'; type PasswordType = 'password' | 'retype_password';
const AddDialog = () => { const AddDialog = () => {
@ -62,7 +82,8 @@ const AddDialog = () => {
retype_password: '', retype_password: '',
name: '', name: '',
id_role: '', id_role: '',
status: '' status: '',
customerid: ''
}; };
const [formField, setFormField] = useState(initialState); const [formField, setFormField] = useState(initialState);
const [showPassword, setShowPassword] = useState({ const [showPassword, setShowPassword] = useState({
@ -71,6 +92,8 @@ const AddDialog = () => {
}); });
const [messagePassword, setMessagePassword] = useState(true); const [messagePassword, setMessagePassword] = useState(true);
const [open, setOpen] = useState(false);
const [customers, setCustomers] = useState<CustomerProps[]>([]);
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [passwordErrors, setPasswordErrors] = useState<string[]>([]); const [passwordErrors, setPasswordErrors] = useState<string[]>([]);
@ -155,6 +178,23 @@ const AddDialog = () => {
// console.log('ini data user_role:', response?.data); // console.log('ini data user_role:', response?.data);
}, []); }, []);
const getCustomerList = async (sorting: any) => {
try {
sorting = sorting.length == 0 ? [{ id: 'name', desc: false }] : sorting;
const response = await GetData(`${API_URL_CUSTOMER}/customer/list`, {
limit: 100,
page: 1,
with_deleted: false,
order_field: sorting[0].id,
order_direction: sorting[0].desc == false ? 'ASC' : 'DESC'
});
setCustomers(response?.data.list);
} catch (error) {
console.error('Error fetching customer', error);
}
};
useEffect(() => { useEffect(() => {
fetchRoles(); fetchRoles();
}, [fetchRoles]); }, [fetchRoles]);
@ -162,7 +202,7 @@ const AddDialog = () => {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); e.preventDefault();
console.log('Form data before submit:', formField); // console.log('Form data before submit:', formField);
if ( if (
formField.email.trim() === '' || formField.email.trim() === '' ||
@ -171,7 +211,8 @@ const AddDialog = () => {
formField.retype_password.trim() === '' || formField.retype_password.trim() === '' ||
formField.name.trim() === '' || formField.name.trim() === '' ||
formField.id_role.trim() === '' || formField.id_role.trim() === '' ||
formField.status.trim() === '' formField.status.trim() === '' ||
formField.customerid.trim() === ''
) { ) {
setAlert({ show: true, message: 'Please fill name field.' }); setAlert({ show: true, message: 'Please fill name field.' });
return; return;
@ -196,6 +237,10 @@ const AddDialog = () => {
} }
}, [showAddDialog]); }, [showAddDialog]);
useEffect(() => {
getCustomerList([{ id: 'id', desc: false }]);
}, []);
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] }));
@ -262,6 +307,58 @@ const AddDialog = () => {
</div> </div>
</div> </div>
<div className="w-full">
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label className="form-label flex items-center gap-1 max-w-56">Customer</label>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<button
type="button"
className="input col-span-5 text-left flex justify-between"
style={{ color: 'inherit' }}
>
<span>
{customers.find((customer) => customer.id === formField.customerid)
?.username || 'Select Customer'}
</span>
<ChevronDown className="w-4 h-4 opacity-70" />
</button>
</PopoverTrigger>
<PopoverContent className="w-[400px] p-0">
<Command>
<CommandInput placeholder="Search Customer..." />
<CommandList
className="max-h-[300px] overflow-y-auto"
style={{ touchAction: 'pan-y' }}
onWheel={(e) => {
e.currentTarget.scrollTop += e.deltaY;
}}
>
<CommandEmpty>No Customer found.</CommandEmpty>
<CommandGroup>
{customers.map((customer) => (
<CommandItem
key={customer.id}
value={customer.username}
onSelect={() => {
setFormField({
...formField,
customerid: customer.id
});
setOpen(false);
}}
>
{customer.username}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</div>
</div>
<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">
<label className="form-label flex items-center gap-1 max-w-56">Email</label> <label className="form-label flex items-center gap-1 max-w-56">Email</label>

View File

@ -6,7 +6,15 @@ import {
SelectTrigger, SelectTrigger,
SelectValue SelectValue
} from '@/components/ui/select'; } from '@/components/ui/select';
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList
} from '@/components/ui/command';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { import {
Dialog, Dialog,
DialogBody, DialogBody,
@ -15,6 +23,7 @@ import {
DialogHeader, DialogHeader,
DialogTitle DialogTitle
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import { CustomerProps } from './AddDialog';
import { useUserContext } from '../hooks'; import { useUserContext } from '../hooks';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@ -30,6 +39,7 @@ interface RoleListProps {
status: string; status: string;
} }
const API_URL_CUSTOMER = apiConfig.service_customer;
const API_URL = apiConfig.service_dashboard; const API_URL = apiConfig.service_dashboard;
const initialState = { const initialState = {
@ -37,7 +47,8 @@ const initialState = {
username: '', username: '',
email: '', email: '',
id_role: '', id_role: '',
status: '' status: '',
customerid: ''
}; };
const EditDialog = () => { const EditDialog = () => {
@ -45,7 +56,9 @@ const EditDialog = () => {
const { showEditDialog, selectedUser, handleEditDialog } = useUserContext(); const { showEditDialog, selectedUser, handleEditDialog } = useUserContext();
const { reload } = useDataGrid(); const { reload } = useDataGrid();
const { GetData, PutData } = useCallApi(); const { GetData, PutData } = useCallApi();
const [open, setOpen] = useState(false);
const [roles, setRoles] = useState<RoleListProps[]>([]); const [roles, setRoles] = useState<RoleListProps[]>([]);
const [customers, setCustomers] = useState<CustomerProps[]>([]);
const [alert, setAlert] = useState({ const [alert, setAlert] = useState({
show: false, show: false,
message: '' message: ''
@ -109,9 +122,27 @@ const EditDialog = () => {
} }
}, []); }, []);
const getCustomerList = async (sorting: any) => {
try {
sorting = sorting.length == 0 ? [{ id: 'name', desc: false }] : sorting;
const response = await GetData(`${API_URL_CUSTOMER}/customer/list`, {
limit: 100,
page: 1,
with_deleted: false,
order_field: sorting[0].id,
order_direction: sorting[0].desc == false ? 'ASC' : 'DESC'
});
// console.log('CUSTOMER: ', response?.data);
setCustomers(response?.data.list);
} catch (error) {
console.error('Error fetching customer', 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 });
// console.log('User detail response:', response); console.log('User detail response:', response?.data);
if (response?.status) { if (response?.status) {
setFormField((prev) => ({ setFormField((prev) => ({
@ -120,8 +151,10 @@ const EditDialog = () => {
username: response.data.username, username: response.data.username,
email: response.data.email, email: response.data.email,
id_role: response.data.idRole, id_role: response.data.idRole,
status: response.data.status status: response.data.status,
customerid: response.data.customerid
})); }));
console.log('Customer ID dari user detail:', response.data.customerid);
} else { } else {
setFormField((prev) => ({ setFormField((prev) => ({
...prev, ...prev,
@ -129,7 +162,8 @@ const EditDialog = () => {
username: '', username: '',
email: '', email: '',
id_role: '0', id_role: '0',
status: '' status: '',
customerid: ''
})); }));
} }
// console.log('Fetched ID Role:', response?.data.id_role); // console.log('Fetched ID Role:', response?.data.id_role);
@ -141,6 +175,10 @@ const EditDialog = () => {
} }
}, [selectedUser]); }, [selectedUser]);
useEffect(() => {
getCustomerList([{ id: 'id', desc: false }]);
}, []);
useEffect(() => { useEffect(() => {
if (showEditDialog === false) { if (showEditDialog === false) {
resetForm(); resetForm();
@ -192,6 +230,55 @@ const EditDialog = () => {
)} )}
<form onSubmit={doUpdateUser}> <form onSubmit={doUpdateUser}>
<div className="card-body grid gap-5 p-0"> <div className="card-body grid gap-5 p-0">
<div className="w-full">
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label className="form-label flex items-center gap-1 max-w-56">Customer</label>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<button
type="button"
className="input col-span-5 text-left"
style={{ color: 'inherit' }}
>
{customers.find((customer) => customer.id === formField.customerid)
?.username || 'Select Customer'}
</button>
</PopoverTrigger>
<PopoverContent className="w-[400px] p-0">
<Command>
<CommandInput placeholder="Search Customer..." />
<CommandList
className="max-h-[300px] overflow-y-auto"
style={{ touchAction: 'pan-y' }}
onWheel={(e) => {
e.currentTarget.scrollTop += e.deltaY;
}}
>
<CommandEmpty>No Customer found.</CommandEmpty>
<CommandGroup>
{customers.map((customer) => (
<CommandItem
key={customer.id}
value={customer.username}
onSelect={() => {
setFormField({
...formField,
customerid: customer.id
});
setOpen(false);
}}
>
{customer.username}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</div>
</div>
<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">
<label className="form-label flex items-center gap-1 max-w-56">Name</label> <label className="form-label flex items-center gap-1 max-w-56">Name</label>

View File

@ -27,6 +27,7 @@ interface SelectedUser {
role: string; role: string;
new_password: string; new_password: string;
check_new_password: string; check_new_password: string;
customer: string;
} }
const initialProps: ContextProps = { const initialProps: ContextProps = {
@ -83,6 +84,16 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode })
enableSorting: true, enableSorting: true,
enableHiding: false enableHiding: false
}, },
{
accessorFn: (row) => row.customer?.username,
id: 'customer',
header: ({ column }) => <DataGridColumnHeader title="Customer" column={column} />,
enableSorting: false,
enableHiding: false,
meta: {
headerClassName: 'w-[300px]'
}
},
{ {
accessorFn: (row) => row.email, accessorFn: (row) => row.email,
id: 'email', id: 'email',
@ -100,7 +111,7 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode })
enableSorting: false, enableSorting: false,
enableHiding: false, enableHiding: false,
meta: { meta: {
headerClassName: 'w-[350px]' headerClassName: 'w-[250px]'
} }
}, },
{ {
@ -179,7 +190,7 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode })
); );
const doGetListData = async (page: number, limit: number, sorting: any, filter: any) => { const doGetListData = async (page: number, limit: number, sorting: any, filter: any) => {
sorting = sorting.length == 0 ? [{ id: 'username', desc: false }] : sorting; sorting = sorting.length == 0 ? [{ id: 'Users.username', desc: false }] : sorting;
filter = filter.length == 0 ? {} : { any: filter[0].value.toLowerCase() }; filter = filter.length == 0 ? {} : { any: filter[0].value.toLowerCase() };
const response = await GetData(`${API_URL}/user/list`, { const response = await GetData(`${API_URL}/user/list`, {
limit: limit, limit: limit,
@ -189,7 +200,7 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode })
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 };
}; };
@ -215,7 +226,7 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode })
pagination={{ size: 10 }} pagination={{ size: 10 }}
toolbar={<ListToolBar />} toolbar={<ListToolBar />}
layout={{ card: true }} layout={{ card: true }}
sorting={[{ id: 'username', desc: false }]} sorting={[{ id: 'Users.username', desc: false }]}
serverSide={true} serverSide={true}
onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) =>
doGetListData(pageIndex, pageSize, sorting, columnFilters) doGetListData(pageIndex, pageSize, sorting, columnFilters)