Compare commits
3 Commits
95814d6b00
...
6a7d5523a7
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a7d5523a7 | |||
| 6a6aa76b1e | |||
| 23f47705bb |
@@ -1,7 +1,8 @@
|
|||||||
VITE_TOKEN_NAME = 'dmnu_a_t'
|
VITE_TOKEN_NAME = 'dmnu_a_t'
|
||||||
VITE_REFRESH_TOKEN_NAME = 'dmnu-a-rt'
|
VITE_REFRESH_TOKEN_NAME = 'dmnu-a-rt'
|
||||||
|
|
||||||
# VITE_BASE_URL = 'https://dmenu-api.danakcorp.com'
|
VITE_BASE_URL = 'https://dmenu-api.danakcorp.com'
|
||||||
VITE_BASE_URL = 'http://192.168.99.131:2000'
|
# VITE_BASE_URL = 'http://192.168.99.131:2000'
|
||||||
|
|
||||||
VITE_SOCKET_URL = 'https://dmenu-api.danakcorp.com'
|
VITE_SOCKET_URL = 'https://dmenu-api.danakcorp.com'
|
||||||
|
VITE_INVOICE_URL = 'https://console.danakcorp.com/receipts/'
|
||||||
+2
-2
@@ -66,13 +66,13 @@ server {
|
|||||||
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ ^/(foods|orders|customers|schedule|payment_methods|discounts|announcements|reports|comments|roles|admins|shipment_methods|coupons|reviews|pagers|notifications)/ {
|
location ~ ^/(foods|orders|customers|schedule|payment_methods|discounts|announcements|reports|comments|roles|admins|shipment_methods|coupons|reviews|pagers|notifications|wallet)/ {
|
||||||
try_files $uri $uri/ /index.html;
|
try_files $uri $uri/ /index.html;
|
||||||
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||||
}
|
}
|
||||||
|
|
||||||
# Restaurant menu preview (same-origin iframe; strips X-Frame-Options from upstream)
|
# Restaurant menu preview (same-origin iframe; strips X-Frame-Options from upstream)
|
||||||
location ~ ^/(?!auth|dashboard|setting|statistics|foods|orders|customers|schedule|payment_methods|discounts|announcements|reports|comments|roles|admins|shipment_methods|coupons|reviews|pagers|notifications|assets|icons|_next)([a-zA-Z0-9][a-zA-Z0-9_-]*)(/.*)?$ {
|
location ~ ^/(?!auth|dashboard|setting|statistics|foods|orders|customers|schedule|payment_methods|discounts|announcements|reports|comments|roles|admins|shipment_methods|coupons|reviews|pagers|notifications|wallet|assets|icons|_next)([a-zA-Z0-9][a-zA-Z0-9_-]*)(/.*)?$ {
|
||||||
proxy_pass https://dmenu.danakcorp.com/$1$2$is_args$args;
|
proxy_pass https://dmenu.danakcorp.com/$1$2$is_args$args;
|
||||||
proxy_ssl_server_name on;
|
proxy_ssl_server_name on;
|
||||||
proxy_set_header Host dmenu.danakcorp.com;
|
proxy_set_header Host dmenu.danakcorp.com;
|
||||||
|
|||||||
@@ -96,4 +96,7 @@ export const Pages = {
|
|||||||
statistics: {
|
statistics: {
|
||||||
list: "/statistics",
|
list: "/statistics",
|
||||||
},
|
},
|
||||||
|
wallet: {
|
||||||
|
transactions: "/wallet/transactions",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export const MENU_PREVIEW_RESERVED_SEGMENTS = new Set([
|
|||||||
"pagers",
|
"pagers",
|
||||||
"notifications",
|
"notifications",
|
||||||
"sliders",
|
"sliders",
|
||||||
|
"wallet",
|
||||||
"assets",
|
"assets",
|
||||||
"menu-preview",
|
"menu-preview",
|
||||||
]);
|
]);
|
||||||
|
|||||||
+24
-1
@@ -611,6 +611,30 @@
|
|||||||
"exsist_special_character": "شامل کاراکتر های خاص (نماد ها) باشد"
|
"exsist_special_character": "شامل کاراکتر های خاص (نماد ها) باشد"
|
||||||
},
|
},
|
||||||
"wallet": {
|
"wallet": {
|
||||||
|
"charge": "شارژ",
|
||||||
|
"charge_title": "شارژ کیف پول",
|
||||||
|
"amount": "مبلغ",
|
||||||
|
"amount_placeholder": "مبلغ را وارد کنید",
|
||||||
|
"amount_required": "مبلغ را وارد کنید",
|
||||||
|
"charge_success": "درخواست شارژ با موفقیت ثبت شد",
|
||||||
|
"invoice_issued": "صورتحساب صادر شد",
|
||||||
|
"cancel": "انصراف",
|
||||||
|
"balance": "موجودی کیف پول",
|
||||||
|
"balance_after": "موجودی پس از تراکنش",
|
||||||
|
"type_credit": "واریز",
|
||||||
|
"type_debit": "برداشت",
|
||||||
|
"reason": "دلیل",
|
||||||
|
"reason_deposit": "شارژ کیف پول",
|
||||||
|
"reason_sms_send": "ارسال پیامک",
|
||||||
|
"transactions_tab": "تراکنشها",
|
||||||
|
"charge_requests_tab": "درخواستهای شارژ",
|
||||||
|
"invoice": "صورتحساب",
|
||||||
|
"view_invoice": "مشاهده صورتحساب",
|
||||||
|
"charge_status_pending": "در انتظار پرداخت",
|
||||||
|
"charge_status_paid": "پرداخت شده",
|
||||||
|
"charge_status_success": "پرداخت موفق",
|
||||||
|
"charge_status_failed": "ناموفق",
|
||||||
|
"charge_status_cancelled": "لغو شده",
|
||||||
"increese_wallet": "افزایش موجودی حساب اعتباری",
|
"increese_wallet": "افزایش موجودی حساب اعتباری",
|
||||||
"online_pay": "پرداخت آنلاین",
|
"online_pay": "پرداخت آنلاین",
|
||||||
"card_to_card": "کارت به کارت",
|
"card_to_card": "کارت به کارت",
|
||||||
@@ -619,7 +643,6 @@
|
|||||||
"enter_your_price": "مبلغ دلخواه خودرا وارد کنید",
|
"enter_your_price": "مبلغ دلخواه خودرا وارد کنید",
|
||||||
"select_payment": "انتخاب درگاه پرداخت",
|
"select_payment": "انتخاب درگاه پرداخت",
|
||||||
"pay": "پرداخت",
|
"pay": "پرداخت",
|
||||||
"amount": "مبلغ",
|
|
||||||
"enter_amount_card": "مبلغ کارت به کارت شده را وارد کنید",
|
"enter_amount_card": "مبلغ کارت به کارت شده را وارد کنید",
|
||||||
"upload_deposit_slip": "آپلود فیش واریزی",
|
"upload_deposit_slip": "آپلود فیش واریزی",
|
||||||
"submit_slip": "ثبت فیش",
|
"submit_slip": "ثبت فیش",
|
||||||
|
|||||||
+29
-1
@@ -3,7 +3,7 @@ import Table from '@/components/Table'
|
|||||||
import Filters from '@/components/Filters'
|
import Filters from '@/components/Filters'
|
||||||
import { Add } from 'iconsax-react'
|
import { Add } from 'iconsax-react'
|
||||||
import Button from '@/components/Button'
|
import Button from '@/components/Button'
|
||||||
import { useGetFoods, useDeleteFood, useSetStockForFood } from './hooks/useFoodData'
|
import { useGetFoods, useDeleteFood, useCloneFood, useSetStockForFood } from './hooks/useFoodData'
|
||||||
import { useFoodFilters } from './hooks/useFoodFilters'
|
import { useFoodFilters } from './hooks/useFoodFilters'
|
||||||
import { getFoodTableColumns } from './components/FoodTableColumns'
|
import { getFoodTableColumns } from './components/FoodTableColumns'
|
||||||
import { useFoodFiltersFields } from './components/FoodFiltersFields'
|
import { useFoodFiltersFields } from './components/FoodFiltersFields'
|
||||||
@@ -20,6 +20,7 @@ const FoodsList: FC = () => {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [stockModalFood, setStockModalFood] = useState<Food | null>(null)
|
const [stockModalFood, setStockModalFood] = useState<Food | null>(null)
|
||||||
const [deleteConfirmFood, setDeleteConfirmFood] = useState<Food | null>(null)
|
const [deleteConfirmFood, setDeleteConfirmFood] = useState<Food | null>(null)
|
||||||
|
const [cloneConfirmFood, setCloneConfirmFood] = useState<Food | null>(null)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
filters,
|
filters,
|
||||||
@@ -31,6 +32,7 @@ const FoodsList: FC = () => {
|
|||||||
|
|
||||||
const { data: foodsData, isLoading } = useGetFoods(apiParams, filters)
|
const { data: foodsData, isLoading } = useGetFoods(apiParams, filters)
|
||||||
const { mutate: deleteFood, isPending: isDeleting } = useDeleteFood()
|
const { mutate: deleteFood, isPending: isDeleting } = useDeleteFood()
|
||||||
|
const { mutate: cloneFood, isPending: isCloning } = useCloneFood()
|
||||||
const { mutate: setStock, isPending: isUpdatingStock } = useSetStockForFood()
|
const { mutate: setStock, isPending: isUpdatingStock } = useSetStockForFood()
|
||||||
|
|
||||||
const foods = foodsData?.data || []
|
const foods = foodsData?.data || []
|
||||||
@@ -49,6 +51,10 @@ const FoodsList: FC = () => {
|
|||||||
label: 'بروزرسانی موجودی',
|
label: 'بروزرسانی موجودی',
|
||||||
onClick: () => setStockModalFood(item),
|
onClick: () => setStockModalFood(item),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'دابلیکیت کردن',
|
||||||
|
onClick: () => setCloneConfirmFood(item),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'حذف',
|
label: 'حذف',
|
||||||
onClick: () => setDeleteConfirmFood(item),
|
onClick: () => setDeleteConfirmFood(item),
|
||||||
@@ -85,6 +91,20 @@ const FoodsList: FC = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleCloneConfirm = () => {
|
||||||
|
if (!cloneConfirmFood) return
|
||||||
|
|
||||||
|
cloneFood(cloneConfirmFood.id, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success('غذا با موفقیت دابلیکیت شد')
|
||||||
|
setCloneConfirmFood(null)
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(extractErrorMessage(error))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-5'>
|
<div className='mt-5'>
|
||||||
<div className='flex justify-between items-center'>
|
<div className='flex justify-between items-center'>
|
||||||
@@ -138,6 +158,14 @@ const FoodsList: FC = () => {
|
|||||||
isloading={isDeleting}
|
isloading={isDeleting}
|
||||||
label={deleteConfirmFood ? `آیا از حذف «${deleteConfirmFood.title}» اطمینان دارید؟` : undefined}
|
label={deleteConfirmFood ? `آیا از حذف «${deleteConfirmFood.title}» اطمینان دارید؟` : undefined}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ModalConfrim
|
||||||
|
isOpen={!!cloneConfirmFood}
|
||||||
|
close={() => setCloneConfirmFood(null)}
|
||||||
|
onConfrim={handleCloneConfirm}
|
||||||
|
isloading={isCloning}
|
||||||
|
label={cloneConfirmFood ? `آیا از دابلیکیت کردن «${cloneConfirmFood.title}» اطمینان دارید؟` : undefined}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,16 @@ export const useDeleteFood = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useCloneFood = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.cloneFood,
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["foods"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useCreateCategory = () => {
|
export const useCreateCategory = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
|
|||||||
@@ -55,6 +55,11 @@ export const deleteFood = async (id: string) => {
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const cloneFood = async (id: string) => {
|
||||||
|
const { data } = await axios.post(`/admin/foods/${id}/clone`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
export const createCategory = async (params: CreateCategoryType) => {
|
export const createCategory = async (params: CreateCategoryType) => {
|
||||||
const { data } = await axios.post(`/admin/categories`, params);
|
const { data } = await axios.post(`/admin/categories`, params);
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import Button from "@/components/Button";
|
||||||
|
import Tabs from "@/components/Tabs";
|
||||||
|
import { EmptyWallet, ReceiptItem } from "iconsax-react";
|
||||||
|
import { type FC, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { formatPrice } from "@/helpers/func";
|
||||||
|
import ChargeModal from "./components/ChargeModal";
|
||||||
|
import ChargeRequestsTab from "./components/ChargeRequestsTab";
|
||||||
|
import TransactionsTab from "./components/TransactionsTab";
|
||||||
|
import { WalletTabEnum } from "./enum/Enum";
|
||||||
|
import { useGetWalletBalance } from "./hooks/useWalletData";
|
||||||
|
import MoonLoader from "react-spinners/MoonLoader";
|
||||||
|
|
||||||
|
const WalletTransactions: FC = () => {
|
||||||
|
const { t } = useTranslation("global");
|
||||||
|
const [activeTab, setActiveTab] = useState<WalletTabEnum>(WalletTabEnum.TRANSACTIONS);
|
||||||
|
const [showChargeModal, setShowChargeModal] = useState(false);
|
||||||
|
const { data: balanceData, isLoading: isBalanceLoading } = useGetWalletBalance();
|
||||||
|
|
||||||
|
const balance = balanceData?.data?.balance ?? 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-5">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<h1 className="text-lg font-light">{t("transaction.transaction")}</h1>
|
||||||
|
<Button className="w-fit px-6" onClick={() => setShowChargeModal(true)}>
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
<EmptyWallet color="#fff" size={20} />
|
||||||
|
<span>{t("wallet.charge")}</span>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-6 bg-[#F5F5F5] rounded-2xl px-6 py-4 flex items-center gap-3 w-fit">
|
||||||
|
<EmptyWallet size={22} color="black" />
|
||||||
|
<div>
|
||||||
|
<div className="text-xs text-description">{t("wallet.balance")}</div>
|
||||||
|
<div className="text-sm font-medium min-w-[100px]">
|
||||||
|
{isBalanceLoading ? (
|
||||||
|
<MoonLoader size={14} color="#000" />
|
||||||
|
) : (
|
||||||
|
formatPrice(balance)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-8">
|
||||||
|
<Tabs
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
icon: (
|
||||||
|
<ReceiptItem
|
||||||
|
color={activeTab === WalletTabEnum.TRANSACTIONS ? "black" : "#8C90A3"}
|
||||||
|
size={24}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
label: t("wallet.transactions_tab"),
|
||||||
|
value: WalletTabEnum.TRANSACTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: (
|
||||||
|
<EmptyWallet
|
||||||
|
color={activeTab === WalletTabEnum.CHARGE_REQUESTS ? "black" : "#8C90A3"}
|
||||||
|
size={24}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
label: t("wallet.charge_requests_tab"),
|
||||||
|
value: WalletTabEnum.CHARGE_REQUESTS,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onChange={(value) => setActiveTab(value as WalletTabEnum)}
|
||||||
|
active={activeTab}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{activeTab === WalletTabEnum.TRANSACTIONS && <TransactionsTab />}
|
||||||
|
{activeTab === WalletTabEnum.CHARGE_REQUESTS && <ChargeRequestsTab />}
|
||||||
|
|
||||||
|
<ChargeModal
|
||||||
|
open={showChargeModal}
|
||||||
|
onClose={() => setShowChargeModal(false)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WalletTransactions;
|
||||||
@@ -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,79 @@
|
|||||||
|
import type { ColumnType } from "@/components/types/TableTypes";
|
||||||
|
import StatusWithText from "@/components/StatusWithText";
|
||||||
|
import { formatOptionalDate, formatPrice } from "@/helpers/func";
|
||||||
|
import type { TFunction } from "i18next";
|
||||||
|
import type { ChargeRequest } from "../types/Types";
|
||||||
|
|
||||||
|
const getStatusVariant = (status?: string | null): "success" | "error" | "warning" => {
|
||||||
|
if (!status) return "warning";
|
||||||
|
|
||||||
|
const normalized = status.toLowerCase();
|
||||||
|
|
||||||
|
if (["success", "successful", "paid", "completed", "approved"].includes(normalized)) {
|
||||||
|
return "success";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (["failed", "unsuccessful", "rejected", "cancelled", "canceled"].includes(normalized)) {
|
||||||
|
return "error";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "warning";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusLabel = (status: string | null | undefined, t: TFunction) => {
|
||||||
|
if (!status) return "-";
|
||||||
|
|
||||||
|
const key = `wallet.charge_status_${status.toLowerCase()}`;
|
||||||
|
const translated = t(key);
|
||||||
|
return translated !== key ? translated : status;
|
||||||
|
};
|
||||||
|
|
||||||
|
const openInvoice = (invoiceId: string) => {
|
||||||
|
const invoiceBaseUrl = import.meta.env.VITE_INVOICE_URL;
|
||||||
|
if (invoiceId && invoiceBaseUrl) {
|
||||||
|
window.open(`${invoiceBaseUrl}${invoiceId}`, "_blank");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getChargeRequestTableColumns = (
|
||||||
|
t: TFunction
|
||||||
|
): ColumnType<ChargeRequest>[] => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: "amount",
|
||||||
|
title: t("transaction.amount"),
|
||||||
|
render: (item: ChargeRequest) => formatPrice(item.amount),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "status",
|
||||||
|
title: t("transaction.status"),
|
||||||
|
render: (item: ChargeRequest) => (
|
||||||
|
<StatusWithText
|
||||||
|
variant={getStatusVariant(item.status)}
|
||||||
|
text={getStatusLabel(item.status, t)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "invoiceId",
|
||||||
|
title: t("wallet.invoice"),
|
||||||
|
render: (item: ChargeRequest) =>
|
||||||
|
item.invoiceId ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => openInvoice(item.invoiceId)}
|
||||||
|
className="text-primary text-xs hover:underline"
|
||||||
|
>
|
||||||
|
{t("wallet.view_invoice")}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
"-"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "createdAt",
|
||||||
|
title: t("transaction.date"),
|
||||||
|
render: (item: ChargeRequest) => formatOptionalDate(item.createdAt),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import Filters from "@/components/Filters";
|
||||||
|
import Table from "@/components/Table";
|
||||||
|
import type { RowDataType } from "@/components/types/TableTypes";
|
||||||
|
import { type FC } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { getChargeRequestTableColumns } from "./ChargeRequestTableColumns";
|
||||||
|
import { useGetChargeRequests } from "../hooks/useWalletData";
|
||||||
|
import { useChargeRequestFilters } from "../hooks/useChargeRequestFilters";
|
||||||
|
import type { ChargeRequest } from "../types/Types";
|
||||||
|
|
||||||
|
const ChargeRequestsTab: FC = () => {
|
||||||
|
const { t } = useTranslation("global");
|
||||||
|
const {
|
||||||
|
filters,
|
||||||
|
currentPage,
|
||||||
|
apiParams,
|
||||||
|
handleFiltersChange,
|
||||||
|
handlePageChange,
|
||||||
|
limit,
|
||||||
|
} = useChargeRequestFilters();
|
||||||
|
|
||||||
|
const { data: chargeRequestsData, isLoading } = useGetChargeRequests(apiParams);
|
||||||
|
|
||||||
|
const chargeRequests = chargeRequestsData?.data || [];
|
||||||
|
const totalPages =
|
||||||
|
chargeRequestsData?.meta?.totalPages ||
|
||||||
|
Math.max(1, Math.ceil(chargeRequests.length / limit) || 1);
|
||||||
|
const columns = getChargeRequestTableColumns(t);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="mt-8">
|
||||||
|
<Filters
|
||||||
|
fields={[]}
|
||||||
|
onChange={handleFiltersChange}
|
||||||
|
initialValues={filters}
|
||||||
|
searchField="search"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Table<ChargeRequest & RowDataType>
|
||||||
|
columns={columns}
|
||||||
|
data={chargeRequests as (ChargeRequest & RowDataType)[]}
|
||||||
|
isloading={isLoading}
|
||||||
|
pagination={{
|
||||||
|
currentPage,
|
||||||
|
totalPages,
|
||||||
|
onPageChange: handlePageChange,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChargeRequestsTab;
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import type { ColumnType } from "@/components/types/TableTypes";
|
||||||
|
import StatusWithText from "@/components/StatusWithText";
|
||||||
|
import { formatOptionalDate, formatPrice } from "@/helpers/func";
|
||||||
|
import type { TFunction } from "i18next";
|
||||||
|
import type { WalletTransaction } from "../types/Types";
|
||||||
|
|
||||||
|
const getTypeLabel = (type: string, t: TFunction) => {
|
||||||
|
if (type === "credit") return t("wallet.type_credit");
|
||||||
|
if (type === "debit") return t("wallet.type_debit");
|
||||||
|
return type || "-";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTypeVariant = (type: string): "success" | "error" | "warning" => {
|
||||||
|
if (type === "credit") return "success";
|
||||||
|
if (type === "debit") return "error";
|
||||||
|
return "warning";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getReasonLabel = (reason: string, t: TFunction) => {
|
||||||
|
const key = `wallet.reason_${reason}`;
|
||||||
|
const translated = t(key);
|
||||||
|
return translated !== key ? translated : reason || "-";
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTransactionTableColumns = (
|
||||||
|
t: TFunction
|
||||||
|
): ColumnType<WalletTransaction>[] => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: "type",
|
||||||
|
title: t("transaction.transaction2"),
|
||||||
|
render: (item: WalletTransaction) => (
|
||||||
|
<StatusWithText
|
||||||
|
variant={getTypeVariant(item.type)}
|
||||||
|
text={getTypeLabel(item.type, t)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "amount",
|
||||||
|
title: t("transaction.amount"),
|
||||||
|
render: (item: WalletTransaction) => formatPrice(item.amount),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "balance",
|
||||||
|
title: t("wallet.balance_after"),
|
||||||
|
render: (item: WalletTransaction) => formatPrice(item.balance),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "reason",
|
||||||
|
title: t("wallet.reason"),
|
||||||
|
render: (item: WalletTransaction) => getReasonLabel(item.reason, t),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "createdAt",
|
||||||
|
title: t("transaction.date"),
|
||||||
|
render: (item: WalletTransaction) => formatOptionalDate(item.createdAt),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import Filters from "@/components/Filters";
|
||||||
|
import Table from "@/components/Table";
|
||||||
|
import type { RowDataType } from "@/components/types/TableTypes";
|
||||||
|
import { type FC } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { getTransactionTableColumns } from "./TransactionTableColumns";
|
||||||
|
import { useGetWalletTransactions } from "../hooks/useWalletData";
|
||||||
|
import { useWalletTransactionFilters } from "../hooks/useWalletTransactionFilters";
|
||||||
|
import type { WalletTransaction } from "../types/Types";
|
||||||
|
|
||||||
|
const TransactionsTab: FC = () => {
|
||||||
|
const { t } = useTranslation("global");
|
||||||
|
const {
|
||||||
|
filters,
|
||||||
|
currentPage,
|
||||||
|
apiParams,
|
||||||
|
handleFiltersChange,
|
||||||
|
handlePageChange,
|
||||||
|
limit,
|
||||||
|
} = useWalletTransactionFilters();
|
||||||
|
|
||||||
|
const { data: transactionsData, isLoading } = useGetWalletTransactions(apiParams);
|
||||||
|
|
||||||
|
const transactions = transactionsData?.data || [];
|
||||||
|
const totalPages =
|
||||||
|
transactionsData?.meta?.totalPages ||
|
||||||
|
Math.max(1, Math.ceil(transactions.length / limit) || 1);
|
||||||
|
const columns = getTransactionTableColumns(t);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="mt-8">
|
||||||
|
<Filters
|
||||||
|
fields={[]}
|
||||||
|
onChange={handleFiltersChange}
|
||||||
|
initialValues={filters}
|
||||||
|
searchField="search"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Table<WalletTransaction & RowDataType>
|
||||||
|
columns={columns}
|
||||||
|
data={transactions as (WalletTransaction & RowDataType)[]}
|
||||||
|
isloading={isLoading}
|
||||||
|
pagination={{
|
||||||
|
currentPage,
|
||||||
|
totalPages,
|
||||||
|
onPageChange: handlePageChange,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TransactionsTab;
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { type FC, useState } from "react";
|
||||||
|
import { EmptyWallet } from "iconsax-react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { Pages } from "@/config/Pages";
|
||||||
|
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 navigate = useNavigate();
|
||||||
|
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">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => navigate(Pages.wallet.transactions)}
|
||||||
|
className="flex items-center gap-2 cursor-pointer hover:opacity-80 transition-opacity"
|
||||||
|
>
|
||||||
|
<EmptyWallet size={18} color="black" />
|
||||||
|
<div className="text-xs whitespace-nowrap min-w-[80px]">
|
||||||
|
{isLoading ? (
|
||||||
|
<MoonLoader size={12} color="#000" />
|
||||||
|
) : (
|
||||||
|
formatPrice(balance)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<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,4 @@
|
|||||||
|
export enum WalletTabEnum {
|
||||||
|
TRANSACTIONS = "transactions",
|
||||||
|
CHARGE_REQUESTS = "charge_requests",
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { useMemo, useState } from "react";
|
||||||
|
import type { FilterValues } from "@/components/Filters";
|
||||||
|
import type { GetChargeRequestsParams } from "../types/Types";
|
||||||
|
|
||||||
|
const DEFAULT_PAGE = 1;
|
||||||
|
const DEFAULT_LIMIT = 10;
|
||||||
|
|
||||||
|
export const useChargeRequestFilters = () => {
|
||||||
|
const [filters, setFilters] = useState<FilterValues>({});
|
||||||
|
const [currentPage, setCurrentPage] = useState(DEFAULT_PAGE);
|
||||||
|
const [limit] = useState(DEFAULT_LIMIT);
|
||||||
|
|
||||||
|
const apiParams = useMemo<GetChargeRequestsParams>(() => {
|
||||||
|
const params: GetChargeRequestsParams = {
|
||||||
|
page: currentPage,
|
||||||
|
limit,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (filters.search) {
|
||||||
|
params.search = filters.search as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}, [filters, currentPage, limit]);
|
||||||
|
|
||||||
|
const handleFiltersChange = (newFilters: FilterValues) => {
|
||||||
|
setFilters(newFilters);
|
||||||
|
setCurrentPage(DEFAULT_PAGE);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageChange = (page: number) => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
filters,
|
||||||
|
currentPage,
|
||||||
|
apiParams,
|
||||||
|
handleFiltersChange,
|
||||||
|
handlePageChange,
|
||||||
|
limit,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import * as api from "../service/WalletService";
|
||||||
|
import type { GetChargeRequestsParams, GetWalletTransactionsParams } from "../types/Types";
|
||||||
|
|
||||||
|
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"] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["charge-requests"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetWalletTransactions = (params?: GetWalletTransactionsParams) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["wallet-transactions", params],
|
||||||
|
queryFn: () => api.getWalletTransactions(params),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetChargeRequests = (params?: GetChargeRequestsParams) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["charge-requests", params],
|
||||||
|
queryFn: () => api.getChargeRequests(params),
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { useMemo, useState } from "react";
|
||||||
|
import type { FilterValues } from "@/components/Filters";
|
||||||
|
import type { GetWalletTransactionsParams } from "../types/Types";
|
||||||
|
|
||||||
|
const DEFAULT_PAGE = 1;
|
||||||
|
const DEFAULT_LIMIT = 10;
|
||||||
|
|
||||||
|
export const useWalletTransactionFilters = () => {
|
||||||
|
const [filters, setFilters] = useState<FilterValues>({});
|
||||||
|
const [currentPage, setCurrentPage] = useState(DEFAULT_PAGE);
|
||||||
|
const [limit] = useState(DEFAULT_LIMIT);
|
||||||
|
|
||||||
|
const apiParams = useMemo<GetWalletTransactionsParams>(() => {
|
||||||
|
const params: GetWalletTransactionsParams = {
|
||||||
|
page: currentPage,
|
||||||
|
limit,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (filters.search) {
|
||||||
|
params.search = filters.search as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}, [filters, currentPage, limit]);
|
||||||
|
|
||||||
|
const handleFiltersChange = (newFilters: FilterValues) => {
|
||||||
|
setFilters(newFilters);
|
||||||
|
setCurrentPage(DEFAULT_PAGE);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageChange = (page: number) => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
filters,
|
||||||
|
currentPage,
|
||||||
|
apiParams,
|
||||||
|
handleFiltersChange,
|
||||||
|
handlePageChange,
|
||||||
|
limit,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import axios from "@/config/axios";
|
||||||
|
import type {
|
||||||
|
ChargeRequestResponse,
|
||||||
|
ChargeRequestType,
|
||||||
|
ChargeRequestsResponse,
|
||||||
|
GetChargeRequestsParams,
|
||||||
|
GetWalletTransactionsParams,
|
||||||
|
WalletBalanceResponse,
|
||||||
|
WalletTransactionsResponse,
|
||||||
|
} 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWalletTransactions = async (
|
||||||
|
params?: GetWalletTransactionsParams
|
||||||
|
): Promise<WalletTransactionsResponse> => {
|
||||||
|
const { data } = await axios.get<WalletTransactionsResponse>(
|
||||||
|
"/admin/restaurants/wallet/transactions",
|
||||||
|
{ params }
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getChargeRequests = async (
|
||||||
|
params?: GetChargeRequestsParams
|
||||||
|
): Promise<ChargeRequestsResponse> => {
|
||||||
|
const { data } = await axios.get<ChargeRequestsResponse>(
|
||||||
|
"/admin/restaurants/charge-requests",
|
||||||
|
{ params }
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
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>;
|
||||||
|
|
||||||
|
export type ChargeRequest = {
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
deletedAt: string | null;
|
||||||
|
restaurant: string;
|
||||||
|
amount: number;
|
||||||
|
invoiceId: string;
|
||||||
|
status: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetChargeRequestsParams = {
|
||||||
|
page?: number;
|
||||||
|
limit?: number;
|
||||||
|
search?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ChargeRequestsResponse = IResponse<ChargeRequest[]> & {
|
||||||
|
meta?: PaginationMeta;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WalletTransactionType = "credit" | "debit";
|
||||||
|
|
||||||
|
export type WalletTransaction = {
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
deletedAt: string | null;
|
||||||
|
restaurant: string;
|
||||||
|
amount: number;
|
||||||
|
balance: number;
|
||||||
|
type: WalletTransactionType | string;
|
||||||
|
reason: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PaginationMeta = {
|
||||||
|
total: number;
|
||||||
|
page: number;
|
||||||
|
limit: number;
|
||||||
|
totalPages: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetWalletTransactionsParams = {
|
||||||
|
page?: number;
|
||||||
|
limit?: number;
|
||||||
|
search?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WalletTransactionsResponse = IResponse<WalletTransaction[]> & {
|
||||||
|
meta?: PaginationMeta;
|
||||||
|
};
|
||||||
@@ -55,6 +55,7 @@ import CreateUserGroup from '@/pages/customers/groups/Create'
|
|||||||
import UpdateUserGroup from '@/pages/customers/groups/Update'
|
import UpdateUserGroup from '@/pages/customers/groups/Update'
|
||||||
import CampaignsList from '@/pages/customers/campaigns/List'
|
import CampaignsList from '@/pages/customers/campaigns/List'
|
||||||
import CreateCampaign from '@/pages/customers/campaigns/Create'
|
import CreateCampaign from '@/pages/customers/campaigns/Create'
|
||||||
|
import WalletTransactions from '@/pages/wallet/Transactions'
|
||||||
const MainRouter: FC = () => {
|
const MainRouter: FC = () => {
|
||||||
|
|
||||||
const { hasSubMenu } = useSharedStore()
|
const { hasSubMenu } = useSharedStore()
|
||||||
@@ -132,6 +133,8 @@ const MainRouter: FC = () => {
|
|||||||
<Route path={Pages.reports.list} element={<ReportsList />} />
|
<Route path={Pages.reports.list} element={<ReportsList />} />
|
||||||
|
|
||||||
<Route path={Pages.statistics.list} element={<Statistics />} />
|
<Route path={Pages.statistics.list} element={<Statistics />} />
|
||||||
|
|
||||||
|
<Route path={Pages.wallet.transactions} element={<WalletTransactions />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useSharedStore } from './store/sharedStore'
|
|||||||
import { HambergerMenu } from 'iconsax-react'
|
import { HambergerMenu } from 'iconsax-react'
|
||||||
import { clx } from '@/helpers/utils'
|
import { clx } from '@/helpers/utils'
|
||||||
import Notifications from '@/pages/notification/Notification'
|
import Notifications from '@/pages/notification/Notification'
|
||||||
|
import WalletHeader from '@/pages/wallet/components/WalletHeader'
|
||||||
|
|
||||||
const Header: FC = () => {
|
const Header: FC = () => {
|
||||||
|
|
||||||
@@ -43,9 +44,7 @@ const Header: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
{/* <img src={LogoImage} className='h-6 xl:hidden block absolute right-0 left-0 mx-auto' /> */}
|
{/* <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'>
|
<div className='flex xl:gap-6 gap-4 items-center'>
|
||||||
{/* <Link className='xl:hidden' to={Pages.wallet}>
|
<WalletHeader />
|
||||||
<Wallet className='xl:size-[18px] size-[17px]' color='black' />
|
|
||||||
</Link> */}
|
|
||||||
<Notifications />
|
<Notifications />
|
||||||
{/* {
|
{/* {
|
||||||
data && (
|
data && (
|
||||||
|
|||||||
Vendored
+8
@@ -1,5 +1,13 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
readonly VITE_INVOICE_URL: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
}
|
||||||
|
|
||||||
declare module "*.svg?raw" {
|
declare module "*.svg?raw" {
|
||||||
const content: string;
|
const content: string;
|
||||||
export default content;
|
export default content;
|
||||||
|
|||||||
Reference in New Issue
Block a user