update
This commit is contained in:
@ -15,7 +15,7 @@ import {
|
||||
DialogTitle,
|
||||
Typography,
|
||||
Button,
|
||||
Box,
|
||||
Box,
|
||||
Breadcrumbs,
|
||||
Link
|
||||
} from '@mui/material';
|
||||
@ -24,6 +24,7 @@ import { toast } from 'sonner';
|
||||
import { Container, DataGridLoader, DataGridProvider, LoaderTransparant } from '@/components';
|
||||
import { ListToolBar } from './ListToolbar';
|
||||
import { toAbsoluteUrl } from '@/utils';
|
||||
|
||||
const BASE_URL = apiConfig.service_customer;
|
||||
|
||||
let initBalance = {
|
||||
@ -52,10 +53,9 @@ const AgentBalance = () => {
|
||||
const [barcode, setBarcode] = useState('');
|
||||
const [requested_date, setRequested_date] = useState('');
|
||||
const [token_legacy, setToken_legacy] = useState('');
|
||||
// const [selectedMember, setSelectedMember] = useState(initBalance);
|
||||
|
||||
useEffect(() => {
|
||||
// fetchAgentBalance();
|
||||
// fetchAgentBalance();
|
||||
}, []);
|
||||
|
||||
const fetchAgentBalance = async (page: number, limit: number, sorting: any, filter: any) => {
|
||||
@ -63,7 +63,7 @@ const AgentBalance = () => {
|
||||
let balances = await axios.get(`${BASE_URL}/customer/agent-balance`, {
|
||||
params: {
|
||||
limit: limit,
|
||||
page: page + 1, // +1 because your API uses 1-based pages
|
||||
page: page + 1,
|
||||
with_deleted: false,
|
||||
order_field: 'created_at',
|
||||
order_direction: 'DESC',
|
||||
@ -80,6 +80,41 @@ const AgentBalance = () => {
|
||||
return { data: resBalance, totalCount: balances?.data.data.total_count };
|
||||
};
|
||||
|
||||
// ─── Export ───────────────────────────────────────────────────────────────────────────────
|
||||
const fetchExport = async (filter: any) => {
|
||||
try {
|
||||
toast.info('Preparing export…');
|
||||
|
||||
// API expects filter as array of { id, value } — same shape as columnFilters
|
||||
const filterArray = Object.entries(filter)
|
||||
.filter(([, value]) => value !== '' && value != null)
|
||||
.map(([id, value]) => ({ id, value }));
|
||||
|
||||
const res = await axios.get(`${BASE_URL}/customer/agent-balance/export-excel`, {
|
||||
params: {
|
||||
with_deleted: false,
|
||||
order_field: 'created_at',
|
||||
order_direction: 'DESC',
|
||||
filter: JSON.stringify(filterArray),
|
||||
specialFilter: true
|
||||
},
|
||||
responseType: 'blob'
|
||||
});
|
||||
|
||||
const url = URL.createObjectURL(new Blob([res.data]));
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `agent_balance_export_${new Date().toISOString().slice(0, 10)}.xlsx`;
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
toast.success('Export complete');
|
||||
} catch (error: any) {
|
||||
const msg = error?.response?.data?.error || error.message;
|
||||
toast.error(`Export failed: ${msg}`);
|
||||
}
|
||||
};
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
const openDialog = () => setIsDialogOpen(true);
|
||||
const closeDialog = () => {
|
||||
setIsDialogOpen(false);
|
||||
@ -108,7 +143,6 @@ const AgentBalance = () => {
|
||||
await fetchAgentBalance(currentPage, pageSize, '', '');
|
||||
closeDialog();
|
||||
setIsReloading(false);
|
||||
// fetchAgentBalance();
|
||||
};
|
||||
|
||||
function formatDate(value: string) {
|
||||
@ -139,24 +173,14 @@ const AgentBalance = () => {
|
||||
<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>
|
||||
@ -166,7 +190,6 @@ const AgentBalance = () => {
|
||||
{isReloading && <DataGridLoader />}
|
||||
{!isReloading && (
|
||||
<DataGridProvider
|
||||
// data={dataBalance}
|
||||
pagination={{ size: pageSize }}
|
||||
columns={getColumns(handleUpdate)}
|
||||
layout={{ card: true }}
|
||||
@ -179,6 +202,7 @@ const AgentBalance = () => {
|
||||
createGroup={createGroup}
|
||||
onReload={handleReload}
|
||||
isReloading={isReloading}
|
||||
onExport={fetchExport}
|
||||
/>
|
||||
}
|
||||
onRowSelectionChange={(selected, table: any) => {
|
||||
@ -209,7 +233,7 @@ const AgentBalance = () => {
|
||||
);
|
||||
};
|
||||
|
||||
// ====================
|
||||
// ====================
|
||||
const PaymentCard = ({
|
||||
barcode,
|
||||
agent_name,
|
||||
@ -335,4 +359,4 @@ const PaymentCard = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default AgentBalance;
|
||||
export default AgentBalance;
|
||||
@ -6,14 +6,13 @@ interface ListToolBarProps {
|
||||
createGroup: () => void;
|
||||
onReload: () => void;
|
||||
isReloading: boolean;
|
||||
onExport: (filter: Record<string, string>) => void;
|
||||
}
|
||||
|
||||
const ListToolBar = ({ createGroup, onReload, isReloading }: ListToolBarProps) => {
|
||||
const ListToolBar = ({ createGroup, onReload, isReloading, onExport }: ListToolBarProps) => {
|
||||
const { table, reload } = useDataGrid();
|
||||
const [usernameFilter, setUsernameFilter] = useState('');
|
||||
const [msisdn, setMsisdn] = useState('');
|
||||
const [typeSearchValue, setSearchTypeValue] = useState<string>();
|
||||
const [searchValue, setSearchValue] = useState<string>();
|
||||
const [fullname, setFullname] = useState('');
|
||||
const [merchantName, setMerchantName] = useState('');
|
||||
|
||||
@ -33,14 +32,27 @@ const ListToolBar = ({ createGroup, onReload, isReloading }: ListToolBarProps) =
|
||||
setMerchantName(e.target.value);
|
||||
};
|
||||
|
||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
e.preventDefault()
|
||||
table.resetPageIndex()
|
||||
/** Build the same filter object the table uses for server-side fetching */
|
||||
const buildFilter = (): Record<string, string> => {
|
||||
const filter: Record<string, string> = {};
|
||||
if (msisdn) filter['msisdn'] = msisdn;
|
||||
if (fullname) filter['fullname'] = fullname;
|
||||
if (usernameFilter) filter['username'] = usernameFilter;
|
||||
if (merchantName) filter['agent_name'] = merchantName;
|
||||
return filter;
|
||||
};
|
||||
|
||||
const handleSearch = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
table.resetPageIndex();
|
||||
table.getColumn('username')?.setFilterValue(usernameFilter);
|
||||
table.getColumn('msisdn')?.setFilterValue(msisdn);
|
||||
table.getColumn('fullname')?.setFilterValue(fullname);
|
||||
table.getColumn('agent_name')?.setFilterValue(merchantName);
|
||||
// if (!merchantName && !fullname && !msisdn && !usernameFilter) reload()
|
||||
};
|
||||
|
||||
const handleExport = () => {
|
||||
onExport(buildFilter());
|
||||
};
|
||||
|
||||
return (
|
||||
@ -48,25 +60,30 @@ const ListToolBar = ({ createGroup, onReload, isReloading }: ListToolBarProps) =
|
||||
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
|
||||
<div className="flex justify-between w-full items-center">
|
||||
|
||||
<form onSubmit={(e:any) => handleSearch(e)}>
|
||||
<form onSubmit={handleSearch}>
|
||||
<div className="flex gap-3 items-center">
|
||||
<label className="input input-sm w-1/3">
|
||||
<input type="text" placeholder="Search phone" value={msisdn} onChange={filtermsisdn}/>
|
||||
</label>
|
||||
<label className="input input-sm w-1/3">
|
||||
<input type="text" placeholder="Search fullname" value={fullname} onChange={filterfullname}/>
|
||||
</label>
|
||||
<label className="input input-sm w-1/3">
|
||||
<input type="text" placeholder="Search username" value={usernameFilter} onChange={handleUsernameChange}/>
|
||||
</label>
|
||||
<label className="input input-sm w-1/3">
|
||||
<input type="text" placeholder="Search merchant name" value={merchantName} onChange={filtermerchantname}/>
|
||||
</label>
|
||||
<Button variant="outline" className="h-7.5"><KeenIcon icon="magnifier" /></Button>
|
||||
</div>
|
||||
<label className="input input-sm w-1/3">
|
||||
<input type="text" placeholder="Search phone" value={msisdn} onChange={filtermsisdn} />
|
||||
</label>
|
||||
<label className="input input-sm w-1/3">
|
||||
<input type="text" placeholder="Search fullname" value={fullname} onChange={filterfullname} />
|
||||
</label>
|
||||
<label className="input input-sm w-1/3">
|
||||
<input type="text" placeholder="Search username" value={usernameFilter} onChange={handleUsernameChange} />
|
||||
</label>
|
||||
<label className="input input-sm w-1/3">
|
||||
<input type="text" placeholder="Search merchant name" value={merchantName} onChange={filtermerchantname} />
|
||||
</label>
|
||||
<Button variant="outline" className="h-7.5" type="submit">
|
||||
<KeenIcon icon="magnifier" />
|
||||
</Button>
|
||||
<Button className="h-7.5" type="button" onClick={handleExport}>
|
||||
Export
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div className="flex gap-3 items-center">
|
||||
<div className="flex gap-2 items-center">
|
||||
<DefaultTooltip title={isReloading ? 'Refreshing...' : 'Refresh'} placement={'top'}>
|
||||
<Button variant="outline" className="h-7.5" onClick={onReload} disabled={isReloading}>
|
||||
{isReloading ? (
|
||||
@ -79,10 +96,11 @@ const ListToolBar = ({ createGroup, onReload, isReloading }: ListToolBarProps) =
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ListToolBar };
|
||||
export { ListToolBar };
|
||||
Reference in New Issue
Block a user