add agent balance

This commit is contained in:
unknown
2025-05-13 14:10:09 +07:00
parent 61b793dbad
commit 22e3624965
5 changed files with 525 additions and 5 deletions

View File

@ -0,0 +1,278 @@
import { DataTable } from '@/components/ui/DataTable';
import { getColumns } from './Column';
import { apiConfig } from '@/config/api.config';
import axios, { AxiosResponse } from 'axios';
import { Helmet } from 'react-helmet';
import {
DialogContent,
MenuItem,
Radio,
RadioGroup,
FormControlLabel,
FormControl,
Dialog,
DialogTitle,
Typography,
Button,
Box,
Breadcrumbs,
Link
} from '@mui/material';
import { useState, useEffect } from 'react';
import CloseIcon from '@mui/icons-material/Close';
import Divider from '@mui/material/Divider';
import ConfirmDialog from '@/components/confirm';
import { toast } from 'sonner';
import { Container, DataGridLoader, DataGridProvider, LoaderTransparant } from '@/components';
import { ListToolBar } from './ListToolbar';
import { RefreshCw } from 'lucide-react';
import { setgroups } from 'process';
const BASE_URL = apiConfig.service_customer;
let initBalance = {
id: '',
msisdn: '',
fullname: '',
username: '',
agent_name: '',
balance_emoney: '',
balance_merchant: '',
balance_point: '',
balance_deposit: ''
};
const AgentBalance = () => {
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [loading, setLoading] = useState(false);
const [dataBalance, setDataBalance] = useState([]);
const [formData, setFormData] = useState(initBalance);
const [pageIndex, setPageIndex] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [dialogType, setDialogType] = useState('');
const [dialogOpen, setDialogOpen] = useState(false);
const [isReloading, setIsReloading] = useState(false);
useEffect(() => {
fetchAgentBalance();
}, []);
async function fetchAgentBalance(): Promise<void> {
setIsReloading(true);
try {
let balances = await axios.get(`${BASE_URL}/customer/agent-balance`, {
params: {
limit: 20,
page: 1,
with_deleted: false,
order_field: 'created_at',
order_direction: 'DESC'
}
});
let temp = 1;
let resBalance = balances.data.data.list.map((el: any) => {
el.no = temp++;
return el;
});
setDataBalance(resBalance);
} catch (error: any) {
alert(error.message);
console.log(error);
} finally {
setIsReloading(false);
}
}
const openDialog = () => setIsDialogOpen(true);
const closeDialog = () => {
setIsDialogOpen(false);
setFormData(initBalance);
};
// const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// setFormData({
// ...formData,
// [e.target.name]: e.target.value
// });
// };
// const handleSubmit = async (e: React.FormEvent) => {
// e.preventDefault();
// // if (!formData.groupName) return toast.warning(`Group name can not be empty!`);
// setIsDialogOpen(false);
// setDialogOpen(true);
// };
function createGroup() {
setDialogType('create');
openDialog();
}
const handleUpdate = (group: any) => {
setDialogType('update');
setIsDialogOpen(true);
};
const handleReload = () => {
setIsReloading(true);
fetchAgentBalance();
};
// const handleYes = async () => {
// setIsReloading(true);
// try {
// if (dialogType === 'create') {
// await axios.post(`${BASE_URL}/groups/create`, {
// name: formData.groupName,
// status: formData.status,
// description: formData.description,
// created_at: new Date()
// });
// toast.success(`Success create group`);
// }
// } catch (error: any) {
// console.log(error);
// toast.error(error.message);
// } finally {
// setDialogOpen(false);
// closeDialog();
// await fetchAgentBalance();
// setLoading(false);
// }
// };
// function setShowAddDialog(el: any) {
// setIsDialogOpen(el);
// }
if (loading) return <LoaderTransparant />;
return (
<>
<Helmet>
<title>TPAY | Agent Balance</title>
</Helmet>
<Container>
{/* <ConfirmDialog
open={dialogOpen}
onClose={() => setDialogOpen(false)}
title="Confirm Action"
content={''}
onYes={handleYes}
onNo={() => setDialogOpen(false)}
/> */}
<h1 className="text-xl font-medium leading-none text-gray-900 mb-5">Agent Balances</h1>
<Breadcrumbs>
<Link underline="none" color="inherit" href="/">
<span className="text-sm hover:underline">Dashboard</span>
</Link>
<Link underline="none" color="inherit">
<span className="text-sm">Members</span>
</Link>
<Link underline="none" color="inherit">
<span className="text-sm">Agent Balances</span>
</Link>
</Breadcrumbs>
{/* <div className="w-full overflow-x-auto px-4"> */}
<div className="grid gap-5 lg:gap-7.5 mt-5 relative">
{isReloading && (
<DataGridLoader />
)}
<DataGridProvider
data={dataBalance}
columns={getColumns(handleUpdate)}
pagination={{ size: 10 }}
toolbar={
<ListToolBar
createGroup={createGroup}
onReload={handleReload}
isReloading={isReloading}
/>
}
layout={{ card: true }}
serverSide={false}
onRowSelectionChange={(selected, table: any) => {
const selectedRow = table.getSelectedRowModel().rows[0];
if (selectedRow) handleUpdate(selectedRow.original);
}}
/>
</div>
{/* </div> */}
{/* <Dialog open={isDialogOpen} onClose={closeDialog}>
<DialogContent className="w-full">
<div className="flex justify-between">
<DialogTitle>
{dialogType === 'create' ? 'Create New Group' : 'Update Group'}
</DialogTitle>
<Box display="flex" justifyContent="flex-end">
<Button
variant="outlined"
sx={{ borderColor: 'white', color: 'grey' }}
onClick={closeDialog}
>
<CloseIcon />
</Button>
</Box>
</div>
<Divider />
<div className="p-5 mt-5">
<form onSubmit={handleSubmit} className="flex flex-col gap-4 w-full">
<div className="grid grid-cols-4 items-center gap-4 w-full">
<label className="form-label text-sm">
<span className="text-red-500">*</span>Group Name:
</label>
<input
type="text"
name="groupName"
className="input w-full col-span-3"
value={formData.groupName}
onChange={handleChange}
/>
</div>
<div className="grid grid-cols-4 items-center gap-4 w-full">
<label className="form-label text-sm">
<span className="text-red-500">*</span>Active Status:
</label>
<FormControl>
<RadioGroup name="status" row value={formData.status} onChange={handleChange}>
<FormControlLabel
value="Y"
checked={formData.status === 'Y'}
control={<Radio />}
label="Yes"
/>
<FormControlLabel
value="N"
checked={formData.status === 'N'}
control={<Radio />}
label="No"
/>
</RadioGroup>
</FormControl>
</div>
<div className="grid grid-cols-4 items-center gap-4 w-full">
<label className="form-label text-sm">
<span className="text-red-500">*</span>Description:
</label>
<input
type="text"
name="description"
className="input w-full col-span-3"
value={formData.description}
onChange={handleChange}
/>
</div>
<Button type="submit">Submit</Button>
</form>
</div>
</DialogContent>
</Dialog> */}
</Container>
</>
);
};
export default AgentBalance;

View File

@ -0,0 +1,146 @@
import { KeenIcon } from '@/components';
import { Button } from '@/components/ui/button';
import { ColumnDef } from '@tanstack/react-table';
import { ArrowUpDown } from 'lucide-react';
import moment from 'moment';
export type AgentBalance = {
id: string,
msisdn: string,
fullname: string,
username: string,
agent_name: string,
balance_emoney: string,
balance_merchant: string,
balance_point: string,
balance_deposit: string
};
export const getColumns = (handleUpdate: (data: any) => void): ColumnDef<AgentBalance>[] => [
{
accessorKey: 'no',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
No.
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
accessorKey: 'msisdn',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Phone Number
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
accessorKey: 'fullname',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Fullname
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
accessorKey: 'username',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Username
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
accessorKey: 'agent_name',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Merchant Name
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
accessorKey: 'balance_emoney',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
E-money
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
accessorKey: 'balance_merchant',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Merchant
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
accessorKey: 'balance_point',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Point
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
accessorKey: 'balance_deposit',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Deposit
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
];

View File

@ -0,0 +1,82 @@
import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components';
import { Button } from '@/components/ui/button';
import React, { useState } from 'react';
interface ListToolBarProps {
createGroup: () => void;
onReload: () => void;
isReloading: boolean;
}
const ListToolBar = ({ createGroup, onReload, isReloading }: ListToolBarProps) => {
const { table } = useDataGrid();
const [usernameFilter, setUsernameFilter] = useState('');
const [msisdn, setMsisdn] = useState('');
const [fullname, setFullname] = useState('');
const [merchantName, setMerchantName] = useState('');
const handleUsernameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setUsernameFilter(e.target.value);
table.getColumn('username')?.setFilterValue(e.target.value);
};
const filtermsisdn = (e: React.ChangeEvent<HTMLInputElement>) => {
setMsisdn(e.target.value);
table.getColumn('msisdn')?.setFilterValue(e.target.value);
};
const filterfullname = (e: React.ChangeEvent<HTMLInputElement>) => {
setFullname(e.target.value);
table.getColumn('fullname')?.setFilterValue(e.target.value);
};
const filtermerchantname = (e: React.ChangeEvent<HTMLInputElement>) => {
setMerchantName(e.target.value);
table.getColumn('agent_name')?.setFilterValue(e.target.value);
};
return (
<div className="card-header flex-wrap gap-2 border-b-0 px-5">
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
<div className="flex justify-between w-full items-center">
<div className="flex w-[50%] gap-3 items-center">
<label className="input input-sm w-1/3">
<KeenIcon icon="magnifier" />
<input type="text" placeholder="Search phone" value={msisdn} onChange={filtermsisdn}/>
</label>
<label className="input input-sm w-1/3">
<KeenIcon icon="magnifier" />
<input type="text" placeholder="Search fullname" value={fullname} onChange={filterfullname}/>
</label>
<label className="input input-sm w-1/3">
<KeenIcon icon="magnifier" />
<input type="text" placeholder="Search username" value={usernameFilter} onChange={handleUsernameChange}/>
</label>
<label className="input input-sm w-1/3">
<KeenIcon icon="magnifier" />
<input type="text" placeholder="Search merchant name" value={merchantName} onChange={filtermerchantname}/>
</label>
</div>
<div className="flex gap-3 items-center">
{/* <Button variant="outline" className="h-7.5 text-[0.8rem]" onClick={createGroup}>
Add Data
</Button> */}
<DefaultTooltip title={isReloading ? 'Refreshing...' : 'Refresh'} placement={'top'}>
<Button variant="outline" className="h-7.5" onClick={onReload} disabled={isReloading}>
{isReloading ? (
<div className="animate-spin">
<KeenIcon icon="arrows-circle" />
</div>
) : (
<KeenIcon icon="arrows-circle" />
)}
</Button>
</DefaultTooltip>
</div>
</div>
</div>
</div>
);
};
export { ListToolBar };

View File

@ -18,7 +18,7 @@ const BASE_URL_CUSTOMER = apiConfig.service_customer;
const BASE_URL = apiConfig.service_customer;
const ManageMembers = () => {
const [loading, setLoading] = useState(false);
// const [loading, setLoading] = useState(false);
const [members, setMembers] = useState([]);
const [selectedMember, setSelectedMember] = useState('');
const [member, setMember] = useState(initialMember);
@ -28,6 +28,9 @@ const ManageMembers = () => {
const [dialogType, setDialogType] = useState('');
const [groups, setGroups] = useState([]);
const [isReloading, setIsReloading] = useState(false);
const [currentPage, setCurrentPage] = useState(0);
const [totalItems, setTotalItems] = useState(10);
const closeDialog = () => {
setIsDialogOpen(false);
setMember(initialMember);
@ -36,6 +39,7 @@ const ManageMembers = () => {
useEffect(() => {
fetchCustomers();
fetchMasters();
}, []);
async function fetchCustomers(): Promise<void> {
@ -44,7 +48,7 @@ const ManageMembers = () => {
let customers = await axios.get(`${BASE_URL}/customer/list`, {
params: {
limit: 30,
page: 1,
page: 1 + currentPage,
with_deleted: false,
order_field: 'created_at',
order_direction: 'DESC'
@ -61,8 +65,17 @@ const ManageMembers = () => {
return el;
});
setMembers(resMembers);
// setIsReloading(false);
} catch (error: any) {
toast.error(error.message);
console.log(error);
} finally {
setIsReloading(false);
}
}
async function fetchMasters(): Promise<void> {
setIsReloading(true);
try {
let getProfession: any = await axios.get(`${BASE_URL_MASTER_DATA}/profession/list`, {
params: {
limit: 50,
@ -87,7 +100,6 @@ const ManageMembers = () => {
toast.error(error.message);
console.log(error);
} finally {
// setLoading(false);
setIsReloading(false);
}
}
@ -269,7 +281,7 @@ const ManageMembers = () => {
)}
<DataGridProvider
data={members}
pagination={{ size: 10 }}
pagination={{ size: totalItems }}
columns={getColumns(handleUpdate)}
layout={{ card: true }}
serverSide={false}