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
+1
View File
@@ -5,3 +5,4 @@ VITE_REFRESH_TOKEN_NAME = 'dmnu-a-rt'
VITE_BASE_URL = 'http://192.168.99.131:2000'
VITE_SOCKET_URL = 'https://dmenu-api.danakcorp.com'
VITE_INVOICE_URL = 'https://console.danakcorp.com/receipts/'
+9 -1
View File
@@ -611,6 +611,15 @@
"exsist_special_character": "شامل کاراکتر های خاص (نماد ها) باشد"
},
"wallet": {
"charge": "شارژ",
"charge_title": "شارژ کیف پول",
"amount": "مبلغ",
"amount_placeholder": "مبلغ را وارد کنید",
"amount_required": "مبلغ را وارد کنید",
"charge_success": "درخواست شارژ با موفقیت ثبت شد",
"invoice_issued": "صورتحساب صادر شد",
"cancel": "انصراف",
"balance": "موجودی کیف پول",
"increese_wallet": "افزایش موجودی حساب اعتباری",
"online_pay": "پرداخت آنلاین",
"card_to_card": "کارت به کارت",
@@ -619,7 +628,6 @@
"enter_your_price": "مبلغ دلخواه خودرا وارد کنید",
"select_payment": "انتخاب درگاه پرداخت",
"pay": "پرداخت",
"amount": "مبلغ",
"enter_amount_card": "مبلغ کارت به کارت شده را وارد کنید",
"upload_deposit_slip": "آپلود فیش واریزی",
"submit_slip": "ثبت فیش",
@@ -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>;
+2 -3
View File
@@ -7,6 +7,7 @@ import { useSharedStore } from './store/sharedStore'
import { HambergerMenu } from 'iconsax-react'
import { clx } from '@/helpers/utils'
import Notifications from '@/pages/notification/Notification'
import WalletHeader from '@/pages/wallet/components/WalletHeader'
const Header: FC = () => {
@@ -43,9 +44,7 @@ const Header: FC = () => {
</div>
{/* <img src={LogoImage} className='h-6 xl:hidden block absolute right-0 left-0 mx-auto' /> */}
<div className='flex xl:gap-6 gap-4 items-center'>
{/* <Link className='xl:hidden' to={Pages.wallet}>
<Wallet className='xl:size-[18px] size-[17px]' color='black' />
</Link> */}
<WalletHeader />
<Notifications />
{/* {
data && (
+8
View File
@@ -1,5 +1,13 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_INVOICE_URL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
declare module "*.svg?raw" {
const content: string;
export default content;