member agent
This commit is contained in:
@ -52,6 +52,7 @@
|
||||
"date-fns": "^3.0.0",
|
||||
"formik": "^2.4.6",
|
||||
"helmet": "^8.1.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"https": "^1.0.0",
|
||||
"leaflet": "^1.9.4",
|
||||
"lucide-react": "^0.456.0",
|
||||
|
||||
BIN
public/media/images/card.png
Normal file
BIN
public/media/images/card.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 399 KiB |
@ -2,6 +2,7 @@ import { DataTable } from '@/components/ui/DataTable';
|
||||
import { getColumns } from './Column';
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
import html2canvas from 'html2canvas';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import {
|
||||
DialogContent,
|
||||
@ -18,7 +19,7 @@ import {
|
||||
Breadcrumbs,
|
||||
Link
|
||||
} from '@mui/material';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import ConfirmDialog from '@/components/confirm';
|
||||
@ -27,6 +28,7 @@ import { Container, DataGridLoader, DataGridProvider, LoaderTransparant } from '
|
||||
import { ListToolBar } from './ListToolbar';
|
||||
import { RefreshCw } from 'lucide-react';
|
||||
import { setgroups } from 'process';
|
||||
import { toAbsoluteUrl } from '@/utils';
|
||||
const BASE_URL = apiConfig.service_customer;
|
||||
|
||||
let initBalance = {
|
||||
@ -51,6 +53,8 @@ const AgentBalance = () => {
|
||||
const [dialogType, setDialogType] = useState('');
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [isReloading, setIsReloading] = useState(false);
|
||||
const [barcode, setBarcode] = useState('');
|
||||
// const [selectedMember, setSelectedMember] = useState(initBalance);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAgentBalance();
|
||||
@ -94,7 +98,9 @@ const AgentBalance = () => {
|
||||
openDialog();
|
||||
}
|
||||
|
||||
const handleUpdate = (group: any) => {
|
||||
const handleUpdate = async (data: any) => {
|
||||
await getQrCode(data.id);
|
||||
setFormData(data)
|
||||
setDialogType('update');
|
||||
setIsDialogOpen(true);
|
||||
};
|
||||
@ -104,6 +110,18 @@ const AgentBalance = () => {
|
||||
fetchAgentBalance();
|
||||
};
|
||||
|
||||
async function getQrCode(customerId:any): Promise<void> {
|
||||
try {
|
||||
setBarcode(`https://img.freepik.com/free-vector/scan-me-qr-code_78370-2915.jpg?semt=ais_hybrid&w=740`)
|
||||
let getToken = await axios.get(`${BASE_URL}/token/get`, { params: { id: customerId }});
|
||||
console.log(getToken.data.data);
|
||||
// setBarcode(getToken.data.data.qrCode);
|
||||
} catch (error: any) {
|
||||
toast.warning(error.message)
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) return <LoaderTransparant />;
|
||||
|
||||
return (
|
||||
@ -160,78 +178,134 @@ const AgentBalance = () => {
|
||||
</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>
|
||||
<Dialog open={isDialogOpen} onClose={closeDialog}>
|
||||
<DialogContent className="w-full flex justify-center items-center">
|
||||
<div className="relative">
|
||||
<PaymentCard barcode={barcode} customerId={formData.id} agent_name={formData.agent_name}/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog> */}
|
||||
</Dialog>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const PaymentCard = ({ barcode, agent_name, customerId }:any) => {
|
||||
const cardRef:any = useRef(null);
|
||||
// useEffect(() => {
|
||||
// fetchToken();
|
||||
// }, []);
|
||||
|
||||
// async function fetchToken(): Promise<void> {
|
||||
// try {
|
||||
// let getToken = await axios.get(`${BASE_URL}/token/get`, { params: { id: customerId }});
|
||||
// console.log(getToken.data.data);
|
||||
// // setBarcode(getToken.data.data.qrCode);
|
||||
// setBarcode(`https://img.freepik.com/free-vector/scan-me-qr-code_78370-2915.jpg?semt=ais_hybrid&w=740`)
|
||||
// } catch (error: any) {
|
||||
// toast.warning(error.message)
|
||||
// console.log(error);
|
||||
// }
|
||||
// }
|
||||
|
||||
const handleDownload = async () => {
|
||||
if (!cardRef.current) return;
|
||||
|
||||
const img = cardRef.current.querySelector('img');
|
||||
if (!img || !img.complete) {
|
||||
// Wait until the barcode image has fully loaded
|
||||
await new Promise((resolve) => {
|
||||
img.onload = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
const canvas = await html2canvas(cardRef.current, {
|
||||
useCORS: true, // Important for external images
|
||||
});
|
||||
|
||||
const dataUrl = canvas.toDataURL('image/png');
|
||||
const link = document.createElement('a');
|
||||
link.href = dataUrl;
|
||||
link.download = `${agent_name}_barcode_card.png`;
|
||||
link.click();
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center">
|
||||
{/* Card content to render */}
|
||||
<div ref={cardRef}
|
||||
className="relative w-[320px] h-[460px] rounded-xl overflow-hidden shadow-xl bg-white"
|
||||
>
|
||||
<div className="absolute inset-0 z-10 flex flex-col items-center justify-start pt-[50px] px-4 text-center">
|
||||
<p className="text-[24px] font-semibold text-[#5b0066] mb-5">
|
||||
{agent_name}
|
||||
</p>
|
||||
|
||||
{barcode && (
|
||||
<img
|
||||
src={barcode}
|
||||
alt="Barcode"
|
||||
className="w-56 h-56 object-contain mb-2"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Download Button */}
|
||||
<button
|
||||
onClick={handleDownload}
|
||||
className="mt-4 border border-red-400 text-red-500 px-4 py-1 rounded hover:bg-red-50 text-sm"
|
||||
>
|
||||
Print QR
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="relative w-[320px] h-[460px] rounded-xl overflow-hidden shadow-xl">
|
||||
<div className="absolute inset-0 z-10 flex flex-col items-center justify-start pt-[50px] px-4 text-center">
|
||||
<p className="text-[24px] font-semibold text-[#5b0066] mb-5">
|
||||
{agent_name}
|
||||
</p>
|
||||
{barcode && (
|
||||
<img
|
||||
src={barcode}
|
||||
alt="Barcode"
|
||||
className="w-70 h-70 object-contain mb-2"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// return (
|
||||
// <div className="relative w-[320px] h-[460px] rounded-xl overflow-hidden shadow-lg">
|
||||
// {/* Background Card Image */}
|
||||
// <img
|
||||
// src={toAbsoluteUrl("/media/images/card.png")}
|
||||
// alt="Card Template"
|
||||
// className="absolute inset-0 w-full h-full object-cover"
|
||||
// />
|
||||
|
||||
// {/* Content Overlay */}
|
||||
// <div className="absolute inset-0 z-10 flex flex-col items-center justify-start pt-32 px-4 text-center">
|
||||
// {/* Agent Name at Top */}
|
||||
// <p className="text-sm font-semibold text-[#5b0066] mb-4">
|
||||
// {agent_name}
|
||||
// </p>
|
||||
|
||||
// {/* Barcode */}
|
||||
// {barcode && (
|
||||
// <img
|
||||
// src={barcode}
|
||||
// alt="Barcode"
|
||||
// className="w-40 h-40 object-contain mb-4"
|
||||
// />
|
||||
// )}
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
};
|
||||
|
||||
export default AgentBalance;
|
||||
|
||||
@ -143,4 +143,15 @@ export const getColumns = (handleUpdate: (data: any) => void): ColumnDef<AgentBa
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
cell: ({ row }) => {
|
||||
const dataMembers = row.original;
|
||||
return (
|
||||
<button className="btn btn-sm btn-icon btn-clear btn-light" onClick={() => handleUpdate(dataMembers)}>
|
||||
<KeenIcon icon="notepad-edit" />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@ -121,22 +121,7 @@ const ManageMembers = () => {
|
||||
sorting: any,
|
||||
filter: any
|
||||
) => {
|
||||
// [ SAMPLE
|
||||
// {
|
||||
// "id": "username",
|
||||
// "value": "d"
|
||||
// },
|
||||
// {
|
||||
// "id": "group_name",
|
||||
// "value": "Bank"
|
||||
// },
|
||||
// {
|
||||
// "id": "msisdn",
|
||||
// "value": "88"
|
||||
// }
|
||||
// ]
|
||||
sorting = sorting.length == 0 ? [{ id: 'name', desc: true }] : sorting;
|
||||
// filter = filter.length ? { any: filter[0].value?.toLowerCase() } : {};
|
||||
filter = filter.length ? filter : {};
|
||||
|
||||
const res = await axios.get(`${BASE_URL}/customer/list`, {
|
||||
|
||||
@ -49,7 +49,7 @@ const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialDa
|
||||
img: ''
|
||||
});
|
||||
const [banks] = useState([
|
||||
{ name: 'BNCTL',id: 'BNCTL' }, { name: 'BRI',id: 'BRI' }, { name: 'BNU',id: 'BNU' }, { name: 'Mandiri',id: 'Mandiri' }
|
||||
{ name: 'BNCTL',id: 'BNCTL' }, { name: 'BRI',id: 'BRI' }, { name: 'BNU',id: 'BNU' }, { name: 'MANDIRI',id: 'MANDIRI' }
|
||||
])
|
||||
const parentRef = useRef<any | null>(null);
|
||||
const [alert, setAlert] = useState({
|
||||
@ -214,7 +214,7 @@ const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialDa
|
||||
{(formData.id || dialogType === "create") ? generateImage(formData, handleChange, 'Photo', 'photouser', previewImg, setPreviewImg): ''}
|
||||
{(formData.id || dialogType === "create") ? generateList(formData, handleChange, genders, 'gender', 'Gender', true, false): ''}
|
||||
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Date of Birth', 'date_birth', 'date', true, viewOnly): ''}
|
||||
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Phone Number', 'msisdn', 'text', true, dialogType === "create"||false): ''}
|
||||
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Phone Number', 'msisdn', 'text', true, dialogType === "create"?false:true): ''}
|
||||
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Email', 'email', 'text', true, viewOnly): ''}
|
||||
{/* {generateList(formData, handleChange, status, 'status', 'Status', null, true)} */}
|
||||
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Mother Fullname', 'mother_fullname', 'text', true, viewOnly): ''}
|
||||
|
||||
Reference in New Issue
Block a user