diff --git a/src/auth/pages/jwt/Login.tsx b/src/auth/pages/jwt/Login.tsx index 28a283f..f8c0a64 100644 --- a/src/auth/pages/jwt/Login.tsx +++ b/src/auth/pages/jwt/Login.tsx @@ -9,6 +9,7 @@ import { useAuthContext } from '@/auth'; import { useLayout } from '@/providers'; import { Alert } from '@/components'; import moment from 'moment'; +import { Helmet } from 'react-helmet'; const loginSchema = Yup.object().shape({ username: Yup.string().required('Username is required'), @@ -76,101 +77,106 @@ const Login = () => { }; return ( -
-
-
-

Sign in

-
- {formik.status && {formik.status}} -
- - - {formik.touched.username && formik.errors.username && ( - - {formik.errors.username} - - )} -
- -
-
- -
- - {formik.touched.password && formik.errors.password && ( - - {formik.errors.password} - - )} -
- -
- - - Forgot Password? - -
- - -
-

- Copyright {moment().year()} © Telkomcel All rights reserved. -

-
-
-
+
+

Sign in

+
+ {formik.status && {formik.status}} +
+ + + {formik.touched.username && formik.errors.username && ( + + {formik.errors.username} + + )} +
+ +
+
+ +
+ + {formik.touched.password && formik.errors.password && ( + + {formik.errors.password} + + )} +
+ +
+ + + Forgot Password? + +
+ + +
+

+ Copyright {moment().year()} © Telkomcel All rights reserved. +

+
+ + + ); }; diff --git a/src/auth/pages/jwt/reset-password/ResetPassword.tsx b/src/auth/pages/jwt/reset-password/ResetPassword.tsx index f8db220..857219c 100644 --- a/src/auth/pages/jwt/reset-password/ResetPassword.tsx +++ b/src/auth/pages/jwt/reset-password/ResetPassword.tsx @@ -9,6 +9,7 @@ import { useAuthContext } from '@/auth/useAuthContext'; import { Alert, KeenIcon } from '@/components'; import { useLayout } from '@/providers'; import { AxiosError } from 'axios'; +import { Helmet } from 'react-helmet'; const initialValues = { email: '' @@ -64,70 +65,75 @@ const ResetPassword = () => { } }); return ( -
-
-
-

Your Email

- - Enter your email to reset password - -
- - {hasErrors && {formik.status}} - - {hasErrors === false && ( - - Password reset link sent. Please check your email to proceed - - )} - -
- - - {formik.touched.email && formik.errors.email && ( - - {formik.errors.email} + <> + + TPAY | Reset Password + +
+ +
+

Your Email

+ + Enter your email to reset password +
+ + {hasErrors && {formik.status}} + + {hasErrors === false && ( + + Password reset link sent. Please check your email to proceed + )} -
-
- +
+ + + {formik.touched.email && formik.errors.email && ( + + {formik.errors.email} + + )} +
- - - Back to Login - -
- -
+
+ + + + + Back to Login + +
+ +
+ ); }; diff --git a/src/components/confirm.tsx b/src/components/confirm.tsx index b3a5596..67e18e9 100644 --- a/src/components/confirm.tsx +++ b/src/components/confirm.tsx @@ -1,23 +1,21 @@ -import React from "react"; -import Dialog from "@mui/material/Dialog"; -import DialogActions from "@mui/material/DialogActions"; -import DialogContent from "@mui/material/DialogContent"; -import DialogTitle from "@mui/material/DialogTitle"; import Button from "@mui/material/Button"; +import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogDescription, DialogBody } from '@/components/ui/dialog'; -const ConfirmDialog = ({ open, onClose, title, content, onYes, onNo }: any) => { +const ConfirmDialog = ({ open, onClose, title, content, onYes, onNo, onOpenChange }: any) => { return ( - - {title && {title}} - {content && {content}} - - - - + + + + {title && {title}} + + {content && {content}} + + + + + + + ); }; diff --git a/src/components/loaders/LoaderTransparant.tsx b/src/components/loaders/LoaderTransparant.tsx new file mode 100644 index 0000000..b1eb099 --- /dev/null +++ b/src/components/loaders/LoaderTransparant.tsx @@ -0,0 +1,16 @@ +import { toAbsoluteUrl } from '@/utils'; + +const LoaderTransparant = () => { + return ( +
+ logo +
Loading...
+
+ ); +}; + +export { LoaderTransparant }; diff --git a/src/components/loaders/index.ts b/src/components/loaders/index.ts index 1d1dad1..1b8a61e 100644 --- a/src/components/loaders/index.ts +++ b/src/components/loaders/index.ts @@ -1,3 +1,4 @@ export * from './ContentLoader'; export * from './ProgressBarLoader'; export * from './ScreenLoader'; +export * from './LoaderTransparant'; diff --git a/src/config/api.config.ts b/src/config/api.config.ts index 535dc2a..81e8652 100644 --- a/src/config/api.config.ts +++ b/src/config/api.config.ts @@ -4,7 +4,9 @@ interface apiConfigProps { service_master_data: string; service_transaction: string; service_wallet: string; - transaction: string + transaction: string; + nationality: string; + service_disbursement: string; } const API_URL = import.meta.env.VITE_APP_API_URL; @@ -16,7 +18,10 @@ const apiConfig: apiConfigProps = { service_master_data: `${API_URL}/t`, service_transaction: `${API_URL}/tt`, service_wallet: `${API_URL}/w`, - transaction: `${API_URL}/x` + transaction: `${API_URL}/x`, + service_disbursement: `${API_URL}/s`, + nationality: `https://tpay.shiblysolution.id/cms/api/mobile/list-country/ +` }; export { apiConfig }; diff --git a/src/hooks/useCallApi.ts b/src/hooks/useCallApi.ts index a359418..c610dd0 100644 --- a/src/hooks/useCallApi.ts +++ b/src/hooks/useCallApi.ts @@ -57,6 +57,24 @@ const useCallApi = () => { } }, []); + const PostDataFile = useCallback(async (url: string, field: any, config = {}) => { + try { + const response = await axios.post(url, field, config); + const result = response.data; + + if (result.status) { + return { status: true, message: result.data }; + } + return { status: false, message: result.message }; + } catch (error: any) { + const { response } = error; + return { + status: false, + message: response?.data?.error ?? response?.data?.message ?? 'Something went wrong' + }; + } + }, []); + const PutData = useCallback(async (url: string, field: any) => { try { const response = await axios.put(url, field); @@ -85,7 +103,7 @@ const useCallApi = () => { } }, []); - return { GetData, PostData, PutData, DeleteData, GetExportData }; + return { GetData, PostData, PutData, DeleteData, GetExportData, PostDataFile }; }; export { useCallApi }; diff --git a/src/pages/disbursement/history-transaction/HistoryTransaction.tsx b/src/pages/disbursement/history-transaction/HistoryTransaction.tsx new file mode 100644 index 0000000..61b9d2d --- /dev/null +++ b/src/pages/disbursement/history-transaction/HistoryTransaction.tsx @@ -0,0 +1,39 @@ +import { Container, DataGridInner } from '@/components'; +import { TransactionProvider } from './hooks/TransactionContext'; +import { Breadcrumbs, Link } from '@mui/material'; +import { Helmet } from 'react-helmet'; +import { UploadBatchDialog } from './blocks/UploadBatchDialog'; + +const HistoryTransactionDisbursement = () => { + return ( + <> + + TPAY | History Disbursement + + + +

DISBURSEMENT

+ + + Dashboard + + + + Disbursement + + + + History Disbursement + + +
+ +
+ +
+
+ + ); +}; + +export default HistoryTransactionDisbursement; diff --git a/src/pages/disbursement/history-transaction/blocks/DetailTransaction.tsx b/src/pages/disbursement/history-transaction/blocks/DetailTransaction.tsx new file mode 100644 index 0000000..28df134 --- /dev/null +++ b/src/pages/disbursement/history-transaction/blocks/DetailTransaction.tsx @@ -0,0 +1,137 @@ +import { useTransactionContext } from '../hooks/useTransactionContext'; +import { useCallApi } from '@/hooks'; +import { apiConfig } from '@/config/api.config'; +import { useEffect, useState } from 'react'; +import { + Dialog, + DialogBody, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog'; + +const API_URL = apiConfig.service_disbursement; + +type StatusCode = 'W' | 'P' | 'F' | 'D' | 'Y'; + +interface StatusInfo { + label: string; + bg: string; + text: string; +} + +const statusMap: Record = { + W: { label: 'Waiting', bg: 'bg-yellow-100', text: 'text-yellow-600' }, + P: { label: 'Pending', bg: 'bg-blue-100', text: 'text-blue-600' }, + F: { label: 'Fail', bg: 'bg-red-100', text: 'text-red-600' }, + D: { label: 'Done', bg: 'bg-green-100', text: 'text-green-600' }, + Y: { label: 'Active', 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 DetailTransaction = () => { + const { GetData } = useCallApi(); + const { + showDetailDialog, + setShowDetailDialog, + selectedTransactionId + } = useTransactionContext(); + + const [transactionDetails, setTransactionDetails] = useState(null); + + useEffect(() => { + const fetchTransactionDetails = async () => { + console.log(selectedTransactionId) + if (selectedTransactionId) { + try { + const response = await GetData(`${API_URL}/transaction/history/${selectedTransactionId}`, { + id: selectedTransactionId + }); + // console.log(response?.data); + setTransactionDetails(response?.data); + } catch (error) { + console.error('Error fetching transaction', error); + } + } + }; + + if (showDetailDialog && selectedTransactionId) { + fetchTransactionDetails(); + } + }, [showDetailDialog, selectedTransactionId, GetData]); + + const [activeTab, setActiveTab] = useState('detail'); // 'detail', 'log', 'approve' + + return ( + + + + Transaction Details + + + {/* Tab Content */} +
+
+
+ + + + + + + + + + + + + + + {transactionDetails?.log && transactionDetails?.log.length > 0 ? ( + transactionDetails.log.map((log: { customer: any, amount: number, remark: string, reference: string, response_date: string, payment_response: string, status: string}, index: number) => ( + + + + + + + + + + + )) + ) : ( + + + + )} + +
UsernameNameTransfer AmountDescriptionInvoice NumberProcess DateResponseStatus
{log.customer.username ?? '-'}{log.customer.fullname ?? '-'}{log.amount ?? '-'}{log.remark ?? '-'}{log.reference ?? '-'}{log.response_date ?? '-'}{log.payment_response ?? '-'}{renderStatusBadge(log.status) ?? '-'}
+ No logs available +
+
+
+ +
+
+
+
+ ); +}; + +export default DetailTransaction; diff --git a/src/pages/disbursement/history-transaction/blocks/ListToolbar.tsx b/src/pages/disbursement/history-transaction/blocks/ListToolbar.tsx new file mode 100644 index 0000000..4b6c69f --- /dev/null +++ b/src/pages/disbursement/history-transaction/blocks/ListToolbar.tsx @@ -0,0 +1,70 @@ +import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components'; +import { useTransactionContext } from '../hooks/useTransactionContext'; +import { Button } from '@/components/ui/button'; +import { useCallback, useState, useEffect } from 'react'; +import { toast } from 'sonner'; + +const ListToolbar = () => { + const { table, reload } = useDataGrid(); + const { handleUploadBatchDialog } = useTransactionContext(); + + // 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/disbursement/history-transaction/blocks/UploadBatchDialog.tsx b/src/pages/disbursement/history-transaction/blocks/UploadBatchDialog.tsx new file mode 100644 index 0000000..237a72e --- /dev/null +++ b/src/pages/disbursement/history-transaction/blocks/UploadBatchDialog.tsx @@ -0,0 +1,194 @@ +import { MouseEvent, useCallback, useEffect, useRef, useState } from 'react'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from '@/components/ui/select'; + +import { + Dialog, + DialogBody, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog'; +import { useTransactionContext } from '../hooks'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { apiConfig } from '@/config/api.config'; +import { Alert, KeenIcon, useDataGrid } from '@/components'; +import { toast } from 'sonner'; +import { useCallApi } from '@/hooks'; +import { doSaveLogActivity } from '@/actions/GlobalActions'; +import clsx from 'clsx'; + +const API_URL = apiConfig.service_disbursement; + +const UploadBatchDialog = () => { + const parentRef = useRef(null); + const { showUploadBatchDialog, handleUploadBatchDialog } = useTransactionContext(); + const { reload } = useDataGrid(); + const { PostData, PostDataFile, GetData } = useCallApi(); + const [alert, setAlert] = useState({ + show: false, + message: '' + }); + const initialState: { + execution_date: string, + file: File | null; + } = { + execution_date: '', + file: null + } + const [formField, setFormField] = useState(initialState); + + const resetForm = () => { + setFormField(initialState); + setAlert({ show: false, message: '' }); + }; + + /* actions */ + const doUploadBatch = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + + const formData = new FormData(); + formData.append('execution_date', formField.execution_date); + if (formField.file) { + formData.append('file', formField.file); + } + + try { + const response = await PostDataFile(`${API_URL}/upload-excel`, formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }); + + if (response?.status) { + handleUploadBatchDialog(false); + resetForm(); + reload(); + + const createActivity = { + module: 'Disbursement', + description: `Create New Disbursement => ${formField.file?.name}`, + action: 'C' + }; + + doSaveLogActivity(createActivity); + toast.success('Success Create Disbursement'); + } else { + toast.error('Failed to create disbursement'); + setAlert({ show: true, message: response?.message ?? 'Failed to create disbursement.' }); + } + } catch (error) { + toast.error('Error uploading batch'); + setAlert({ show: true, message: 'Something went wrong. Please try again.' }); + } + }, + [formField] + ); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + console.log('Form data before submit:', formField); + + if ( + formField.execution_date.trim() === '' || + formField.file === null + ) { + setAlert({ show: true, message: 'Please fill in all required fields.' }); + return; + } + + doUploadBatch(e); + // console.log(formField); + setAlert({ show: false, message: '' }); + }; + + useEffect(() => { + if (showUploadBatchDialog === false) { + resetForm(); + } + }, [showUploadBatchDialog]); + + return ( + handleUploadBatchDialog(open)}> + + + + +
+
+

Upload Batch

+
+
+
{ + handleUploadBatchDialog(false); + resetForm(); + }} + > + +
+
+
+ +
+ {alert.show && ( + +

{alert.message}

+
+ )} +
+
+
+
+ + + setFormField((prev) => ({ ...prev, execution_date: target.value })) + } + /> +
+
+
+
+ + + setFormField((prev) => ({ ...prev, file: target.files?.[0] ?? null })) + } + /> +
+
+
+ +
+
+
+
+
+
+
+ ); +}; + +export { UploadBatchDialog }; diff --git a/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx b/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx new file mode 100644 index 0000000..19e2382 --- /dev/null +++ b/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx @@ -0,0 +1,289 @@ +import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components'; +import { Toaster } from '@/components/ui/sonner'; +import { apiConfig } from '@/config/api.config'; +import { ColumnDef } from '@tanstack/react-table'; +import axios from 'axios'; +import React, { createContext, useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallApi } from '@/hooks'; +import ListToolbar from '../blocks/ListToolbar'; +import { Button } from '@/components/ui/button'; +import { useNavigate } from 'react-router'; +import moment from 'moment'; +import DetailTransaction from '../blocks/DetailTransaction'; + +interface TransactionProps { + id: number; + name: string; +} + +interface ContextProps { + getTransactionLists: ( + limit: number, + page: number, + with_deleted: boolean, + order_field: any, + order_direction: any, + filter: any + ) => Promise<{ data: TransactionProps[]; totalCount: number } | undefined>; + showDetailDialog: boolean; + setShowDetailDialog: React.Dispatch>; + selectedTransactionId: number | null; + setSelectedTransactionId: React.Dispatch>; + showUploadBatchDialog: boolean; + handleUploadBatchDialog: (show: boolean) => void; +} + +const initialProps: ContextProps = { + getTransactionLists: async () => ({ data: [], totalCount: 0 }), + showDetailDialog: false, + setShowDetailDialog: () => { }, + selectedTransactionId: null, + setSelectedTransactionId: () => { }, + showUploadBatchDialog: false, + handleUploadBatchDialog: (show: boolean) => {}, +}; + +type StatusCode = 'W' | 'P' | 'F' | 'D' | 'Y'; + +interface StatusInfo { + label: string; + bg: string; + text: string; +} + +const statusMap: Record = { + W: { label: 'Waiting', bg: 'bg-yellow-100', text: 'text-yellow-600' }, + P: { label: 'Pending', bg: 'bg-blue-100', text: 'text-blue-600' }, + F: { label: 'Fail', bg: 'bg-red-100', text: 'text-red-600' }, + D: { label: 'Done', bg: 'bg-green-100', text: 'text-green-600' }, + Y: { label: 'Active', bg: 'bg-green-100', text: 'text-green-600' }, +}; + + +const ManageTransactionContext = createContext(initialProps); +const API_URL = apiConfig.service_disbursement; + +const TransactionProvider = ({ children }: { children: React.ReactNode }) => { + const [showDetailDialog, setShowDetailDialog] = useState(false); + const [selectedTransactionId, setSelectedTransactionId] = useState(null); + const [showUploadBatchDialog, setShowUploadBatchDialog] = useState(false); + const [transaction, setTransaction] = useState([]); + const { GetData } = useCallApi(); + const navigate = useNavigate(); + + const handleUploadBatchDialog = useCallback((show: boolean) => { + setShowUploadBatchDialog(show); + }, []); + + const columns = useMemo[]>( + () => [ + // { + // accessorKey: 'transaction_date', + // header: ({ column }) => , + // enableSorting: false, + // enableHiding: false, + // meta: { + // headerClassName: 'w-[250px]' + // } + // }, + { + accessorKey: 'file_name', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + }, + }, + { + accessorFn: (row) => { + return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(row.amount); + }, + id: 'amount', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]', + }, + }, + { + accessorKey: 'total_record', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + }, + }, + { + accessorKey: 'total_success', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + }, + }, + { + accessorKey: 'total_fail', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + }, + }, + { + accessorKey: 'total_pending', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + }, + }, + { + accessorFn: (row) => row.status, + id: 'status', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + cell: ({ row }) => { + const status = row.original.status as StatusCode; + const { label, bg, text } = statusMap[status] ?? { + label: 'Unknown', + bg: 'bg-gray-100', + text: 'text-gray-600', + }; + + return ( + + {label} + + ); + }, + meta: { + headerClassName: 'w-[100px]', + cellClassName: 'text-center', + }, + }, + { + accessorFn: (row) => row.execution_date, + id: 'execution_date', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + cell: ({ row }) => moment(row.original.execution_date).format('YYYY-MM-DD HH:mm:ss') + }, + { + accessorFn: (row) => row.done_date, + id: 'done_date', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + cell: ({ row }) => row.original.done_date ? moment(row.original.done_date).format('YYYY-MM-DD HH:mm:ss') : '' + }, + { + id: 'actions', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + cell: (data) => { + const row = data.row.original; + return ( +
+ +
+ ); + }, + meta: { + headerClassName: 'w-[100px]', + cellClassName: 'text-center' + } + } + ], + []); + + const getTransactionLists = async (page: number, limit: number, sorting: any, filter: any) => { + try { + let startdate; + let enddate; + let formattedFilter; + + if (filter == undefined || filter.length == 0) { + const today = new Date(); + const nextWeek = new Date(); + nextWeek.setDate(today.getDate() + 7); + + startdate = today.toISOString().split('T')[0]; + enddate = nextWeek.toISOString().split('T')[0]; + } else if (filter != undefined || filter.length != 0) { + startdate = filter[0].value.from; + enddate = filter[0].value.to; + } + + formattedFilter = { + + }; + + const response = await GetData(`${API_URL}/transaction/history`, { + limit, + page: page + 1, + with_deleted: false, + order_field: "id", + order_direction: 'DESC', + filter: JSON.stringify(formattedFilter) + }); + + setTransaction(response?.data.list); + return { data: response?.data.list, totalCount: response?.data.total_count }; + } catch (error) { + console.error('Error fetching transaction', error); + } + }; + + return ( + + + + + } + layout={{ card: true }} + sorting={[{ id: 'id', desc: false }]} + serverSide={true} + onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => + getTransactionLists(pageIndex, pageSize, sorting, columnFilters) + } + > + {children} + + + ); +}; + +export { TransactionProvider, ManageTransactionContext }; +export type { TransactionProps }; diff --git a/src/pages/disbursement/history-transaction/hooks/index.tsx b/src/pages/disbursement/history-transaction/hooks/index.tsx new file mode 100644 index 0000000..d25a2f6 --- /dev/null +++ b/src/pages/disbursement/history-transaction/hooks/index.tsx @@ -0,0 +1,2 @@ +export * from './TransactionContext'; +export * from './useTransactionContext'; diff --git a/src/pages/disbursement/history-transaction/hooks/useTransactionContext.tsx b/src/pages/disbursement/history-transaction/hooks/useTransactionContext.tsx new file mode 100644 index 0000000..4af7036 --- /dev/null +++ b/src/pages/disbursement/history-transaction/hooks/useTransactionContext.tsx @@ -0,0 +1,12 @@ +import { useContext } from 'react'; +import { ManageTransactionContext } from './TransactionContext'; + +const useTransactionContext = () => { + const context = useContext(ManageTransactionContext); + + if (!context) throw new Error('useTransactionContext must be used within AuthProvider'); + + return context; +}; + +export { useTransactionContext }; diff --git a/src/pages/master/aldeias/AldeiasMaster.tsx b/src/pages/master/aldeias/AldeiasMaster.tsx index bb3fec1..05f8a0e 100644 --- a/src/pages/master/aldeias/AldeiasMaster.tsx +++ b/src/pages/master/aldeias/AldeiasMaster.tsx @@ -9,11 +9,11 @@ const AldeiasMaster = () => { return ( <> - TPAY | Manage Aldeias + TPAY | Manage Aldeia -

Aldeias

+

Aldeia

Dashboard @@ -24,7 +24,7 @@ const AldeiasMaster = () => { - Manage Aldeias + Manage Aldeia diff --git a/src/pages/master/aldeias/blocks/AddDialog.tsx b/src/pages/master/aldeias/blocks/AddDialog.tsx index 38e4c1b..a1c566c 100644 --- a/src/pages/master/aldeias/blocks/AddDialog.tsx +++ b/src/pages/master/aldeias/blocks/AddDialog.tsx @@ -69,18 +69,18 @@ const AddDialog = () => { if (response?.status) { resetForm(); handleAddDialog(false); - toast.success('Success Create Aldeias'); + toast.success('Success Create Aldeia'); reload(); const createActivity = { - module: 'Manage Aldeias', + module: 'Manage Aldeia', description: `Create Aldeia => ${formField.name}`, action: 'C' }; doSaveLogActivity(createActivity); } else { - toast.error('Error Create Aldeias'); - setAlert({ show: true, message: 'Failed to create Aldeias. Please try again.' }); + toast.error('Error Create Aldeia'); + setAlert({ show: true, message: 'Failed to create Aldeia. Please try again.' }); } }, [formField] @@ -100,7 +100,8 @@ const AddDialog = () => { // console.log('SUCOS', response?.data); setSucos(response?.data.list); } catch (error) { - console.error('Error fetching municipios', error); + // console.error('Error fetching Municipio', error); + setAlert({ show: true, message: 'Failed to fetch Municipio. Please try again.' }); } }; @@ -141,7 +142,7 @@ const AddDialog = () => { handleAddDialog(open)}> - Aldeias - Create + Aldeia - Create @@ -157,7 +158,7 @@ const AddDialog = () => {
{ - @@ -192,7 +197,7 @@ const AddDialog = () => { {sucos.map((suco) => ( { setFormField({ ...formField, diff --git a/src/pages/master/aldeias/blocks/DeleteDialog.tsx b/src/pages/master/aldeias/blocks/DeleteDialog.tsx index 92e570e..44f0a81 100644 --- a/src/pages/master/aldeias/blocks/DeleteDialog.tsx +++ b/src/pages/master/aldeias/blocks/DeleteDialog.tsx @@ -32,11 +32,11 @@ const DeleteDialog = () => { if (response?.status) { setAlert({ show: false, message: '' }); handleDeleteDialog(false, null); - toast.success('Success Delete Aldeias'); + toast.success('Success Delete Aldeia'); reload(); const createActivity = { - module: 'Manage Aldeias', + module: 'Manage Aldeia', description: `Delete Aldeia => ${selectedAldeias}`, action: 'D' }; @@ -44,7 +44,7 @@ const DeleteDialog = () => { doSaveLogActivity(createActivity); } else { setAlert({ show: true, message: response?.message }); - toast.error('Failed Delete Aldeias'); + toast.error('Failed Delete Aldeia'); } }, [selectedAldeias, DeleteData, handleDeleteDialog, reload]); diff --git a/src/pages/master/aldeias/blocks/EditDialog.tsx b/src/pages/master/aldeias/blocks/EditDialog.tsx index 79eb0ce..0eec124 100644 --- a/src/pages/master/aldeias/blocks/EditDialog.tsx +++ b/src/pages/master/aldeias/blocks/EditDialog.tsx @@ -68,18 +68,18 @@ const EditDialog = () => { if (response?.status) { resetForm(); handleEditDialog(false, null); - toast.success('Success Update Aldeias'); + toast.success('Success Update Aldeia'); reload(); const createActivity = { - module: 'Manage Aldeias', + module: 'Manage Aldeia', description: `Edit Aldeia => ${selectedAldeias}`, action: 'U' }; doSaveLogActivity(createActivity); } else { - toast.error('Error Update Aldeias'); - setAlert({ show: true, message: 'Error Update Aldeias' }); + toast.error('Error Update Aldeia'); + setAlert({ show: true, message: 'Error Update Aldeia' }); } }, [selectedAldeias, formField] @@ -99,7 +99,8 @@ const EditDialog = () => { // console.log('SUCOS', response?.data); setSucos(response?.data.list); } catch (error) { - console.error('Error fetching municipios', error); + console.error('Error fetching Sucos', error); + setAlert({ show: true, message: 'Failed to get Sucos. Please try again.' }); } }; @@ -164,7 +165,7 @@ const EditDialog = () => { handleEditDialog(open, null)}> - Aldeias - Update + Aldeia - Update @@ -180,7 +181,7 @@ const EditDialog = () => {
{ - diff --git a/src/pages/master/aldeias/blocks/ListToolbar.tsx b/src/pages/master/aldeias/blocks/ListToolbar.tsx index fbf8537..2454a1c 100644 --- a/src/pages/master/aldeias/blocks/ListToolbar.tsx +++ b/src/pages/master/aldeias/blocks/ListToolbar.tsx @@ -15,7 +15,7 @@ const ListToolbar = () => { table.getColumn('name')?.setFilterValue(event.target.value)} /> diff --git a/src/pages/master/aldeias/hooks/ManageAldeiasContext.tsx b/src/pages/master/aldeias/hooks/ManageAldeiasContext.tsx index 03519e9..355a683 100644 --- a/src/pages/master/aldeias/hooks/ManageAldeiasContext.tsx +++ b/src/pages/master/aldeias/hooks/ManageAldeiasContext.tsx @@ -80,7 +80,7 @@ const ManageAldeiasContextProvider = ({ children }: { children: React.ReactNode { accessorFn: (row) => row.name, id: 'name', - header: ({ column }) => , + header: ({ column }) => , enableSorting: true, enableHiding: false, meta: { @@ -90,7 +90,7 @@ const ManageAldeiasContextProvider = ({ children }: { children: React.ReactNode { accessorFn: (row) => row.sucos.name, id: 'sucos_name', - header: ({ column }) => , + header: ({ column }) => , enableSorting: true, enableHiding: false, meta: { diff --git a/src/pages/master/currency/CurrencyMaster.tsx b/src/pages/master/currency/CurrencyMaster.tsx index be439cd..17772d7 100644 --- a/src/pages/master/currency/CurrencyMaster.tsx +++ b/src/pages/master/currency/CurrencyMaster.tsx @@ -5,39 +5,43 @@ import { Delete } from 'lucide-react'; import AddDialog from './blocks/AddDialog'; import DeleteDialog from './blocks/DeleteDialog'; import EditDialog from './blocks/EditDialog'; - -// import EditDialog from './blocks/EditDialog'; +import { Helmet } from 'react-helmet'; const CurrencyMaster = () => { return ( - - -

Currency

- - - Dashboard - + <> + + TPAY | Manage Currency + + + +

Currency

+ + + Dashboard + - - Master Data - + + Master Data + - - Manage Currency - - + + Manage Currency + +
-
- -
+
+ +
- - - - {/* + + + + {/* */} -
-
+ + + ); }; diff --git a/src/pages/master/municipios/Municipios.tsx b/src/pages/master/municipios/Municipios.tsx index cc82b5c..253ff86 100644 --- a/src/pages/master/municipios/Municipios.tsx +++ b/src/pages/master/municipios/Municipios.tsx @@ -11,11 +11,11 @@ const Municipios = () => { return ( <> - TPAY | Municipios + TPAY | Municipio -

MUNICIPIOS

+

MUNICIPIO

Dashboard @@ -26,7 +26,7 @@ const Municipios = () => { - Manage Municipios + Manage Municipio
diff --git a/src/pages/master/municipios/blocks/AddDialog.tsx b/src/pages/master/municipios/blocks/AddDialog.tsx index 824645b..2b88edc 100644 --- a/src/pages/master/municipios/blocks/AddDialog.tsx +++ b/src/pages/master/municipios/blocks/AddDialog.tsx @@ -56,7 +56,7 @@ const AddDialog = () => { reload(); toast.success('Municipio created successfully!'); const createActivity = { - module: 'Manage Municipios', + module: 'Manage Municipio', description: `Create Municipio => ${formField.name}`, action: 'C' }; @@ -107,52 +107,36 @@ const AddDialog = () => { return ( handleAddDialog(open)}> - - - - -
-
-

Add Municipios

-
-
-
{ - handleAddDialog(false); - resetForm(); - }} - > - -
-
+ + + Municipio - Create + - -
+ +
{alert.show && ( - - {alert.message} + +

{alert.message}

)} -
-
-
- - - setFormField((prev) => ({ ...prev, name: target.value })) - } - /> + +
+
+
+ + setFormField({ ...formField, name: e.target.value })} + /> +
-
+
diff --git a/src/pages/master/municipios/blocks/DeleteDialog.tsx b/src/pages/master/municipios/blocks/DeleteDialog.tsx index edab547..08a0551 100644 --- a/src/pages/master/municipios/blocks/DeleteDialog.tsx +++ b/src/pages/master/municipios/blocks/DeleteDialog.tsx @@ -43,7 +43,7 @@ const DeleteDialog = () => { reload(); const createActivity = { - module: 'Manage Municipios', + module: 'Manage Municipio', description: `Delete Municipio => ${selectedMunicipios}`, action: 'D' }; diff --git a/src/pages/master/municipios/blocks/EditDialog.tsx b/src/pages/master/municipios/blocks/EditDialog.tsx index e7365c5..6b6724a 100644 --- a/src/pages/master/municipios/blocks/EditDialog.tsx +++ b/src/pages/master/municipios/blocks/EditDialog.tsx @@ -21,7 +21,6 @@ import { doSaveLogActivity } from '@/actions/GlobalActions'; const API_URL = apiConfig.service_master_data; const EditDialog = () => { - const parentRef = useRef(null); const { showEditDialog, handleEditDialog, selectedMunicipios, municipios } = useManageMunicipiosContext(); const { reload } = useDataGrid(); @@ -131,7 +130,7 @@ const EditDialog = () => { handleEditDialog(open, null)}> - Municipios - Update + Municipio - Update @@ -147,7 +146,7 @@ const EditDialog = () => {
{
-
+
diff --git a/src/pages/master/municipios/blocks/ListToolbar.tsx b/src/pages/master/municipios/blocks/ListToolbar.tsx index 4c5a633..4ebdf62 100644 --- a/src/pages/master/municipios/blocks/ListToolbar.tsx +++ b/src/pages/master/municipios/blocks/ListToolbar.tsx @@ -28,7 +28,7 @@ const ListToolbar = () => { table.getColumn('name')?.setFilterValue(event.target.value)} /> diff --git a/src/pages/master/municipios/hooks/ManageMunicipiosContext.tsx b/src/pages/master/municipios/hooks/ManageMunicipiosContext.tsx index 5a95477..3fcb85e 100644 --- a/src/pages/master/municipios/hooks/ManageMunicipiosContext.tsx +++ b/src/pages/master/municipios/hooks/ManageMunicipiosContext.tsx @@ -75,7 +75,6 @@ const ManageMunicipiosProvider = ({ children }: { children: React.ReactNode }) = const [selectedMunicipios, setSelectedMunicipios] = useState(null); const [municipios, setMunicipios] = useState([]); const { GetData } = useCallApi(); - const navigate = useNavigate(); const handleSearchDialog = useCallback((show: boolean) => { setShowSearchDialog(show); @@ -95,18 +94,13 @@ const ManageMunicipiosProvider = ({ children }: { children: React.ReactNode }) = setShowDeleteDialog(show); }, []); - const handleNavigate = (path: string) => { - const url = navigate(`${API_URL}/municipios/postoadms/${path}`); - console.log(url); - }; - const columns = useMemo[]>( () => [ { // accessorFn: (row) => row.name, // id: 'name', accessorKey: 'name', - header: ({ column }) => , + header: ({ column }) => , enableSorting: true, enableHiding: false, meta: { @@ -158,12 +152,6 @@ const ManageMunicipiosProvider = ({ children }: { children: React.ReactNode }) = order_direction: sorting[0].desc == false ? 'ASC' : 'DESC', filter: JSON.stringify(filter) }); - // console.log(response?.data); - // const sortedList = response.data.data.list.sort((a: MunicipiosProps, b: MunicipiosProps) => { - // if (a.name < b.name) return -1; - // if (a.name > b.name) return 1; - // return 0; - // }); setMunicipios(response?.data.list); return { data: response?.data.list, totalCount: response?.data.total_count }; } catch (error) { diff --git a/src/pages/master/postoadms/blocks/AddDialog.tsx b/src/pages/master/postoadms/blocks/AddDialog.tsx index a268b9e..6958a63 100644 --- a/src/pages/master/postoadms/blocks/AddDialog.tsx +++ b/src/pages/master/postoadms/blocks/AddDialog.tsx @@ -72,17 +72,20 @@ const AddDialog = () => { handleAddDialog(false); resetForm(); reload(); - toast.success('Posto Adm created successfully!'); + toast.success('Postu Administrativo created successfully!'); const createActivity = { module: 'Manage Posto Administrativo', - description: `Create PostoAdms => ${formField.name}`, + description: `Create Postu Administrativo => ${formField.name}`, action: 'C' }; doSaveLogActivity(createActivity); } else { - toast.error('Failed to create Posto Adm. Please try again.'); - setAlert({ show: true, message: 'Failed to create Posto Adm. Please try again.' }); + toast.error('Failed to create Postu Administrativo. Please try again.'); + setAlert({ + show: true, + message: 'Failed to create Postu Administrativo. Please try again.' + }); } }, [formField] @@ -102,7 +105,8 @@ const AddDialog = () => { // console.log(response?.data); setMunicipios(response?.data.list); } catch (error) { - console.error('Error fetching municipios', error); + console.error('Error fetching Municipio', error); + setAlert({ show: true, message: 'Failed to get Municipio. Please try again.' }); } }; @@ -159,7 +163,7 @@ const AddDialog = () => {
{ { onWheel={(e) => e.stopPropagation()} > - + No Municipio found. diff --git a/src/pages/master/postoadms/blocks/DeleteDialog.tsx b/src/pages/master/postoadms/blocks/DeleteDialog.tsx index b94301e..72dad0d 100644 --- a/src/pages/master/postoadms/blocks/DeleteDialog.tsx +++ b/src/pages/master/postoadms/blocks/DeleteDialog.tsx @@ -28,7 +28,7 @@ const DeleteDialog = () => { const doDeletePostoAdm = useCallback(async () => { if (!selectedPostoAdms) { - toast.error('No Posto Adm selected'); + toast.error('No Postu Administrativo selected'); return; } @@ -39,18 +39,18 @@ const DeleteDialog = () => { if (response?.status) { setAlert({ show: false, message: '' }); handleDeleteDialog(false, null); - toast.success('Success Delete Posto Adm'); + toast.success('Success Delete Postu Administrativo'); reload(); const createActivity = { - module: 'Manage Posto Administrativo', - description: `Delete PostoAdms => ${selectedPostoAdms}`, + module: 'Manage Postu Administrativo', + description: `Delete Postu Administrativo => ${selectedPostoAdms}`, action: 'D' }; doSaveLogActivity(createActivity); } else { setAlert({ show: true, message: response?.message }); - toast.error('Failed Delete Posto Adm'); + toast.error('Failed Delete Postu Administrativo'); } }, [selectedPostoAdms, DeleteData, handleDeleteDialog, reload]); diff --git a/src/pages/master/postoadms/blocks/EditDialog.tsx b/src/pages/master/postoadms/blocks/EditDialog.tsx index 532403b..fd68b10 100644 --- a/src/pages/master/postoadms/blocks/EditDialog.tsx +++ b/src/pages/master/postoadms/blocks/EditDialog.tsx @@ -71,19 +71,22 @@ const EditDialog = () => { if (response?.status) { handleEditDialog(false, null); resetForm(); - toast.success('Success Update Posto Adm'); + toast.success('Success Update Postu Administrativo'); reload(); const createActivity = { - module: 'Manage Posto Administrativo', - description: `Edit PostoAdms => ${selectedPostoAdms}`, + module: 'Manage Postu Administrativo', + description: `Edit Postu Administrativo => ${selectedPostoAdms}`, action: 'U' }; doSaveLogActivity(createActivity); } else { - toast.error('Error Update Posto Adm'); - setAlert({ show: true, message: 'Failed to update posto adm. Please try again.' }); + toast.error('Error Update Postu Administrativo'); + setAlert({ + show: true, + message: 'Failed to update Postu Administrativo Please try again.' + }); } }, [selectedPostoAdms, formField] @@ -103,7 +106,8 @@ const EditDialog = () => { // console.log('MUNICIPIOS: ', response?.data); setMunicipios(response?.data.list); } catch (error) { - console.error('Error fetching municipios', error); + console.error('Error fetching Municipio', error); + setAlert({ show: true, message: 'Failed to get Municipio. Please try again.' }); } }, []); @@ -169,7 +173,7 @@ const EditDialog = () => { handleEditDialog(open, null)}> - Posto Adm - Update + Postu Administrativo - Update @@ -185,7 +189,7 @@ const EditDialog = () => {
{ - + No Municipio found. diff --git a/src/pages/master/postoadms/blocks/ListToolbar.tsx b/src/pages/master/postoadms/blocks/ListToolbar.tsx index 04742a3..37b3976 100644 --- a/src/pages/master/postoadms/blocks/ListToolbar.tsx +++ b/src/pages/master/postoadms/blocks/ListToolbar.tsx @@ -16,7 +16,7 @@ const ListToolbar = () => { table.getColumn('name')?.setFilterValue(event.target.value)} /> diff --git a/src/pages/master/postoadms/hooks/ManagePostoAdmsContext.tsx b/src/pages/master/postoadms/hooks/ManagePostoAdmsContext.tsx index ef6f7b5..fe31218 100644 --- a/src/pages/master/postoadms/hooks/ManagePostoAdmsContext.tsx +++ b/src/pages/master/postoadms/hooks/ManagePostoAdmsContext.tsx @@ -88,7 +88,7 @@ const ManagePostoAdmsContextProvider = ({ children }: { children: React.ReactNod id: 'name', // accessorKey: 'PostoAdms_name', header: ({ column }) => ( - + ), enableSorting: true, enableHiding: false, @@ -99,7 +99,7 @@ const ManagePostoAdmsContextProvider = ({ children }: { children: React.ReactNod { accessorFn: (row) => row.municipios_name, id: 'municipios_name', - header: ({ column }) => , + header: ({ column }) => , enableSorting: false, enableHiding: false, meta: { diff --git a/src/pages/master/products/blocks/AddDialog.tsx b/src/pages/master/products/blocks/AddDialog.tsx index 0690b4b..6c58950 100644 --- a/src/pages/master/products/blocks/AddDialog.tsx +++ b/src/pages/master/products/blocks/AddDialog.tsx @@ -23,6 +23,7 @@ import { SelectValue } from '@/components/ui/select'; import { doSaveLogActivity } from '@/actions/GlobalActions'; +import { NumericFormat } from 'react-number-format'; interface ProviderProps { provider_id: number; @@ -40,15 +41,29 @@ const AddDialog = () => { show: false, message: '' }); - const initialState = { + const initialState: { + name: string; + code: string; + type: string; + description: string; + price_point: string | number | null; + price_cash: string | number | null; + cashback_point: string | number | null; + cashback_cash: string | number | null; + status: string; + provider: string; + process_on_third_party: string; + created_by: string; + created_at: string; + } = { name: '', code: '', type: '', description: '', - price_point: 0, - price_cash: 0, - cashback_point: 0, - cashback_cash: 0, + price_point: null, + price_cash: null, + cashback_point: null, + cashback_cash: null, status: '', provider: '', process_on_third_party: '', @@ -117,11 +132,13 @@ const AddDialog = () => { formField.type.trim() === '' || formField.code.trim() === '' || formField.description.trim() === '' || - formField.price_point === 0 || - formField.price_cash === 0 || - formField.cashback_point === 0 || - formField.cashback_cash === 0 || + formField.price_point === null || + formField.price_cash === null || + formField.cashback_point === null || + formField.cashback_cash === null || formField.status.trim() === '' || + formField.provider.trim() === '' || + formField.process_on_third_party.trim() === '' || formField.created_by.trim() === '' || formField.created_at.trim() === '' ) { @@ -130,7 +147,7 @@ const AddDialog = () => { } doCreateProduct(e); - console.log(formField); + // console.log(formField); setAlert({ show: false, message: '' }); }; @@ -234,19 +251,19 @@ const AddDialog = () => { - { - const value = parseFloat(e.target.value); - setFormField({ - ...formField, - price_point: isNaN(value) ? 0 : value - }); + value={formField.price_point ?? ''} + thousandSeparator="." + decimalSeparator="," + allowNegative={false} + onValueChange={(values) => { + setFormField((prev) => ({ + ...prev, + price_point: values.floatValue !== undefined ? values.floatValue : '' + })); }} + placeholder="Enter Price Point" />
@@ -256,19 +273,19 @@ const AddDialog = () => { - { - const value = parseFloat(e.target.value); - setFormField({ - ...formField, - price_cash: isNaN(value) ? 0 : value - }); + value={formField.price_cash ?? ''} + thousandSeparator="." + decimalSeparator="," + allowNegative={false} + onValueChange={(values) => { + setFormField((prev) => ({ + ...prev, + price_cash: values.floatValue !== undefined ? values.floatValue : '' + })); }} + placeholder="Enter Price Cash" />
@@ -278,19 +295,19 @@ const AddDialog = () => { - { - const value = parseFloat(e.target.value); - setFormField({ - ...formField, - cashback_point: isNaN(value) ? 0 : value - }); + value={formField.cashback_point ?? ''} + thousandSeparator="." + decimalSeparator="," + allowNegative={false} + onValueChange={(values) => { + setFormField((prev) => ({ + ...prev, + cashback_point: values.floatValue !== undefined ? values.floatValue : '' + })); }} + placeholder="Enter Cashback Point" />
@@ -300,19 +317,19 @@ const AddDialog = () => { - { - const value = parseFloat(e.target.value); - setFormField({ - ...formField, - cashback_cash: isNaN(value) ? 0 : value - }); + value={formField.cashback_cash ?? ''} + thousandSeparator="." + decimalSeparator="," + allowNegative={false} + onValueChange={(values) => { + setFormField((prev) => ({ + ...prev, + cashback_cash: values.floatValue !== undefined ? values.floatValue : '' + })); }} + placeholder="Enter Cashback Cash" />
diff --git a/src/pages/master/products/blocks/EditDialog.tsx b/src/pages/master/products/blocks/EditDialog.tsx index 66c809d..493489d 100644 --- a/src/pages/master/products/blocks/EditDialog.tsx +++ b/src/pages/master/products/blocks/EditDialog.tsx @@ -23,6 +23,7 @@ import { SelectValue } from '@/components/ui/select'; import { doSaveLogActivity } from '@/actions/GlobalActions'; +import { NumericFormat } from 'react-number-format'; interface ProviderProps { provider_id: string; @@ -41,15 +42,29 @@ const EditDialog = () => { show: false, message: '' }); - const initialState = { + const initialState: { + name: string; + code: string; + type: string; + description: string; + price_point: string | number | null; + price_cash: string | number | null; + cashback_point: string | number | null; + cashback_cash: string | number | null; + status: string; + provider: string; + process_on_third_party: string; + updated_by: string; + updated_at: string; + } = { name: '', code: '', type: '', description: '', - price_point: 0, - price_cash: 0, - cashback_point: 0, - cashback_cash: 0, + price_point: null, + price_cash: null, + cashback_point: null, + cashback_cash: null, status: '', provider: '', process_on_third_party: '', @@ -109,22 +124,22 @@ const EditDialog = () => { const doFetchData = useCallback(async (id: string) => { const response = await GetData(`${API_URL}/product/getdata/${id}`, { id }); - console.log(response); + // console.log(response); if (response?.status) { setFormField((prev) => ({ ...prev, - name: response.data.name, - type: response.data.type, - code: response.data.code, - description: response.data.description, - price_point: response.data.price_point, - price_cash: response.data.price_cash, - cashback_point: response.data.cashback_point, - cashback_cash: response.data.cashback_cash, - status: response.data.status, - provider: response.data.provider.id, - process_on_third_party: response.data.process_on_third_party + name: response.data?.name, + type: response.data?.type, + code: response.data?.code, + description: response.data?.description, + price_point: response.data?.price_point, + price_cash: response.data?.price_cash, + cashback_point: response.data?.cashback_point, + cashback_cash: response.data?.cashback_cash, + status: response.data?.status, + provider: response.data?.provider?.id, + process_on_third_party: response.data?.process_on_third_party })); } else { setFormField(initialState); @@ -139,11 +154,13 @@ const EditDialog = () => { formField.type.trim() === '' || formField.code.trim() === '' || formField.description.trim() === '' || - formField.price_point === 0 || - formField.price_cash === 0 || - formField.cashback_point === 0 || - formField.cashback_cash === 0 || - formField.status === '' || + formField.price_point === null || + formField.price_cash === null || + formField.cashback_point === null || + formField.cashback_cash === null || + formField.status.trim() === '' || + formField.provider.trim() === '' || + formField.process_on_third_party.trim() === '' || formField.updated_by.trim() === '' || formField.updated_at.trim() === '' ) { @@ -152,7 +169,7 @@ const EditDialog = () => { } doUpdateProduct(e); - console.log(formField); + // console.log(formField); setAlert({ show: false, message: '' }); }; @@ -260,16 +277,19 @@ const EditDialog = () => { - { - const value = parseFloat(e.target.value); - setFormField({ ...formField, price_point: isNaN(value) ? 0 : value }); + value={formField.price_point ?? ''} + thousandSeparator="." + decimalSeparator="," + allowNegative={false} + onValueChange={(values) => { + setFormField((prev) => ({ + ...prev, + price_point: values.floatValue !== undefined ? values.floatValue : '' + })); }} + placeholder="Enter Price Point" />
@@ -279,16 +299,19 @@ const EditDialog = () => { - { - const value = parseFloat(e.target.value); - setFormField({ ...formField, price_cash: isNaN(value) ? 0 : value }); + value={formField.price_cash ?? ''} + thousandSeparator="." + decimalSeparator="," + allowNegative={false} + onValueChange={(values) => { + setFormField((prev) => ({ + ...prev, + price_cash: values.floatValue !== undefined ? values.floatValue : '' + })); }} + placeholder="Enter Price Cash" />
@@ -298,16 +321,19 @@ const EditDialog = () => { - { - const value = parseFloat(e.target.value); - setFormField({ ...formField, cashback_point: isNaN(value) ? 0 : value }); + value={formField.cashback_point ?? ''} + thousandSeparator="." + decimalSeparator="," + allowNegative={false} + onValueChange={(values) => { + setFormField((prev) => ({ + ...prev, + cashback_point: values.floatValue !== undefined ? values.floatValue : '' + })); }} + placeholder="Enter Cashback Point" />
@@ -317,16 +343,19 @@ const EditDialog = () => { - { - const value = parseFloat(e.target.value); - setFormField({ ...formField, cashback_cash: isNaN(value) ? 0 : value }); + value={formField.cashback_cash ?? ''} + thousandSeparator="." + decimalSeparator="," + allowNegative={false} + onValueChange={(values) => { + setFormField((prev) => ({ + ...prev, + cashback_cash: values.floatValue !== undefined ? values.floatValue : '' + })); }} + placeholder="Enter Cashback Cash" />
diff --git a/src/pages/master/products/hooks/ManageProductsContext.tsx b/src/pages/master/products/hooks/ManageProductsContext.tsx index 541c710..236e306 100644 --- a/src/pages/master/products/hooks/ManageProductsContext.tsx +++ b/src/pages/master/products/hooks/ManageProductsContext.tsx @@ -72,6 +72,19 @@ const ManageProductsContextProvider = ({ children }: { children: React.ReactNode const columns = useMemo[]>( () => [ + { + accessorFn: (row) => row.code, + id: 'code', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { + headerClassName: 'w-[100px]' + }, + filterFn: (row, id, value) => { + return row.original.products_name.toLowerCase().includes(value.toLowerCase()); + } + }, { accessorFn: (row) => row.name, id: 'name', @@ -89,12 +102,32 @@ const ManageProductsContextProvider = ({ children }: { children: React.ReactNode accessorFn: (row) => row.description, id: 'description', header: ({ column }) => , - enableSorting: true, + enableSorting: false, enableHiding: false, meta: { headerClassName: 'w-[250px]' } }, + { + accessorFn: (row) => row.provider?.name, + id: 'provider_name', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + accessorFn: (row) => row.type, + id: 'type', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { + headerClassName: 'w-[150px]' + } + }, { accessorFn: (row) => row.price_point, id: 'price_point', @@ -115,6 +148,64 @@ const ManageProductsContextProvider = ({ children }: { children: React.ReactNode headerClassName: 'w-[100px]' } }, + { + accessorFn: (row) => row.cashback_point, + id: 'cashback_point', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[100px]' + } + }, + { + accessorFn: (row) => row.cashback_cash, + id: 'cashback_cash', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[100px]' + } + }, + { + accessorFn: (row) => row.process_on_third_party, + id: 'process_on_third_party', + header: ({ column }) => , + cell: ({ row }) => { + const isActive = row.original.process_on_third_party === 'Y'; + + return {isActive ? 'Yes' : 'No'}; + }, + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[100px]' + } + }, + { + accessorFn: (row) => row.status, + id: 'status', + header: ({ column }) => , + cell: ({ row }) => { + const isActive = row.original.status === 'Y'; + + return ( + + {isActive ? 'Active' : 'Inactive'} + + ); + }, + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[100px]' + } + }, { id: 'actions', header: ({ column }) => , @@ -160,7 +251,7 @@ const ManageProductsContextProvider = ({ children }: { children: React.ReactNode order_direction: sorting[0].desc == false ? 'ASC' : 'DESC', filter: JSON.stringify(filter) }); - // console.log(response?.data); + console.log(response?.data); setProducts(response?.data.list); return { data: response?.data.list, totalCount: response?.data.total_count }; } catch (error) { @@ -189,7 +280,7 @@ const ManageProductsContextProvider = ({ children }: { children: React.ReactNode pagination={{ size: 10 }} toolbar={} layout={{ card: true }} - sorting={[{ id: 'Product.id', desc: false }]} + sorting={[{ id: 'Product.created_at', desc: true }]} serverSide={true} onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => getProductsLists(pageIndex, pageSize, sorting, columnFilters) diff --git a/src/pages/master/provider/blocks/AddDialog.tsx b/src/pages/master/provider/blocks/AddDialog.tsx index 84bc992..5872eb4 100644 --- a/src/pages/master/provider/blocks/AddDialog.tsx +++ b/src/pages/master/provider/blocks/AddDialog.tsx @@ -61,16 +61,27 @@ const AddDialog = () => { show: false, message: '' }); - const initialState = { + + const initialState: { + name: string; + description: string; + type: string; + status: string; + transaction_type: string; + agent: string | null; + created_by: string; + created_at: string; + } = { name: '', description: '', type: '', status: '', transaction_type: '', - agent: '', + agent: null, created_by: '', created_at: '' }; + const [formField, setFormField] = useState(initialState); const [customers, setCustomers] = useState([]); const [transactions, setTransactions] = useState([]); @@ -116,14 +127,13 @@ const AddDialog = () => { formField.description.trim() === '' || formField.type.trim() === '' || formField.status.trim() === '' || - formField.transaction_type === '' || - formField.agent.trim() === '' + formField.transaction_type === '' ) { setAlert({ show: true, message: 'Please fill in all required fields.' }); return; } - console.log(formField); + // console.log(formField); doCreateProvider(e); setAlert({ show: false, message: '' }); }; @@ -242,7 +252,7 @@ const AddDialog = () => { - H2H + Host to Host Agent @@ -294,56 +304,67 @@ const AddDialog = () => { -
-
- - - - - - - - - { - e.currentTarget.scrollTop += e.deltaY; - }} + {formField.type === 'agent' ? ( +
+
+ + + + + + + + + { + e.currentTarget.scrollTop += e.deltaY; + }} + > + No Agent found. + + {customers.map((customer) => ( + { + setFormField({ + ...formField, + agent: customer.id + }); + setOpen(false); + }} + > + {customer.username} + + ))} + + + + + +
-
+ ) : ( +
+
+ + +
+
+ )}
-
-
- - - - - - - - - { - e.currentTarget.scrollTop += e.deltaY; - }} + {formField.type === 'agent' ? ( +
+
+ + + + + + + + + { + e.currentTarget.scrollTop += e.deltaY; + }} + > + No Agent found. + + {customers.map((customer) => ( + { + setFormField({ + ...formField, + agent: customer.id + }); + setOpen(false); + }} + > + {customer.username} + + ))} + + + + + +
-
+ ) : ( +
+
+ + +
+
+ )}
diff --git a/src/pages/master/provider/blocks/ListToolbar.tsx b/src/pages/master/provider/blocks/ListToolbar.tsx index 83588b5..6915202 100644 --- a/src/pages/master/provider/blocks/ListToolbar.tsx +++ b/src/pages/master/provider/blocks/ListToolbar.tsx @@ -16,9 +16,9 @@ const ListToolbar = () => { - table.getColumn('provider_name')?.setFilterValue(event.target.value) + table.getColumn('name')?.setFilterValue(event.target.value) } /> diff --git a/src/pages/master/provider/hooks/ManageProviderContext.tsx b/src/pages/master/provider/hooks/ManageProviderContext.tsx index 2cfe75f..0df1c51 100644 --- a/src/pages/master/provider/hooks/ManageProviderContext.tsx +++ b/src/pages/master/provider/hooks/ManageProviderContext.tsx @@ -75,7 +75,7 @@ const ManageProviderContextProvider = ({ children }: { children: React.ReactNode () => [ { accessorFn: (row) => row.provider_name, - id: 'provider_name', + id: 'name', header: ({ column }) => , enableSorting: true, enableHiding: false, @@ -94,7 +94,13 @@ const ManageProviderContextProvider = ({ children }: { children: React.ReactNode } }, { - accessorFn: (row) => row.provider_type, + accessorFn: (row) => { + const typeMapping: Record = { + h2h: 'Host To Host', + agent: 'Agent' + }; + return typeMapping[row.provider_type] || row.provider_type; + }, id: 'type', header: ({ column }) => , enableSorting: false, @@ -107,7 +113,7 @@ const ManageProviderContextProvider = ({ children }: { children: React.ReactNode accessorFn: (row) => row.provider_status, id: 'status', header: ({ column }) => , - enableSorting: false, + enableSorting: true, enableHiding: false, cell: ({ row }) => { const isActive = row.original.provider_status === 'Y'; @@ -203,7 +209,7 @@ const ManageProviderContextProvider = ({ children }: { children: React.ReactNode pagination={{ size: 10 }} toolbar={} layout={{ card: true }} - sorting={[{ id: 'id', desc: false }]} + sorting={[{ id: 'created_at', desc: true }]} serverSide={true} onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => getProviderLists(pageIndex, pageSize, sorting, columnFilters) diff --git a/src/pages/master/reward/Reward.tsx b/src/pages/master/reward/Reward.tsx deleted file mode 100644 index 32e8691..0000000 --- a/src/pages/master/reward/Reward.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import AddDialog from './blocks/AddDialog'; -import EditDialog from './blocks/EditDialog'; -import DeleteDialog from './blocks/DeleteDialog'; -import { ManageRewardContextProvider } from './hooks/ManageRewardContext'; -import { Container, DataGridInner } from '@/components'; -import { Breadcrumbs, Link } from '@mui/material'; - -const RewardMaster = () => { - return ( - - -

Manage Reward

- - - Dashboard - - - - Master Data - - - - Manage Reward - - -
- -
- - - -
-
- ); -}; - -export default RewardMaster; diff --git a/src/pages/master/reward/RewardMaster.tsx b/src/pages/master/reward/RewardMaster.tsx new file mode 100644 index 0000000..f9b1ddd --- /dev/null +++ b/src/pages/master/reward/RewardMaster.tsx @@ -0,0 +1,43 @@ +import AddDialog from './blocks/AddDialog'; +import EditDialog from './blocks/EditDialog'; +import DeleteDialog from './blocks/DeleteDialog'; +import { ManageRewardContextProvider } from './hooks/ManageRewardContext'; +import { Container, DataGridInner } from '@/components'; +import { Breadcrumbs, Link } from '@mui/material'; +import { Helmet } from 'react-helmet'; + +const RewardMaster = () => { + return ( + <> + + TPAY | Manage Reward + + + +

Reward

+ + + Dashboard + + + + Master Data + + + + Manage Reward + + +
+ +
+ + + +
+
+ + ); +}; + +export default RewardMaster; diff --git a/src/pages/master/reward/blocks/AddDialog.tsx b/src/pages/master/reward/blocks/AddDialog.tsx index 40df444..6e32603 100644 --- a/src/pages/master/reward/blocks/AddDialog.tsx +++ b/src/pages/master/reward/blocks/AddDialog.tsx @@ -14,6 +14,7 @@ import { apiConfig } from '@/config/api.config'; import { toast } from 'sonner'; import { getAuth } from '@/auth'; import { useCallApi } from '@/hooks'; +import { NumericFormat } from 'react-number-format'; import { useManageRewardContext } from '../hooks/useManageRewardContext'; import { Select, @@ -54,10 +55,19 @@ const AddDialog = () => { setAlert({ show: false, message: '' }); }; + const RewardType = { + 'Daily Check in': 'D', + Referal: 'R', + 'Level Pro': 'P', + 'Level Prioritas': 'L' + } as const; + + type RewardTypeValue = (typeof RewardType)[keyof typeof RewardType]; + const doCreateReward = useCallback( async (e: React.FormEvent) => { e.preventDefault(); - console.log('Data yang akan dikirim:', formField); + // console.log('Data yang akan dikirim:', formField); const response = await PostData(`${API_URL}/reward/create`, formField); if (response?.status) { @@ -91,20 +101,15 @@ const AddDialog = () => { setAlert({ show: false, message: '' }); }; - const handleReset = () => { - resetForm(); - setAlert({ show: false, message: '' }); - }; - useEffect(() => { if (showAddDialog) { - setFormField({ - ...formField, + setFormField((prev) => ({ + ...prev, created_by: parsedUser?.username, created_at: formattedTime - }); + })); } - }, [formattedTime]); + }, [showAddDialog, parsedUser?.username, formattedTime]); useEffect(() => { if (showAddDialog === false) { @@ -144,34 +149,44 @@ const AddDialog = () => { Type* - - setFormField((prev) => ({ ...prev, type: target.value })) - } - /> +
+ +
- { - const value = parseFloat(e.target.value); - setFormField({ - ...formField, - amount: isNaN(value) ? 0 : value - }); + thousandSeparator="." + decimalSeparator="," + allowNegative={false} + onValueChange={(values) => { + setFormField((prev) => ({ + ...prev, + amount: values.floatValue || 0 + })); }} + placeholder="Enter Amount" />
diff --git a/src/pages/master/reward/blocks/DeleteDialog.tsx b/src/pages/master/reward/blocks/DeleteDialog.tsx index dd7639c..70aaf5e 100644 --- a/src/pages/master/reward/blocks/DeleteDialog.tsx +++ b/src/pages/master/reward/blocks/DeleteDialog.tsx @@ -25,13 +25,16 @@ const DeleteDialog = () => { message: '' }); + // console.log('ini data :', selectedReward); + const doDeleteReward = useCallback(async () => { if (!selectedReward) { toast.error('No Reward selected'); return; } - const response = await DeleteData(`${API_URL}/reward/delete/${selectedReward}`, { - id: selectedReward + // console.log('Ini datanya:', selectedReward); + const response = await DeleteData(`${API_URL}/reward/delete/${selectedReward?.id}/true`, { + id: selectedReward.id }); if (response?.status) { diff --git a/src/pages/master/reward/blocks/EditDialog.tsx b/src/pages/master/reward/blocks/EditDialog.tsx index cb3695a..ac71043 100644 --- a/src/pages/master/reward/blocks/EditDialog.tsx +++ b/src/pages/master/reward/blocks/EditDialog.tsx @@ -14,6 +14,7 @@ import { } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; +import { NumericFormat } from 'react-number-format'; import { Select, SelectContent, @@ -54,11 +55,19 @@ const EditDialog = () => { setAlert({ show: false, message: '' }); }; + const RewardType = { + 'Daily Check in': 'D', + Referal: 'R' + } as const; + + type RewardTypeValue = (typeof RewardType)[keyof typeof RewardType]; + const doUpdateReward = useCallback( async (e: React.FormEvent) => { e.preventDefault(); - const response = await PutData(`${API_URL}/reward/update/${selectedReward}`, formField); + // console.log('Ini datanya:', selectedReward); + const response = await PutData(`${API_URL}/reward/update/${selectedReward?.id}`, formField); if (response?.status) { resetForm(); @@ -74,16 +83,17 @@ const EditDialog = () => { ); const doFetchData = useCallback(async (id: string) => { + // console.log('Ini datanya:', id); const response = await GetData(`${API_URL}/reward/getdata/${id}`, { id }); - console.log('API Response:', response); + // console.log('API Response:', response); if (response?.status) { setFormField((prev) => ({ ...prev, - name: response?.data.name, - type: response?.data.type, - amount: response?.data.amount, - status: response?.data.status + name: response.data.name, + type: response.data.type, + amount: response.data.amount, + status: response.data.status })); } }, []); @@ -107,21 +117,20 @@ const EditDialog = () => { // }; useEffect(() => { - // console.log('Selected Reward:', selectedReward); if (selectedReward) { - doFetchData(selectedReward); + doFetchData(selectedReward.id.toString()); } }, [selectedReward]); useEffect(() => { - if (showEditDialog) { - setFormField({ - ...formField, + if (showEditDialog && selectedReward) { + setFormField((prev) => ({ + ...prev, updated_by: parsedUser.username, updated_at: formattedTime - }); + })); } - }, [formattedTime]); + }, [showEditDialog, selectedReward]); useEffect(() => { if (showEditDialog === false) { @@ -142,67 +151,84 @@ const EditDialog = () => {
-
-