wallet
This commit is contained in:
@@ -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
@@ -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;
|
||||
@@ -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>;
|
||||
@@ -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 && (
|
||||
|
||||
Vendored
+8
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user