Merge branch 'master' into alwi

This commit is contained in:
Wikzyy
2025-03-12 10:59:04 +07:00
6 changed files with 334 additions and 93 deletions

1
.env
View File

@ -3,4 +3,5 @@ VITE_APP_VERSION=1=9.1.1
GENERATE_SOURCEMAP=false GENERATE_SOURCEMAP=false
VITE_APP_API_URL=https://tpay.shiblysolution.id/api VITE_APP_API_URL=https://tpay.shiblysolution.id/api
# VITE_APP_API_URL=http://0.0.0.0:4001/api
VITE_ENV=default VITE_ENV=default

View File

@ -1,10 +1,10 @@
import { apiConfig } from '@/config/api.config'; import { apiConfig } from '@/config/api.config';
import { Account, columns } from './Columns'; import { Account, columns } from './Columns';
import { DataTable } from '@/components/ui/DataTable';
import axios from 'axios'; import axios from 'axios';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { getData } from '@/utils'; import { getData } from '@/utils';
import { useCallApi } from '@/hooks'; import { useCallApi } from '@/hooks';
import { DataTable } from '@/components/ui/DataTable';
const API_URL = apiConfig.service_dashboard; const API_URL = apiConfig.service_dashboard;
@ -79,8 +79,8 @@ const ManageAccount = () => {
<DataTable <DataTable
columns={columns} columns={columns}
data={dataAccount} data={dataAccount}
onUpdate={() => console.log('update')} onUpdate={() => console.log('Callback update')}
onDelete={() => console.log('delete')} onDelete={() => console.log('Callback delete')}
/> />
</div> </div>
</div> </div>

View File

@ -0,0 +1,195 @@
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu';
import { ColumnDef } from '@tanstack/react-table';
import { ArrowUpDown, MoreHorizontal } from 'lucide-react';
export type Members = {
// id: number;
username: number;
fullname: string;
group: string;
email: string;
created_at: Date;
};
export const columns: ColumnDef<Members>[] = [
{
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: 'username',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Username
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
accessorKey: 'name',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Fullname
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
accessorKey: 'group_name',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Group
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
accessorKey: 'destinationGroup',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Destination Group
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
accessorKey: 'email',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Email
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
accessorKey: 'created_at',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Created Date
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }) => new Date(row.original.created_at).toLocaleDateString()
},
{
accessorKey: 'statusApproval',
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Status Approval
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
id: 'actions',
cell: ({ row }) => {
const dataMembers = row.original;
return (
<DropdownMenu>
</DropdownMenu>
);
}
}
];
export const initialMember = {
id: "",
fullname: "",
email: "",
username: "",
msisdn: "",
password: "",
pin: "",
try_pin: "",
mother_fullname: "",
agent_name: "",
bank_name: "",
bank_account: "",
ibank_number: "",
address: "",
longitude: "",
latitude: "",
nationality: "",
photouser: "",
photomerchant: "",
file_selfie: "",
file_document_id: "",
file_document_id_selfie: "",
file_commercial_license: "",
identity_type: "",
identity_number: "",
license_number: "",
date_birth: "",
gender: "",
status: "N",
isneedapproval: "",
isapproved: "",
approveddate: "",
approvedby: "",
created_by: "",
created_at: "",
updated_by: "",
updated_at: "",
deleted_by: "",
deleted_at: "",
group: "",
point_tier: "",
language: "",
municipio: "",
posto_adms: "",
suco: "",
aldeia: "",
profession: "",
}

View File

@ -3,9 +3,10 @@ import { DataTable } from '@/components/ui/DataTable';
import { Table } from '@tanstack/react-table'; import { Table } from '@tanstack/react-table';
import React, { createContext, useContext, useState, useEffect } from 'react'; import React, { createContext, useContext, useState, useEffect } from 'react';
import { ManageKycContextProvider } from './hooks'; import { ManageKycContextProvider } from './hooks';
import { columns, initialMember } from '../manage-members/Columns'; import { columns, initialMember } from './Columns';
import { useAuthContext } from '@/auth'; import { useAuthContext } from '@/auth';
import { apiConfig } from '@/config/api.config'; import { apiConfig } from '@/config/api.config';
import ConfirmDialog from '@/components/confirm';
import axios from 'axios'; import axios from 'axios';
const BASE_URL = apiConfig.service_customer; const BASE_URL = apiConfig.service_customer;
import CustomerDialog from '../manage-members/CustomerDetailModal'; import CustomerDialog from '../manage-members/CustomerDetailModal';
@ -52,7 +53,7 @@ const Kyc = () => {
with_deleted: false, with_deleted: false,
order_field: 'fullname', order_field: 'fullname',
order_direction: 'ASC', order_direction: 'ASC',
// type: "Reguler" type: "kyc"
} }
}); });
let temp = 1 let temp = 1
@ -79,10 +80,16 @@ const Kyc = () => {
setDialogOpen(true) setDialogOpen(true)
} }
function handleReject(data: any) {
setDialogType('reject')
setMember(data)
setDialogOpen(true)
}
const handleYes = async () => { const handleYes = async () => {
let userLogin: any = await getUser(); let userLogin: any = await getUser();
let updateData: any = member; let updateData: any = member;
let customerId = member.id; const customerId = member.id;
updateData.updated_by = userLogin.data ? userLogin.data.id : '' updateData.updated_by = userLogin.data ? userLogin.data.id : ''
let today = new Date(); let today = new Date();
updateData.updated_at = today.toString(); updateData.updated_at = today.toString();
@ -104,11 +111,19 @@ const Kyc = () => {
delete updateData.group_deleted_by; delete updateData.group_deleted_by;
delete updateData.group_deleted_at; delete updateData.group_deleted_at;
try { try {
if (dialogType === 'reject') {
await axios.post(`${BASE_URL}/customer/reject`, {customerid: customerId})
}
if (dialogType === 'update') {
await axios.put(`${BASE_URL}/customer/update/${customerId}`, updateData) await axios.put(`${BASE_URL}/customer/update/${customerId}`, updateData)
if (updateData.isneedapproval == 1) await axios.post(`${BASE_URL}/customer/approve`, {customerid: customerId})
}
await fetchGroups(); await fetchGroups();
setDialogOpen(false)
setIsDialogOpen(false) setIsDialogOpen(false)
} catch (error: any) { } catch (error: any) {
alert(error.message) alert(error.message)
setDialogOpen(false)
setIsDialogOpen(false) setIsDialogOpen(false)
} }
} }
@ -116,15 +131,18 @@ const Kyc = () => {
return ( return (
<div> <div>
<div className="container mx-auto py-5"> <div className="container mx-auto py-5">
<CustomerDialog open={isDialogOpen} handleClose={closeDialog} handleSubmit={handleSubmit} initialData={member} viewStats={true}/> <ConfirmDialog
open={dialogOpen}
onClose={() => setDialogOpen(false)}
title="Confirm Action"
content={`Are you sure you want to ${dialogType}?`}
onYes={handleYes}
onNo={() => setDialogOpen(false)}
/>
<CustomerDialog open={isDialogOpen} handleClose={closeDialog} handleReject={handleReject}
handleSubmit={handleSubmit} initialData={member} viewStats={true}/>
<h1 className="text-xl font-medium leading-none text-gray-900 mb-7">Manage Member KYC</h1> <h1 className="text-xl font-medium leading-none text-gray-900 mb-7">Manage Member KYC</h1>
<DataTable data={members} columns={columns} onUpdate={handleUpdate} onDelete={null}/> <DataTable data={members} columns={columns} onUpdate={handleUpdate} onDelete={null}/>
{/* <ManageKycContextProvider>
<div className="grid gap-5 lg:gap-7.5">
<DataGridInner />
</div>
</ManageKycContextProvider> */}
</div> </div>
</div> </div>
); );

View File

@ -15,7 +15,7 @@ import {
import Divider from '@mui/material/Divider'; import Divider from '@mui/material/Divider';
import { initialMember } from "./Columns"; import { initialMember } from "./Columns";
const CustomerDialog = ({ open, handleClose, handleSubmit, initialData, viewStats }: any) => { const CustomerDialog = ({ open, handleClose, handleSubmit, initialData, viewStats, handleReject }: any) => {
const [formData, setFormData] = useState(initialData || initialMember); const [formData, setFormData] = useState(initialData || initialMember);
const [viewOnly, setViewOnly] = useState(viewStats || false); const [viewOnly, setViewOnly] = useState(viewStats || false);
@ -34,6 +34,11 @@ const CustomerDialog = ({ open, handleClose, handleSubmit, initialData, viewStat
handleClose(); handleClose();
}; };
const onReject = () => {
handleReject(formData);
handleClose();
}
function generateDate(date: any, type: any) { function generateDate(date: any, type: any) {
if (!date) return '' if (!date) return ''
const today = new Date(date); const today = new Date(date);
@ -43,12 +48,20 @@ const CustomerDialog = ({ open, handleClose, handleSubmit, initialData, viewStat
return ( return (
<Dialog open={open} onClose={handleClose} fullWidth maxWidth="sm"> <Dialog open={open} onClose={handleClose} fullWidth maxWidth="sm">
<DialogTitle>Customer Form ({viewStats ? 'View' : 'Edit'})</DialogTitle> <DialogTitle>Customer Form ({viewOnly ? 'View' : 'Edit'})</DialogTitle>
<DialogContent> <DialogContent>
<div className="flex justify-between"> <div className="flex justify-between pb-5">
<Typography sx={{color:'grey'}}>Customer Data</Typography> <Typography sx={{color:'grey'}}>Customer Data</Typography>
<Typography sx={{color:'grey'}} display="flex" justifyContent="flex-end">Created: {generateDate(formData.created_at, 'datetime')}</Typography> <Typography sx={{color:'grey'}} display="flex" justifyContent="flex-end">Created: {generateDate(formData.created_at, 'datetime')}</Typography>
</div> </div>
{
formData.isneedapproval == 1 ? (
<div className="flex justify-between pb-5">
<Typography sx={{color:'orange'}}>User Request for approval </Typography>
</div>
) : ('')
}
<TextField disabled={viewOnly} fullWidth margin="dense" label="Full Name" name="fullname" value={formData.fullname} onChange={handleChange} /> <TextField disabled={viewOnly} fullWidth margin="dense" label="Full Name" name="fullname" value={formData.fullname} onChange={handleChange} />
<TextField disabled={viewOnly} fullWidth margin="dense" label="Email" name="email" value={formData.email} onChange={handleChange} /> <TextField disabled={viewOnly} fullWidth margin="dense" label="Email" name="email" value={formData.email} onChange={handleChange} />
<TextField disabled fullWidth margin="dense" label="Group" name="group" value={formData.group_name} onChange={handleChange} /> <TextField disabled fullWidth margin="dense" label="Group" name="group" value={formData.group_name} onChange={handleChange} />
@ -65,7 +78,14 @@ const CustomerDialog = ({ open, handleClose, handleSubmit, initialData, viewStat
<MenuItem value="F">Female</MenuItem> <MenuItem value="F">Female</MenuItem>
</Select> </Select>
</FormControl> </FormControl>
<TextField disabled={viewOnly} fullWidth margin="dense" label="Identity Type" name="identity_type" value={formData.identity_type} onChange={handleChange} /> <FormControl fullWidth margin="dense">
<InputLabel>Identity Type</InputLabel>
<Select disabled={viewOnly} name="identity_type" value={formData.identity_type} onChange={handleChange}>
<MenuItem value="eleitoral_id">Eleitoral ID</MenuItem>
<MenuItem value="bihete_de_identidade">Bihete de Identidade</MenuItem>
<MenuItem value="passport">Passport</MenuItem>
</Select>
</FormControl>
<TextField disabled={viewOnly} fullWidth margin="dense" label="Identity Number" name="identity_number" value={formData.identity_number} onChange={handleChange} /> <TextField disabled={viewOnly} fullWidth margin="dense" label="Identity Number" name="identity_number" value={formData.identity_number} onChange={handleChange} />
<TextField disabled={viewOnly} type="number" fullWidth margin="dense" label="License Number" name="license_number" value={formData.license_number} onChange={handleChange} /> <TextField disabled={viewOnly} type="number" fullWidth margin="dense" label="License Number" name="license_number" value={formData.license_number} onChange={handleChange} />
<TextField disabled={viewOnly} fullWidth margin="dense" label="Profession" name="profession" value={formData.profession} onChange={handleChange} /> <TextField disabled={viewOnly} fullWidth margin="dense" label="Profession" name="profession" value={formData.profession} onChange={handleChange} />
@ -118,8 +138,16 @@ const CustomerDialog = ({ open, handleClose, handleSubmit, initialData, viewStat
<TextField disabled={viewOnly} fullWidth margin="dense" label="iBank Number" name="ibank_number" value={formData.ibank_number} onChange={handleChange} /> <TextField disabled={viewOnly} fullWidth margin="dense" label="iBank Number" name="ibank_number" value={formData.ibank_number} onChange={handleChange} />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={() => viewOnly ? setViewOnly(false) : setViewOnly(true)} color="secondary">{ viewOnly ? (`Open Edit`) : (`Close Edit`) }</Button>
<Button onClick={handleClose} color="secondary">Cancel</Button> <Button onClick={handleClose} color="secondary">Cancel</Button>
<Button onClick={onSubmit} color="primary" variant="contained">Submit</Button> {
formData.isneedapproval == 1 ? (
<Button onClick={onReject} color="warning" variant="contained">Reject</Button>
) : ('')
}
<Button onClick={onSubmit} color="primary" variant="contained">
{ formData.isneedapproval == 1 ? (`Edit & Approve`) : (`Edit`) }
</Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );

View File

@ -30,7 +30,6 @@ const ManageMembers = () => {
with_deleted: false, with_deleted: false,
order_field: 'fullname', order_field: 'fullname',
order_direction: 'ASC', order_direction: 'ASC',
type: "Reguler"
} }
}); });
let temp = 1 let temp = 1