This commit is contained in:
hamid zarghami
2026-07-04 12:26:28 +03:30
parent 95814d6b00
commit 23f47705bb
9 changed files with 225 additions and 5 deletions
@@ -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;
+19
View File
@@ -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"] });
},
});
};
+23
View File
@@ -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;
};
+20
View File
@@ -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>;