show storage

This commit is contained in:
hamid zarghami
2025-07-12 10:09:11 +03:30
parent 6d564e4045
commit 0cf98ee67c
15 changed files with 223 additions and 19 deletions
+2
View File
@@ -13,6 +13,7 @@ import { IApiErrorRepsonse } from './types/error.types';
import ToastContainer from './components/Toast';
import { getRefreshToken, removeRefreshToken, removeToken, setRefreshToken, setToken } from './config/func';
import { refreshToken } from './pages/auth/service/Service';
import BuySpace from './shared/components/BuySpace'
declare global {
interface Window {
@@ -102,6 +103,7 @@ const App: FC = () => {
<Main />
<ToastContainer />
<BuySpace />
</QueryClientProvider>
</I18nextProvider>
</BrowserRouter>
+57
View File
@@ -0,0 +1,57 @@
import { FC, Fragment, ReactNode, useEffect } from 'react'
import HeaderModal from './HeaderModal'
interface Props {
open: boolean,
close: () => void,
children: ReactNode,
isHeader?: boolean,
title_header?: string,
width?: number
}
const DefaulModal: FC<Props> = (props: Props) => {
useEffect(() => {
if (props.open) {
document.body.style.overflow = 'hidden'
} else {
document.body.style.overflow = 'auto'
}
}, [props.open])
return (
<Fragment>
{
props.open && (
<Fragment>
<div style={{ maxWidth: props.width }} className='xl:justify-center xl:items-center items-end flex overflow-x-hidden overflow-y-auto fixed inset-0 z-[60] h-auto top-0 bottom-0 m-auto outline-none focus:outline-none xl:max-w-xl mx-auto'>
<div className='relative xl:h-full max-h-[80%] bottom-0 left-0 flex xl:items-center sm:h-auto w-full xl:my-6 xl:p-2'>
<div className='border-0 h-auto p-5 lg:min-w-full overflow-y-auto rounded-3xl rounded-b-none xl:rounded-b-3xl relative flex flex-col w-full modalGlass2 outline-none focus:outline-none'>
{
props.isHeader && props.title_header &&
<div onClick={props.close} className='pb-6 border-b border-white border-opacity-20'>
<div className='h-[5px] w-[200px] mx-auto bg-[#D1D3D7] rounded-full mb-4 xl:hidden'></div>
<HeaderModal close={props.close} label={props.title_header} />
</div>
}
{props.children}
</div>
</div>
</div>
<div onClick={props.close} className='fixed size-full top-0 bottom-0 right-0 modalGlass inset-0 z-50 '></div>
</Fragment>
)
}
</Fragment>
)
}
export default DefaulModal
+21
View File
@@ -0,0 +1,21 @@
import { CloseCircle } from 'iconsax-react'
import { FC } from 'react'
type Props = {
label: string,
close: () => void,
}
const HeaderModal: FC<Props> = (props: Props) => {
return (
<div className='flex justify-between items-center'>
<div className='text-sm'>{props.label}</div>
<div className='size-7 rounded-full flex justify-center items-center'>
<CloseCircle size={20} color='black' onClick={props.close} />
</div>
</div>
)
}
export default HeaderModal
+17
View File
@@ -0,0 +1,17 @@
import { FC } from 'react'
const ProgressBar: FC<{ percentage: number }> = ({ percentage }) => {
return (
<div className='w-full bg-gray-200 dltr overflow-hidden rounded-full h-2'>
<div
className={`h-2 rounded-full transition-all duration-300 ${percentage > 90 ? 'bg-red-500' :
percentage > 70 ? 'bg-yellow-500' :
'bg-green-500'
}`}
style={{ width: `${percentage}%` }}
></div>
</div>
)
}
export default ProgressBar
+20
View File
@@ -247,3 +247,23 @@ textarea::placeholder {
.ql-snow .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg {
display: none;
}
.modalGlass {
background: rgba(0, 0, 0, 0.436);
background-blend-mode: multiply;
backdrop-filter: blur(7px);
}
.modalGlass2 {
background: rgba(255, 255, 255, 0.502);
background-blend-mode: multiply;
/* backdrop-filter: blur(5px); */
}
.modalGlass3 {
background: rgba(62, 61, 61, 0.2);
background-blend-mode: multiply;
backdrop-filter: blur(44px);
}
+6 -1
View File
@@ -12,7 +12,12 @@
"spam": "هرزنامه ها",
"setting": "تنظیمات",
"logout": "خروج",
"admin_panel": "پنل مدیریت"
"admin_panel": "پنل مدیریت",
"storage": "ذخیره‌سازی",
"add_space": "اضافه کردن فضا"
},
"space": {
"title": "خرید فضای ذخیره سازی"
},
"header": {
"search": "جستجو"
@@ -1,6 +1,7 @@
import { convertToGB } from '@/config/func'
import { FC } from 'react'
import { AddressData } from '../types/Types'
import ProgressBar from '@/components/ProgressBar'
const ProgressBarEmail: FC<{ item: AddressData }> = ({ item }) => {
const usedQuota = item.emailQuotaUsed || 0;
@@ -17,15 +18,7 @@ const ProgressBarEmail: FC<{ item: AddressData }> = ({ item }) => {
{percentage.toFixed(1)}%
</span> */}
</div>
<div className='w-full bg-gray-200 dltr overflow-hidden rounded-full h-2'>
<div
className={`h-2 rounded-full transition-all duration-300 ${percentage > 90 ? 'bg-red-500' :
percentage > 70 ? 'bg-yellow-500' :
'bg-green-500'
}`}
style={{ width: `${percentage}%` }}
></div>
</div>
<ProgressBar percentage={percentage} />
</div>
)
}
@@ -30,3 +30,10 @@ export const useVerifyDnsRecord = () => {
queryFn: () => api.verifyDnsRecord(),
});
};
export const useGetSpace = () => {
return useQuery({
queryKey: ["space"],
queryFn: () => api.getSpace(),
});
};
@@ -20,3 +20,8 @@ export const verifyDnsRecord = async () => {
const { data } = await axios.get(`/domains/verify-dns`);
return data;
};
export const getSpace = async () => {
const { data } = await axios.get(`/business/quota`);
return data;
};
+2 -9
View File
@@ -2,16 +2,9 @@ import { FC } from 'react'
import SideBar from './SideBar'
import AppRouter from '@/router/AppRouter'
import Header from './Header'
import Button from '@/components/Button'
import { Add } from 'iconsax-react'
import { useTranslation } from 'react-i18next'
import { useSharedStore } from './store/sharedStore'
const Main: FC = () => {
const { t } = useTranslation()
const { setOpenSidebar, setOpenNewMessage } = useSharedStore()
return (
<div className='p-2 md:p-4 overflow-hidden'>
<SideBar />
@@ -24,7 +17,7 @@ const Main: FC = () => {
</div>
</div>
<div className='flex fixed bottom-4 xl:hidden right-0 justify-center mt-10 md:mt-14 px-4'>
{/* <div className='flex fixed bottom-4 xl:hidden right-0 justify-center mt-10 md:mt-14 px-4'>
<Button
className='bg-secondary font-normal text-primary w-fit px-4 md:px-6 text-sm'
onClick={() => {
@@ -37,7 +30,7 @@ const Main: FC = () => {
<div>{t('sidebar.new_message')}</div>
</div>
</Button>
</div>
</div> */}
</div>
)
+2
View File
@@ -7,6 +7,7 @@ import { useSharedStore } from './store/sharedStore'
import { clx } from '../helpers/utils'
import { useTranslation } from 'react-i18next'
import { Paths } from '@/utils/Paths'
import Storage from './components/Storage'
const SideBar: FC = () => {
@@ -76,6 +77,7 @@ const SideBar: FC = () => {
</div>
<div className='flex-1 flex flex-col justify-end mt-10 md:mt-14 pb-6 md:pb-8'>
<Storage />
<div className='text-xs text-[#8C90A3]'>
<SideBarItem
icon={<Logout variant={isActive('logout') ? 'Bold' : 'Outline'} color={isActive('logout') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
+26
View File
@@ -0,0 +1,26 @@
import DefaulModal from '@/components/DefaulModal'
import { useSharedStore } from '@/shared/store/sharedStore'
import { useTranslation } from 'react-i18next'
import { FC } from 'react'
const BuySpace: FC = () => {
const { t } = useTranslation()
const { openBuySpace, setOpenBuySpace } = useSharedStore()
console.log(openBuySpace);
return (
<DefaulModal
open={openBuySpace}
close={() => setOpenBuySpace(false)}
isHeader={true}
title_header={t('space.title')}
>
<div className='mt-5'>
</div>
</DefaulModal >
)
}
export default BuySpace
+52
View File
@@ -0,0 +1,52 @@
import Button from '@/components/Button'
import ProgressBar from '@/components/ProgressBar'
import { useGetSpace } from '@/pages/setting/domain/hooks/useDomainData'
import { useSharedStore } from '@/shared/store/sharedStore'
import { Cloud } from 'iconsax-react'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
const Storage: FC = () => {
const { t } = useTranslation()
const { setOpenBuySpace } = useSharedStore()
const { data } = useGetSpace()
return (
<div className='px-10 pb-5'>
<div className='flex gap-2 items-center'>
<Cloud size={20} color='#8C90A3' />
<div className='text-xs text-description'>
{t('sidebar.storage')}
</div>
</div>
<div className='mt-5'>
<ProgressBar
percentage={
data?.data?.quota?.totalInGB
? Math.round(
(data?.data?.quota?.usedInGB / data?.data?.quota?.totalInGB) * 100
)
: 0
}
/>
</div>
<div className='mt-5 text-xs'>
{data?.data?.quota?.usedInGB} گیگابایت از {data?.data?.quota?.totalInGB} گیگابایت استفاده شده است
</div>
<div className='mt-5'>
<Button
variant='secondary'
label={t('sidebar.add_space')}
onClick={() => setOpenBuySpace(true)}
/>
</div>
</div>
)
}
export default Storage
+2
View File
@@ -6,4 +6,6 @@ export const useSharedStore = create<SharedStoreType>((set) => ({
setOpenSidebar: (value) => set({ openSidebar: value }),
openNewMessage: false,
setOpenNewMessage: (value) => set({ openNewMessage: value }),
openBuySpace: false,
setOpenBuySpace: (value) => set({ openBuySpace: value }),
}));
+2
View File
@@ -3,4 +3,6 @@ export type SharedStoreType = {
setOpenSidebar: (value: boolean) => void;
openNewMessage: boolean;
setOpenNewMessage: (value: boolean) => void;
openBuySpace: boolean;
setOpenBuySpace: (value: boolean) => void;
};