diff --git a/src/pages/members/kyc-delete-member/blocks/DetailDialog.tsx b/src/pages/members/kyc-delete-member/blocks/DetailDialog.tsx index 016674c..8b1ca90 100644 --- a/src/pages/members/kyc-delete-member/blocks/DetailDialog.tsx +++ b/src/pages/members/kyc-delete-member/blocks/DetailDialog.tsx @@ -9,11 +9,12 @@ import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { useManageKycDeletionContext } from '../hooks'; import { apiConfig } from '@/config/api.config'; -import axios from 'axios'; +import { useDataGrid } from '@/components'; const API_URL = apiConfig.service_customer; const DetailDialog = () => { const { showDetailDialog, setShowDetailDialog, detailKyc, handleApproveReject } = useManageKycDeletionContext(); + const { reload } = useDataGrid(); return ( @@ -38,8 +39,14 @@ const DetailDialog = () => {
- - + +
) : (
)} diff --git a/src/pages/members/kyc-delete-member/hooks/ManageKycDeletionContext.tsx b/src/pages/members/kyc-delete-member/hooks/ManageKycDeletionContext.tsx index c0742fd..48e4608 100644 --- a/src/pages/members/kyc-delete-member/hooks/ManageKycDeletionContext.tsx +++ b/src/pages/members/kyc-delete-member/hooks/ManageKycDeletionContext.tsx @@ -37,7 +37,7 @@ interface ContextProps { selectedIdCustomer: string | null; detailKyc: any | null; setDetailKyc: React.Dispatch>; - handleApproveReject: (customerDeletionId: string, status_approve: string) => {}; + handleApproveReject: (customerDeletionId: string, status_approve: string) => Promise; } const initialProps: ContextProps = { @@ -50,7 +50,7 @@ const initialProps: ContextProps = { setShowDetailDialog: () => { }, detailKyc: async () => {}, setDetailKyc: () => { }, - handleApproveReject: () => ({customerDeletionId: '0', status_approve: 'Y'}), + handleApproveReject: async () => ({customerDeletionId: '0', status_approve: 'Y'}), }; const ManageKycDeletionContext = createContext(initialProps); @@ -271,7 +271,6 @@ const ManageKycDeletionContextProvider = ({ children }: { children: React.ReactN }} > - {children} + ); diff --git a/src/pages/transaction/disbursement-saldo/TransactionDisbursement.tsx b/src/pages/transaction/disbursement-saldo/TransactionDisbursement.tsx new file mode 100644 index 0000000..4086d51 --- /dev/null +++ b/src/pages/transaction/disbursement-saldo/TransactionDisbursement.tsx @@ -0,0 +1,105 @@ +import { Container, DataGridInner } from '@/components'; +import { TransactionDisbursementProvider } from './hooks/TransactionDisbursementContext'; +import { Breadcrumbs, Link } from '@mui/material'; +import { Helmet } from 'react-helmet'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { useState } from 'react'; +import { useCallApi } from '@/hooks'; +import { apiConfig } from '@/config/api.config'; +import { toast } from 'sonner'; + +const TransactionDisbursement = () => { + const [form, setForm] = useState({ + customerId: '', + amount: '', + pin: '' + }); + const { GetData, PostData } = useCallApi(); + const API_URL = apiConfig.transaction; + + const handleSubmit = async (e: any) => { + e.preventDefault(); + console.log('Submitted Data:', form); + try { + // let requestTopup = await PostData(`${API_URL}/transaction/request-topup`, { + // amount: form.topupAmount, + // pin: form.pin + // }) + // console.log(requestTopup) + // if (requestTopup?.status == true) { + // toast.success('Success Request Topup') + // } else { + // toast.warning(`${requestTopup?.message}`) + // } + toast.warning('Failed') + } catch (error) { + toast.warning('Failed') + } + // TODO: Kirim ke backend atau proses lainnya + }; + + return ( + <> + + TPAY | Transaction Disbursement Saldo + + + +

MANAGE TRANSACTION DISBURSEMENT SALDO

+ + + Dashboard + + + Transaction + + + Disbursement Saldo + + + +
+
+ {/* form */} +
+
+ + setForm({ ...form, customerId: e.target.value })} + /> +
+
+ + setForm({ ...form, amount: e.target.value })} + /> +
+
+ + setForm({ ...form, pin: e.target.value })} + /> +
+
+ +
+
+
+
+
+
+
+ + ); +}; + +export default TransactionDisbursement; diff --git a/src/pages/transaction/disbursement-saldo/blocks/DetailDialog.tsx b/src/pages/transaction/disbursement-saldo/blocks/DetailDialog.tsx new file mode 100644 index 0000000..8b1ca90 --- /dev/null +++ b/src/pages/transaction/disbursement-saldo/blocks/DetailDialog.tsx @@ -0,0 +1,105 @@ +import { + Dialog, + DialogBody, + DialogContent, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { useManageKycDeletionContext } from '../hooks'; +import { apiConfig } from '@/config/api.config'; +import { useDataGrid } from '@/components'; + +const API_URL = apiConfig.service_customer; +const DetailDialog = () => { + const { showDetailDialog, setShowDetailDialog, detailKyc, handleApproveReject } = useManageKycDeletionContext(); + const { reload } = useDataGrid(); + + return ( + + + + Customer Deletion Details + + + {/* Tab Content */} + {detailKyc && detailKyc != null ? ( +
+ {generateInput(detailKyc, null, 'Group', 'group_name', 'text', false, true)} + {generateInput(detailKyc, null, 'Username', 'username', 'text', false, true)} + {generateInput(detailKyc, null, 'Full Name', 'fullname', 'text', false, true)} + {generateInput(detailKyc, null, 'Gender', 'customers_gender', 'text', false, true)} + {generateInput(detailKyc, null, 'Date of Birth', 'birthdate', 'date', false, true)} + {generateInput(detailKyc, null, 'Mother Name', 'registered_mother_fullname', 'text', false, true)} + {generateInput(detailKyc, null, 'MSISDN', 'registered_msisdn', 'text', false, true)} + {generateInput(detailKyc, null, 'Reason Deletion', 'reason_deletion', 'text', false, true)} + {generateInput(detailKyc, null, 'Reason Note', 'reason_note', 'text', false, true)} + {generateInput(detailKyc, null, 'Status Approval', 'status_approve', 'text', false, true)} + +
+ + + +
+
+ ) : (
)} +
+
+
+ ); +}; + +export default DetailDialog; + +function generateInput(formData: any, handleChange: any, label: string, name: string, type: string, required: boolean, disabled: boolean) { + function generateDate(isoString: string) { + return isoString.slice(0, 10); // "2000-01-18" + } + + type Code = 'W' | 'Y' | 'N' | 'T' | 'P' | 'D' | 'L'; + interface Reason { + label: string; + } + + const statusMap: Record = { + W: { label: 'Waiting Approval' }, + Y: { label: 'Approve' }, + N: { label: 'Reject' }, + T: { label: 'Tidak lagi menggunakan layanan' }, + P: { label: 'Privasi dan keamanan' }, + D: { label: 'Akun ganda' }, + L: { label: 'Lainnya' } + }; + if (name == 'reason_deletion' || name == 'status_approve') { + const status = formData[name] as Code + const fixStatus = statusMap[status] ?? { label: formData[name] } + formData[name] = fixStatus.label + } + return ( + <> +
+
+ + +
+
+ + ) +} \ No newline at end of file diff --git a/src/pages/transaction/disbursement-saldo/blocks/ListToolbar.tsx b/src/pages/transaction/disbursement-saldo/blocks/ListToolbar.tsx new file mode 100644 index 0000000..013a15c --- /dev/null +++ b/src/pages/transaction/disbursement-saldo/blocks/ListToolbar.tsx @@ -0,0 +1,61 @@ +import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components'; +import { Button } from '@/components/ui/button'; +import { useCallback, useState, useEffect } from 'react'; +import { toast } from 'sonner'; + +const ListToolbar = () => { + const { table, reload } = useDataGrid(); + + // Set the initial state for trxDate + const [trxDate, settrxDate] = useState({ from: '', to: '' }); + + // Function to format date to YYYY-MM-DD + const formatDate = (date: Date): string => { + return date.toISOString().split('T')[0]; + }; + + // useEffect to set the default date values + useEffect(() => { + const today = new Date(); + const nextWeek = new Date(today); + nextWeek.setDate(today.getDate() + 7); + + settrxDate({ + from: formatDate(today), // Set 'from' to today + to: formatDate(nextWeek), // Set 'to' to 7 days later + }); + }, []); + + const handleFilterData = useCallback(() => { + try { + table.getColumn('transaction_date')?.setFilterValue(trxDate); + } catch (error) { + toast.error('Error applying filter'); + console.error('Error applying filter:', error); + } + }, [trxDate, table]); + + useEffect(() => { + if (trxDate.from && trxDate.to) { + handleFilterData(); + } + }, [trxDate]); + + return ( +
+
+
+
+ + + +
+
+
+
+ ); +}; + +export default ListToolbar; diff --git a/src/pages/transaction/disbursement-saldo/hooks/TransactionDisbursementContext.tsx b/src/pages/transaction/disbursement-saldo/hooks/TransactionDisbursementContext.tsx new file mode 100644 index 0000000..a3366be --- /dev/null +++ b/src/pages/transaction/disbursement-saldo/hooks/TransactionDisbursementContext.tsx @@ -0,0 +1,81 @@ +import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components'; +import { Toaster } from '@/components/ui/sonner'; +import { toast } from 'sonner'; +import { apiConfig } from '@/config/api.config'; +import { ColumnDef } from '@tanstack/react-table'; +import { createContext, useCallback, useMemo, useState } from 'react'; +import ListToolbar from '../blocks/ListToolbar'; +import { useCallApi } from '@/hooks'; +import moment from 'moment'; +import DetailDialog from '../blocks/DetailDialog'; + +interface TransactionDisbursementProps { + id: string; + customers_id: string; + group_id: string; + username: string; + fullname: string; + email: string; + status: string; + created_at: Date; +} + +interface ContextProps { + +} + +const initialProps: ContextProps = { + +}; + +const TransactionDisbursementContext = createContext(initialProps); +const API_URL = apiConfig.service_customer; + +type StatusCode = 'W' | 'Y' | 'N' | 'T'; + +interface StatusInfo { + label: string; + bg: string; + text: string; +} + +const statusMap: Record = { + W: { label: 'Waiting Approval', bg: 'bg-yellow-100', text: 'text-yellow-600' }, + T: { label: 'No Need', bg: 'bg-blue-100', text: 'text-blue-600' }, + N: { label: 'Reject', bg: 'bg-red-100', text: 'text-red-600' }, + Y: { label: 'Approve', bg: 'bg-green-100', text: 'text-green-600' }, +}; + +export const renderStatusBadge = (statusRaw: string | null | undefined) => { + const status = statusRaw as StatusCode; + const { label, bg, text } = statusMap[status] ?? { + label: 'Unknown', + bg: 'bg-gray-100', + text: 'text-gray-600', + }; + + return ( + + {label} + + ); +}; + +// const { reload } = useDataGrid(); + +const TransactionDisbursementProvider = ({ children }: { children: React.ReactNode }) => { + + return ( + + +
+ {children} +
+
+ ); +}; + +export { TransactionDisbursementProvider, TransactionDisbursementContext }; +export type { TransactionDisbursementProps }; diff --git a/src/pages/transaction/disbursement-saldo/hooks/index.tsx b/src/pages/transaction/disbursement-saldo/hooks/index.tsx new file mode 100644 index 0000000..2def845 --- /dev/null +++ b/src/pages/transaction/disbursement-saldo/hooks/index.tsx @@ -0,0 +1,2 @@ +export * from './TransactionDisbursementContext'; +export * from './useTransactionTopupContext'; diff --git a/src/pages/transaction/disbursement-saldo/hooks/useTransactionDisbursementContext.tsx b/src/pages/transaction/disbursement-saldo/hooks/useTransactionDisbursementContext.tsx new file mode 100644 index 0000000..5137bc2 --- /dev/null +++ b/src/pages/transaction/disbursement-saldo/hooks/useTransactionDisbursementContext.tsx @@ -0,0 +1,12 @@ +import { useContext } from 'react'; +import { TransactionDisbursementContext } from './TransactionDisbursementContext'; + +const useTransactionDisbursementContext = () => { + const context = useContext(TransactionDisbursementContext); + + if (!context) throw new Error('useTransactionDisbursementContext must be used within AuthProvider'); + + return context; +}; + +export { useTransactionDisbursementContext }; diff --git a/src/pages/transaction/topup/TransactionTopup.tsx b/src/pages/transaction/topup/TransactionTopup.tsx new file mode 100644 index 0000000..d6e343c --- /dev/null +++ b/src/pages/transaction/topup/TransactionTopup.tsx @@ -0,0 +1,95 @@ +import { Container, DataGridInner } from '@/components'; +import { TransactionTopupProvider } from './hooks/TransactionTopupContext'; +import { Breadcrumbs, Link } from '@mui/material'; +import { Helmet } from 'react-helmet'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { useState } from 'react'; +import { useCallApi } from '@/hooks'; +import { apiConfig } from '@/config/api.config'; +import { toast } from 'sonner'; + +const TransactionTopup = () => { + const [form, setForm] = useState({ + topupAmount: '', + pin: '' + }); + const { GetData, PostData } = useCallApi(); + const API_URL = apiConfig.transaction; + + const handleSubmit = async (e: any) => { + e.preventDefault(); + console.log('Submitted Data:', form); + try { + let requestTopup = await PostData(`${API_URL}/transaction/request-topup`, { + amount: form.topupAmount, + pin: form.pin + }) + console.log(requestTopup) + if (requestTopup?.status == true) { + toast.success('Success Request Topup') + } else { + toast.warning(`${requestTopup?.message}`) + } + } catch (error) { + toast.warning('Failed') + } + // TODO: Kirim ke backend atau proses lainnya + }; + + return ( + <> + + TPAY | Transaction Topup Request + + + +

MANAGE TRANSACTION TOPUP REQUEST

+ + + Dashboard + + + Transaction + + + Topup + + + +
+
+ {/* form */} +
+
+ + setForm({ ...form, topupAmount: e.target.value })} + /> +
+
+ + setForm({ ...form, pin: e.target.value })} + /> +
+
+ +
+
+
+
+
+
+
+ + ); +}; + +export default TransactionTopup; diff --git a/src/pages/transaction/topup/blocks/DetailDialog.tsx b/src/pages/transaction/topup/blocks/DetailDialog.tsx new file mode 100644 index 0000000..8b1ca90 --- /dev/null +++ b/src/pages/transaction/topup/blocks/DetailDialog.tsx @@ -0,0 +1,105 @@ +import { + Dialog, + DialogBody, + DialogContent, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { useManageKycDeletionContext } from '../hooks'; +import { apiConfig } from '@/config/api.config'; +import { useDataGrid } from '@/components'; + +const API_URL = apiConfig.service_customer; +const DetailDialog = () => { + const { showDetailDialog, setShowDetailDialog, detailKyc, handleApproveReject } = useManageKycDeletionContext(); + const { reload } = useDataGrid(); + + return ( + + + + Customer Deletion Details + + + {/* Tab Content */} + {detailKyc && detailKyc != null ? ( +
+ {generateInput(detailKyc, null, 'Group', 'group_name', 'text', false, true)} + {generateInput(detailKyc, null, 'Username', 'username', 'text', false, true)} + {generateInput(detailKyc, null, 'Full Name', 'fullname', 'text', false, true)} + {generateInput(detailKyc, null, 'Gender', 'customers_gender', 'text', false, true)} + {generateInput(detailKyc, null, 'Date of Birth', 'birthdate', 'date', false, true)} + {generateInput(detailKyc, null, 'Mother Name', 'registered_mother_fullname', 'text', false, true)} + {generateInput(detailKyc, null, 'MSISDN', 'registered_msisdn', 'text', false, true)} + {generateInput(detailKyc, null, 'Reason Deletion', 'reason_deletion', 'text', false, true)} + {generateInput(detailKyc, null, 'Reason Note', 'reason_note', 'text', false, true)} + {generateInput(detailKyc, null, 'Status Approval', 'status_approve', 'text', false, true)} + +
+ + + +
+
+ ) : (
)} +
+
+
+ ); +}; + +export default DetailDialog; + +function generateInput(formData: any, handleChange: any, label: string, name: string, type: string, required: boolean, disabled: boolean) { + function generateDate(isoString: string) { + return isoString.slice(0, 10); // "2000-01-18" + } + + type Code = 'W' | 'Y' | 'N' | 'T' | 'P' | 'D' | 'L'; + interface Reason { + label: string; + } + + const statusMap: Record = { + W: { label: 'Waiting Approval' }, + Y: { label: 'Approve' }, + N: { label: 'Reject' }, + T: { label: 'Tidak lagi menggunakan layanan' }, + P: { label: 'Privasi dan keamanan' }, + D: { label: 'Akun ganda' }, + L: { label: 'Lainnya' } + }; + if (name == 'reason_deletion' || name == 'status_approve') { + const status = formData[name] as Code + const fixStatus = statusMap[status] ?? { label: formData[name] } + formData[name] = fixStatus.label + } + return ( + <> +
+
+ + +
+
+ + ) +} \ No newline at end of file diff --git a/src/pages/transaction/topup/blocks/ListToolbar.tsx b/src/pages/transaction/topup/blocks/ListToolbar.tsx new file mode 100644 index 0000000..013a15c --- /dev/null +++ b/src/pages/transaction/topup/blocks/ListToolbar.tsx @@ -0,0 +1,61 @@ +import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components'; +import { Button } from '@/components/ui/button'; +import { useCallback, useState, useEffect } from 'react'; +import { toast } from 'sonner'; + +const ListToolbar = () => { + const { table, reload } = useDataGrid(); + + // Set the initial state for trxDate + const [trxDate, settrxDate] = useState({ from: '', to: '' }); + + // Function to format date to YYYY-MM-DD + const formatDate = (date: Date): string => { + return date.toISOString().split('T')[0]; + }; + + // useEffect to set the default date values + useEffect(() => { + const today = new Date(); + const nextWeek = new Date(today); + nextWeek.setDate(today.getDate() + 7); + + settrxDate({ + from: formatDate(today), // Set 'from' to today + to: formatDate(nextWeek), // Set 'to' to 7 days later + }); + }, []); + + const handleFilterData = useCallback(() => { + try { + table.getColumn('transaction_date')?.setFilterValue(trxDate); + } catch (error) { + toast.error('Error applying filter'); + console.error('Error applying filter:', error); + } + }, [trxDate, table]); + + useEffect(() => { + if (trxDate.from && trxDate.to) { + handleFilterData(); + } + }, [trxDate]); + + return ( +
+
+
+
+ + + +
+
+
+
+ ); +}; + +export default ListToolbar; diff --git a/src/pages/transaction/topup/hooks/TransactionTopupContext.tsx b/src/pages/transaction/topup/hooks/TransactionTopupContext.tsx new file mode 100644 index 0000000..ffe6337 --- /dev/null +++ b/src/pages/transaction/topup/hooks/TransactionTopupContext.tsx @@ -0,0 +1,81 @@ +import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components'; +import { Toaster } from '@/components/ui/sonner'; +import { toast } from 'sonner'; +import { apiConfig } from '@/config/api.config'; +import { ColumnDef } from '@tanstack/react-table'; +import { createContext, useCallback, useMemo, useState } from 'react'; +import ListToolbar from '../blocks/ListToolbar'; +import { useCallApi } from '@/hooks'; +import moment from 'moment'; +import DetailDialog from '../blocks/DetailDialog'; + +interface TransactionTopupProps { + id: string; + customers_id: string; + group_id: string; + username: string; + fullname: string; + email: string; + status: string; + created_at: Date; +} + +interface ContextProps { + +} + +const initialProps: ContextProps = { + +}; + +const TransactionTopupContext = createContext(initialProps); +const API_URL = apiConfig.service_customer; + +type StatusCode = 'W' | 'Y' | 'N' | 'T'; + +interface StatusInfo { + label: string; + bg: string; + text: string; +} + +const statusMap: Record = { + W: { label: 'Waiting Approval', bg: 'bg-yellow-100', text: 'text-yellow-600' }, + T: { label: 'No Need', bg: 'bg-blue-100', text: 'text-blue-600' }, + N: { label: 'Reject', bg: 'bg-red-100', text: 'text-red-600' }, + Y: { label: 'Approve', bg: 'bg-green-100', text: 'text-green-600' }, +}; + +export const renderStatusBadge = (statusRaw: string | null | undefined) => { + const status = statusRaw as StatusCode; + const { label, bg, text } = statusMap[status] ?? { + label: 'Unknown', + bg: 'bg-gray-100', + text: 'text-gray-600', + }; + + return ( + + {label} + + ); +}; + +// const { reload } = useDataGrid(); + +const TransactionTopupProvider = ({ children }: { children: React.ReactNode }) => { + + return ( + + +
+ {children} +
+
+ ); +}; + +export { TransactionTopupProvider, TransactionTopupContext }; +export type { TransactionTopupProps }; diff --git a/src/pages/transaction/topup/hooks/index.tsx b/src/pages/transaction/topup/hooks/index.tsx new file mode 100644 index 0000000..2b919d5 --- /dev/null +++ b/src/pages/transaction/topup/hooks/index.tsx @@ -0,0 +1,2 @@ +export * from './TransactionTopupContext'; +export * from './useTransactionTopupContext'; diff --git a/src/pages/transaction/topup/hooks/useTransactionTopupContext.tsx b/src/pages/transaction/topup/hooks/useTransactionTopupContext.tsx new file mode 100644 index 0000000..cd253c2 --- /dev/null +++ b/src/pages/transaction/topup/hooks/useTransactionTopupContext.tsx @@ -0,0 +1,12 @@ +import { useContext } from 'react'; +import { TransactionTopupContext } from './TransactionTopupContext'; + +const useTransactionTopupContext = () => { + const context = useContext(TransactionTopupContext); + + if (!context) throw new Error('useTransactionTopupContext must be used within AuthProvider'); + + return context; +}; + +export { useTransactionTopupContext }; diff --git a/src/routing/AppRoutingSetup.tsx b/src/routing/AppRoutingSetup.tsx index 865effb..786cd1e 100644 --- a/src/routing/AppRoutingSetup.tsx +++ b/src/routing/AppRoutingSetup.tsx @@ -10,6 +10,8 @@ import DashboardHomePage from '@/pages/dashboards/home/DashboardHomePage'; import ManageUserPage from '@/pages/settings/user/manage-user/ManageUserPage'; import Transaction from '@/pages/transaction/history-transaction/Transaction'; import ApprovalTransaction from '@/pages/transaction/approval-transaction/ApprovalTransaction'; +import TransactionTopup from '@/pages/transaction/topup/TransactionTopup'; +import TransactionDisbursement from '@/pages/transaction/disbursement-saldo/TransactionDisbursement'; import LogActivityPage from '@/pages/settings/user/log-activity/LogActivityPage'; import ManagePositionPage from '@/pages/settings/user/manage-position/ManagePositionPage'; import ManageAccount from '@/pages/account/manage-account/ManageAccount'; @@ -96,6 +98,8 @@ const AppRoutingSetup = (): ReactElement => { } /> } /> + } /> + } /> } /> } /> } />