wallet
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import { type FC, useState } from "react";
|
||||
import DefaulModal from "@/components/DefaulModal";
|
||||
import Input from "@/components/Input";
|
||||
import Button from "@/components/Button";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useCreateChargeRequest } from "../hooks/useWalletData";
|
||||
import { extractErrorMessage } from "@/config/func";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
type Props = {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const ChargeModal: FC<Props> = ({ open, onClose }) => {
|
||||
const { t } = useTranslation("global");
|
||||
const [amount, setAmount] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
const createChargeRequest = useCreateChargeRequest();
|
||||
|
||||
const handleClose = () => {
|
||||
setAmount("");
|
||||
setError("");
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
const numericAmount = Number(amount);
|
||||
|
||||
if (!numericAmount || numericAmount <= 0) {
|
||||
setError(t("wallet.amount_required"));
|
||||
return;
|
||||
}
|
||||
|
||||
createChargeRequest.mutate(
|
||||
{ amount: numericAmount },
|
||||
{
|
||||
onSuccess: (response) => {
|
||||
const { invoiceId } = response.data;
|
||||
const invoiceBaseUrl = import.meta.env.VITE_INVOICE_URL;
|
||||
|
||||
if (invoiceId && invoiceBaseUrl) {
|
||||
window.open(`${invoiceBaseUrl}${invoiceId}`, "_blank");
|
||||
}
|
||||
|
||||
toast.success(t("wallet.invoice_issued"));
|
||||
handleClose();
|
||||
},
|
||||
onError: (err) => {
|
||||
toast.error(extractErrorMessage(err));
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<DefaulModal
|
||||
open={open}
|
||||
close={handleClose}
|
||||
isHeader
|
||||
title_header={t("wallet.charge_title")}
|
||||
width={480}
|
||||
>
|
||||
<div className="mt-6">
|
||||
<Input
|
||||
name="amount"
|
||||
label={t("wallet.amount")}
|
||||
placeholder={t("wallet.amount_placeholder")}
|
||||
type="number"
|
||||
seprator
|
||||
value={amount}
|
||||
onChange={(e) => {
|
||||
setAmount(e.target.value);
|
||||
setError("");
|
||||
}}
|
||||
error_text={error}
|
||||
/>
|
||||
|
||||
<div className="flex gap-3 mt-6">
|
||||
<Button
|
||||
label={t("wallet.cancel")}
|
||||
type="button"
|
||||
onClick={handleClose}
|
||||
className="bg-gray-100 text-gray-700"
|
||||
/>
|
||||
<Button
|
||||
label={t("wallet.charge")}
|
||||
type="button"
|
||||
onClick={handleSubmit}
|
||||
isloading={createChargeRequest.isPending}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</DefaulModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChargeModal;
|
||||
@@ -0,0 +1,44 @@
|
||||
import { type FC, useState } from "react";
|
||||
import { EmptyWallet } from "iconsax-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { formatPrice } from "@/helpers/func";
|
||||
import { useGetWalletBalance } from "../hooks/useWalletData";
|
||||
import ChargeModal from "./ChargeModal";
|
||||
import MoonLoader from "react-spinners/MoonLoader";
|
||||
|
||||
const WalletHeader: FC = () => {
|
||||
const { t } = useTranslation("global");
|
||||
const [showChargeModal, setShowChargeModal] = useState(false);
|
||||
const { data, isLoading } = useGetWalletBalance();
|
||||
|
||||
const balance = data?.data?.balance ?? 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center gap-2 bg-[#F5F5F5] rounded-xl px-3 h-9">
|
||||
<EmptyWallet size={18} color="black" />
|
||||
<div className="text-xs whitespace-nowrap min-w-[80px]">
|
||||
{isLoading ? (
|
||||
<MoonLoader size={12} color="#000" />
|
||||
) : (
|
||||
formatPrice(balance)
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowChargeModal(true)}
|
||||
className="text-xs bg-primary text-white rounded-lg px-3 h-7 whitespace-nowrap hover:opacity-90 transition-opacity"
|
||||
>
|
||||
{t("wallet.charge")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ChargeModal
|
||||
open={showChargeModal}
|
||||
onClose={() => setShowChargeModal(false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default WalletHeader;
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import * as api from "../service/WalletService";
|
||||
|
||||
export const useGetWalletBalance = () => {
|
||||
return useQuery({
|
||||
queryKey: ["wallet-balance"],
|
||||
queryFn: () => api.getWalletBalance(),
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateChargeRequest = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: api.createChargeRequest,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["wallet-balance"] });
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import axios from "@/config/axios";
|
||||
import type {
|
||||
ChargeRequestResponse,
|
||||
ChargeRequestType,
|
||||
WalletBalanceResponse,
|
||||
} from "../types/Types";
|
||||
|
||||
export const getWalletBalance = async (): Promise<WalletBalanceResponse> => {
|
||||
const { data } = await axios.get<WalletBalanceResponse>(
|
||||
"/admin/restaurants/wallet/balance"
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const createChargeRequest = async (
|
||||
params: ChargeRequestType
|
||||
): Promise<ChargeRequestResponse> => {
|
||||
const { data } = await axios.post<ChargeRequestResponse>(
|
||||
"/admin/restaurants/charge-request",
|
||||
params
|
||||
);
|
||||
return data;
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { IResponse } from "@/types/response.types";
|
||||
|
||||
export type WalletBalance = {
|
||||
balance: number;
|
||||
};
|
||||
|
||||
export type WalletBalanceResponse = IResponse<WalletBalance>;
|
||||
|
||||
export type ChargeRequestType = {
|
||||
amount: number;
|
||||
};
|
||||
|
||||
export type ChargeRequestResult = {
|
||||
id: string;
|
||||
amount: number;
|
||||
invoiceId: string;
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type ChargeRequestResponse = IResponse<ChargeRequestResult>;
|
||||
Reference in New Issue
Block a user