diff --git a/.env b/.env index d0609b0..a309f21 100644 --- a/.env +++ b/.env @@ -4,4 +4,5 @@ VITE_REFRESH_TOKEN_NAME = 'dmnu-a-rt' # VITE_BASE_URL = 'https://dmenu-api.danakcorp.com' VITE_BASE_URL = 'http://192.168.99.131:2000' -VITE_SOCKET_URL = 'https://dmenu-api.danakcorp.com' \ No newline at end of file +VITE_SOCKET_URL = 'https://dmenu-api.danakcorp.com' +VITE_INVOICE_URL = 'https://console.danakcorp.com/receipts/' \ No newline at end of file diff --git a/src/langs/fa.json b/src/langs/fa.json index 4b2829b..38bd46b 100644 --- a/src/langs/fa.json +++ b/src/langs/fa.json @@ -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": "ثبت فیش", diff --git a/src/pages/wallet/components/ChargeModal.tsx b/src/pages/wallet/components/ChargeModal.tsx new file mode 100644 index 0000000..cd50f25 --- /dev/null +++ b/src/pages/wallet/components/ChargeModal.tsx @@ -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 = ({ 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 ( + +
+ { + setAmount(e.target.value); + setError(""); + }} + error_text={error} + /> + +
+
+
+
+ ); +}; + +export default ChargeModal; diff --git a/src/pages/wallet/components/WalletHeader.tsx b/src/pages/wallet/components/WalletHeader.tsx new file mode 100644 index 0000000..a47938f --- /dev/null +++ b/src/pages/wallet/components/WalletHeader.tsx @@ -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 ( + <> +
+ +
+ {isLoading ? ( + + ) : ( + formatPrice(balance) + )} +
+ +
+ + setShowChargeModal(false)} + /> + + ); +}; + +export default WalletHeader; diff --git a/src/pages/wallet/hooks/useWalletData.ts b/src/pages/wallet/hooks/useWalletData.ts new file mode 100644 index 0000000..098bd0a --- /dev/null +++ b/src/pages/wallet/hooks/useWalletData.ts @@ -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"] }); + }, + }); +}; diff --git a/src/pages/wallet/service/WalletService.ts b/src/pages/wallet/service/WalletService.ts new file mode 100644 index 0000000..93671fc --- /dev/null +++ b/src/pages/wallet/service/WalletService.ts @@ -0,0 +1,23 @@ +import axios from "@/config/axios"; +import type { + ChargeRequestResponse, + ChargeRequestType, + WalletBalanceResponse, +} from "../types/Types"; + +export const getWalletBalance = async (): Promise => { + const { data } = await axios.get( + "/admin/restaurants/wallet/balance" + ); + return data; +}; + +export const createChargeRequest = async ( + params: ChargeRequestType +): Promise => { + const { data } = await axios.post( + "/admin/restaurants/charge-request", + params + ); + return data; +}; diff --git a/src/pages/wallet/types/Types.ts b/src/pages/wallet/types/Types.ts new file mode 100644 index 0000000..e9c723b --- /dev/null +++ b/src/pages/wallet/types/Types.ts @@ -0,0 +1,20 @@ +import type { IResponse } from "@/types/response.types"; + +export type WalletBalance = { + balance: number; +}; + +export type WalletBalanceResponse = IResponse; + +export type ChargeRequestType = { + amount: number; +}; + +export type ChargeRequestResult = { + id: string; + amount: number; + invoiceId: string; + status: string; +}; + +export type ChargeRequestResponse = IResponse; diff --git a/src/shared/Header.tsx b/src/shared/Header.tsx index 5cd97bf..db07811 100644 --- a/src/shared/Header.tsx +++ b/src/shared/Header.tsx @@ -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 = () => { {/* */}
- {/* - - */} + {/* { data && ( diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 780ce93..c592426 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1,5 +1,13 @@ /// +interface ImportMetaEnv { + readonly VITE_INVOICE_URL: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} + declare module "*.svg?raw" { const content: string; export default content;