update
This commit is contained in:
@ -15,7 +15,7 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
Typography,
|
Typography,
|
||||||
Button,
|
Button,
|
||||||
Box,
|
Box,
|
||||||
Breadcrumbs,
|
Breadcrumbs,
|
||||||
Link
|
Link
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
@ -24,6 +24,7 @@ import { toast } from 'sonner';
|
|||||||
import { Container, DataGridLoader, DataGridProvider, LoaderTransparant } from '@/components';
|
import { Container, DataGridLoader, DataGridProvider, LoaderTransparant } from '@/components';
|
||||||
import { ListToolBar } from './ListToolbar';
|
import { ListToolBar } from './ListToolbar';
|
||||||
import { toAbsoluteUrl } from '@/utils';
|
import { toAbsoluteUrl } from '@/utils';
|
||||||
|
|
||||||
const BASE_URL = apiConfig.service_customer;
|
const BASE_URL = apiConfig.service_customer;
|
||||||
|
|
||||||
let initBalance = {
|
let initBalance = {
|
||||||
@ -52,10 +53,9 @@ const AgentBalance = () => {
|
|||||||
const [barcode, setBarcode] = useState('');
|
const [barcode, setBarcode] = useState('');
|
||||||
const [requested_date, setRequested_date] = useState('');
|
const [requested_date, setRequested_date] = useState('');
|
||||||
const [token_legacy, setToken_legacy] = useState('');
|
const [token_legacy, setToken_legacy] = useState('');
|
||||||
// const [selectedMember, setSelectedMember] = useState(initBalance);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// fetchAgentBalance();
|
// fetchAgentBalance();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const fetchAgentBalance = async (page: number, limit: number, sorting: any, filter: any) => {
|
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`, {
|
let balances = await axios.get(`${BASE_URL}/customer/agent-balance`, {
|
||||||
params: {
|
params: {
|
||||||
limit: limit,
|
limit: limit,
|
||||||
page: page + 1, // +1 because your API uses 1-based pages
|
page: page + 1,
|
||||||
with_deleted: false,
|
with_deleted: false,
|
||||||
order_field: 'created_at',
|
order_field: 'created_at',
|
||||||
order_direction: 'DESC',
|
order_direction: 'DESC',
|
||||||
@ -80,6 +80,41 @@ const AgentBalance = () => {
|
|||||||
return { data: resBalance, totalCount: balances?.data.data.total_count };
|
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 openDialog = () => setIsDialogOpen(true);
|
||||||
const closeDialog = () => {
|
const closeDialog = () => {
|
||||||
setIsDialogOpen(false);
|
setIsDialogOpen(false);
|
||||||
@ -108,7 +143,6 @@ const AgentBalance = () => {
|
|||||||
await fetchAgentBalance(currentPage, pageSize, '', '');
|
await fetchAgentBalance(currentPage, pageSize, '', '');
|
||||||
closeDialog();
|
closeDialog();
|
||||||
setIsReloading(false);
|
setIsReloading(false);
|
||||||
// fetchAgentBalance();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function formatDate(value: string) {
|
function formatDate(value: string) {
|
||||||
@ -139,24 +173,14 @@ const AgentBalance = () => {
|
|||||||
<title>TPAY | Agent Balance</title>
|
<title>TPAY | Agent Balance</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<Container>
|
<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>
|
<h1 className="text-xl font-medium leading-none text-gray-900 mb-5">Agent Balances</h1>
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Link underline="none" color="inherit" href="/">
|
<Link underline="none" color="inherit" href="/">
|
||||||
<span className="text-sm hover:underline">Dashboard</span>
|
<span className="text-sm hover:underline">Dashboard</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link underline="none" color="inherit">
|
<Link underline="none" color="inherit">
|
||||||
<span className="text-sm">Members</span>
|
<span className="text-sm">Members</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link underline="none" color="inherit">
|
<Link underline="none" color="inherit">
|
||||||
<span className="text-sm">Agent Balances</span>
|
<span className="text-sm">Agent Balances</span>
|
||||||
</Link>
|
</Link>
|
||||||
@ -166,7 +190,6 @@ const AgentBalance = () => {
|
|||||||
{isReloading && <DataGridLoader />}
|
{isReloading && <DataGridLoader />}
|
||||||
{!isReloading && (
|
{!isReloading && (
|
||||||
<DataGridProvider
|
<DataGridProvider
|
||||||
// data={dataBalance}
|
|
||||||
pagination={{ size: pageSize }}
|
pagination={{ size: pageSize }}
|
||||||
columns={getColumns(handleUpdate)}
|
columns={getColumns(handleUpdate)}
|
||||||
layout={{ card: true }}
|
layout={{ card: true }}
|
||||||
@ -179,6 +202,7 @@ const AgentBalance = () => {
|
|||||||
createGroup={createGroup}
|
createGroup={createGroup}
|
||||||
onReload={handleReload}
|
onReload={handleReload}
|
||||||
isReloading={isReloading}
|
isReloading={isReloading}
|
||||||
|
onExport={fetchExport}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onRowSelectionChange={(selected, table: any) => {
|
onRowSelectionChange={(selected, table: any) => {
|
||||||
@ -209,7 +233,7 @@ const AgentBalance = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ====================
|
// ====================
|
||||||
const PaymentCard = ({
|
const PaymentCard = ({
|
||||||
barcode,
|
barcode,
|
||||||
agent_name,
|
agent_name,
|
||||||
@ -335,4 +359,4 @@ const PaymentCard = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AgentBalance;
|
export default AgentBalance;
|
||||||
@ -6,14 +6,13 @@ interface ListToolBarProps {
|
|||||||
createGroup: () => void;
|
createGroup: () => void;
|
||||||
onReload: () => void;
|
onReload: () => void;
|
||||||
isReloading: boolean;
|
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 { table, reload } = useDataGrid();
|
||||||
const [usernameFilter, setUsernameFilter] = useState('');
|
const [usernameFilter, setUsernameFilter] = useState('');
|
||||||
const [msisdn, setMsisdn] = useState('');
|
const [msisdn, setMsisdn] = useState('');
|
||||||
const [typeSearchValue, setSearchTypeValue] = useState<string>();
|
|
||||||
const [searchValue, setSearchValue] = useState<string>();
|
|
||||||
const [fullname, setFullname] = useState('');
|
const [fullname, setFullname] = useState('');
|
||||||
const [merchantName, setMerchantName] = useState('');
|
const [merchantName, setMerchantName] = useState('');
|
||||||
|
|
||||||
@ -33,14 +32,27 @@ const ListToolBar = ({ createGroup, onReload, isReloading }: ListToolBarProps) =
|
|||||||
setMerchantName(e.target.value);
|
setMerchantName(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
/** Build the same filter object the table uses for server-side fetching */
|
||||||
e.preventDefault()
|
const buildFilter = (): Record<string, string> => {
|
||||||
table.resetPageIndex()
|
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('username')?.setFilterValue(usernameFilter);
|
||||||
table.getColumn('msisdn')?.setFilterValue(msisdn);
|
table.getColumn('msisdn')?.setFilterValue(msisdn);
|
||||||
table.getColumn('fullname')?.setFilterValue(fullname);
|
table.getColumn('fullname')?.setFilterValue(fullname);
|
||||||
table.getColumn('agent_name')?.setFilterValue(merchantName);
|
table.getColumn('agent_name')?.setFilterValue(merchantName);
|
||||||
// if (!merchantName && !fullname && !msisdn && !usernameFilter) reload()
|
};
|
||||||
|
|
||||||
|
const handleExport = () => {
|
||||||
|
onExport(buildFilter());
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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 flex-wrap gap-2 lg:gap-5 w-full">
|
||||||
<div className="flex justify-between w-full items-center">
|
<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">
|
<div className="flex gap-3 items-center">
|
||||||
<label className="input input-sm w-1/3">
|
<label className="input input-sm w-1/3">
|
||||||
<input type="text" placeholder="Search phone" value={msisdn} onChange={filtermsisdn}/>
|
<input type="text" placeholder="Search phone" value={msisdn} onChange={filtermsisdn} />
|
||||||
</label>
|
</label>
|
||||||
<label className="input input-sm w-1/3">
|
<label className="input input-sm w-1/3">
|
||||||
<input type="text" placeholder="Search fullname" value={fullname} onChange={filterfullname}/>
|
<input type="text" placeholder="Search fullname" value={fullname} onChange={filterfullname} />
|
||||||
</label>
|
</label>
|
||||||
<label className="input input-sm w-1/3">
|
<label className="input input-sm w-1/3">
|
||||||
<input type="text" placeholder="Search username" value={usernameFilter} onChange={handleUsernameChange}/>
|
<input type="text" placeholder="Search username" value={usernameFilter} onChange={handleUsernameChange} />
|
||||||
</label>
|
</label>
|
||||||
<label className="input input-sm w-1/3">
|
<label className="input input-sm w-1/3">
|
||||||
<input type="text" placeholder="Search merchant name" value={merchantName} onChange={filtermerchantname}/>
|
<input type="text" placeholder="Search merchant name" value={merchantName} onChange={filtermerchantname} />
|
||||||
</label>
|
</label>
|
||||||
<Button variant="outline" className="h-7.5"><KeenIcon icon="magnifier" /></Button>
|
<Button variant="outline" className="h-7.5" type="submit">
|
||||||
</div>
|
<KeenIcon icon="magnifier" />
|
||||||
|
</Button>
|
||||||
|
<Button className="h-7.5" type="button" onClick={handleExport}>
|
||||||
|
Export
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div className="flex gap-3 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<DefaultTooltip title={isReloading ? 'Refreshing...' : 'Refresh'} placement={'top'}>
|
<DefaultTooltip title={isReloading ? 'Refreshing...' : 'Refresh'} placement={'top'}>
|
||||||
<Button variant="outline" className="h-7.5" onClick={onReload} disabled={isReloading}>
|
<Button variant="outline" className="h-7.5" onClick={onReload} disabled={isReloading}>
|
||||||
{isReloading ? (
|
{isReloading ? (
|
||||||
@ -79,10 +96,11 @@ const ListToolBar = ({ createGroup, onReload, isReloading }: ListToolBarProps) =
|
|||||||
</Button>
|
</Button>
|
||||||
</DefaultTooltip>
|
</DefaultTooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { ListToolBar };
|
export { ListToolBar };
|
||||||
Reference in New Issue
Block a user