skeleton check loading
This commit is contained in:
@@ -3,6 +3,7 @@ import { BrowserRouter } from 'react-router-dom'
|
|||||||
import 'swiper/swiper-bundle.css';
|
import 'swiper/swiper-bundle.css';
|
||||||
import 'rc-rate/assets/index.css';
|
import 'rc-rate/assets/index.css';
|
||||||
import './assets/fonts/roboto/roboto.css'
|
import './assets/fonts/roboto/roboto.css'
|
||||||
|
import 'react-loading-skeleton/dist/skeleton.css'
|
||||||
import i18next from 'i18next'
|
import i18next from 'i18next'
|
||||||
import { I18nextProvider } from 'react-i18next'
|
import { I18nextProvider } from 'react-i18next'
|
||||||
import { QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
import { QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { FC, Fragment } from 'react'
|
||||||
|
import Td from './Td'
|
||||||
|
import Skeleton from 'react-loading-skeleton'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
tdCount: number,
|
||||||
|
trCount?: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
const DefaultTableSkeleton: FC<Props> = ({ tdCount, trCount = 5 }) => {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
{
|
||||||
|
Array.from({ length: trCount }).map((_, rowIndex) => (
|
||||||
|
<tr className="tr" key={rowIndex}>
|
||||||
|
{
|
||||||
|
Array.from({ length: tdCount }).map((_, colIndex) => (
|
||||||
|
<Td text={''} key={colIndex}>
|
||||||
|
<Skeleton />
|
||||||
|
</Td>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DefaultTableSkeleton
|
||||||
@@ -9,6 +9,7 @@ import { ItemServiceType } from '../pages/service/types/ServiecTypes'
|
|||||||
type Props = {
|
type Props = {
|
||||||
item: ItemServiceType,
|
item: ItemServiceType,
|
||||||
className?: string,
|
className?: string,
|
||||||
|
isLinkPanel?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const ServiceItem: FC<Props> = (props: Props) => {
|
const ServiceItem: FC<Props> = (props: Props) => {
|
||||||
|
|||||||
@@ -199,3 +199,29 @@ tbody tr {
|
|||||||
.has-number {
|
.has-number {
|
||||||
font-family: Roboto !important;
|
font-family: Roboto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.skeleton-box {
|
||||||
|
display: inline-block;
|
||||||
|
height: 1em;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #dddbdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-box:after {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
transform: translateX(-100%);
|
||||||
|
background-image: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
rgba(#fff, 0) 0,
|
||||||
|
rgba(#fff, 0.2) 20%,
|
||||||
|
rgba(#fff, 0.5) 60%,
|
||||||
|
rgba(#fff, 0)
|
||||||
|
);
|
||||||
|
animation: shimmer 5s infinite;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|||||||
+5
-2
@@ -244,7 +244,9 @@
|
|||||||
"accepted": "تایید شده",
|
"accepted": "تایید شده",
|
||||||
"sure_confrim_pay": "آیا مطمئن هستید که مبلغ صورتحساب از کیف پول شما کسر شود؟",
|
"sure_confrim_pay": "آیا مطمئن هستید که مبلغ صورتحساب از کیف پول شما کسر شود؟",
|
||||||
"thank_pay_1": "ضمن تشکر از خرید شما",
|
"thank_pay_1": "ضمن تشکر از خرید شما",
|
||||||
"thank_pay_2": "مبلغ این صورت حساب پرداخت شده است."
|
"thank_pay_2": "مبلغ این صورت حساب پرداخت شده است.",
|
||||||
|
"discount_code": "کد تخفیف",
|
||||||
|
"apply": "اعمال"
|
||||||
},
|
},
|
||||||
"confrim": {
|
"confrim": {
|
||||||
"subject": "حذف",
|
"subject": "حذف",
|
||||||
@@ -372,7 +374,8 @@
|
|||||||
"username_not_available": "نام کاربری موجود نیست",
|
"username_not_available": "نام کاربری موجود نیست",
|
||||||
"username_save_success": "نام کاربری با موفقیت ذخیره شد",
|
"username_save_success": "نام کاربری با موفقیت ذخیره شد",
|
||||||
"image_uploaded_successfully": "تصویر با موفقیت آپلود شد",
|
"image_uploaded_successfully": "تصویر با موفقیت آپلود شد",
|
||||||
"confrim_email": "تایید ایمیل"
|
"confrim_email": "تایید ایمیل",
|
||||||
|
"confrim": "تایید صورتحساب "
|
||||||
},
|
},
|
||||||
"email": "ایمیل",
|
"email": "ایمیل",
|
||||||
"save": "ذخیره",
|
"save": "ذخیره",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { clx } from '../../helpers/utils'
|
|||||||
import Button from '../../components/Button'
|
import Button from '../../components/Button'
|
||||||
import ApproveInvoice from './components/ApproveInvoice'
|
import ApproveInvoice from './components/ApproveInvoice'
|
||||||
import PayInvoice from './components/PayInvoice'
|
import PayInvoice from './components/PayInvoice'
|
||||||
|
import Input from '../../components/Input'
|
||||||
|
|
||||||
const ReceiptsDetail: FC = () => {
|
const ReceiptsDetail: FC = () => {
|
||||||
|
|
||||||
@@ -24,12 +25,32 @@ const ReceiptsDetail: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-4'>
|
<div className='mt-4'>
|
||||||
|
<div className='flex xl:flex-row flex-col gap-4 items-center'>
|
||||||
<div className='flex gap-6 items-center'>
|
<div className='flex gap-6 items-center'>
|
||||||
<div>{t('receip.detail_account')}</div>
|
<div className='whitespace-nowrap'>{t('receip.detail_account')}</div>
|
||||||
|
|
||||||
<ApproveInvoice status={getInvoce.data?.data?.invoice?.status} id={id ? id : ''} refetch={getInvoce.refetch} />
|
<ApproveInvoice status={getInvoce.data?.data?.invoice?.status} id={id ? id : ''} refetch={getInvoce.refetch} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
getInvoce.data?.data?.invoice?.status === 'PENDING' || getInvoce.data?.data?.invoice?.status === 'APPROVED' ?
|
||||||
|
<div className='flex gap-2 items-center w-full'>
|
||||||
|
<div className='w-full xl:w-fit'>
|
||||||
|
<Input
|
||||||
|
className='bg-transparent border border-border xl:max-w-40'
|
||||||
|
placeholder={t('receip.discount_code')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
label={t('receip.apply')}
|
||||||
|
className='bg-transparent text-black border border-black mt-1 px-5 w-fit'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
getInvoce.isPending || getProfile.isPending ?
|
getInvoce.isPending || getProfile.isPending ?
|
||||||
<PageLoading />
|
<PageLoading />
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const ApproveInvoice: FC<Props> = ({ id, refetch, status }) => {
|
|||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Button
|
<Button
|
||||||
isLoading={approveInvoce.isPending || requestApprove.isPending}
|
disabled={approveInvoce.isPending || requestApprove.isPending}
|
||||||
onClick={() => status === 'PENDING' ? handleShowModal() : null}
|
onClick={() => status === 'PENDING' ? handleShowModal() : null}
|
||||||
className={clx(
|
className={clx(
|
||||||
'px-9 w-fit bg-white bg-opacity-70 text-description',
|
'px-9 w-fit bg-white bg-opacity-70 text-description',
|
||||||
@@ -70,7 +70,7 @@ const ApproveInvoice: FC<Props> = ({ id, refetch, status }) => {
|
|||||||
size={20}
|
size={20}
|
||||||
color={status === 'PENDING' ? '#888888' : '#00BA4B'}
|
color={status === 'PENDING' ? '#888888' : '#00BA4B'}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div className='whitespace-nowrap'>
|
||||||
{
|
{
|
||||||
status === 'APPROVED' ?
|
status === 'APPROVED' ?
|
||||||
t('receip.accepted')
|
t('receip.accepted')
|
||||||
@@ -85,7 +85,7 @@ const ApproveInvoice: FC<Props> = ({ id, refetch, status }) => {
|
|||||||
open={showModal}
|
open={showModal}
|
||||||
close={() => setShowModal(false)}
|
close={() => setShowModal(false)}
|
||||||
isHeader
|
isHeader
|
||||||
title_header={t('profile.confrim_email')}
|
title_header={t('profile.confrim')}
|
||||||
>
|
>
|
||||||
<div className='mt-7'>
|
<div className='mt-7'>
|
||||||
<div className='text-xs text-center'>
|
<div className='text-xs text-center'>
|
||||||
@@ -121,6 +121,7 @@ const ApproveInvoice: FC<Props> = ({ id, refetch, status }) => {
|
|||||||
className='w-[150px] text-xs'
|
className='w-[150px] text-xs'
|
||||||
onClick={handleConfrim}
|
onClick={handleConfrim}
|
||||||
disabled={code.length !== 5}
|
disabled={code.length !== 5}
|
||||||
|
isLoading={approveInvoce.isPending}
|
||||||
>
|
>
|
||||||
<div className='flex gap-2 items-center'>
|
<div className='flex gap-2 items-center'>
|
||||||
<TickCircle
|
<TickCircle
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
import * as api from "../service/ReceiptService";
|
import * as api from "../service/ReceiptService";
|
||||||
import {
|
import {
|
||||||
|
ApplyDiscountType,
|
||||||
ApproveInvoiceType,
|
ApproveInvoiceType,
|
||||||
RequestApproveInvoiceType,
|
RequestApproveInvoiceType,
|
||||||
} from "../types/ReceiptTypes";
|
} from "../types/ReceiptTypes";
|
||||||
@@ -39,3 +40,9 @@ export const useRequestApprove = () => {
|
|||||||
api.requestApprove(variables),
|
api.requestApprove(variables),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useApplyDiscount = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (variables: ApplyDiscountType) => api.applyDiscount(variables),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import axios from "../../../config/axios";
|
import axios from "../../../config/axios";
|
||||||
import {
|
import {
|
||||||
|
ApplyDiscountType,
|
||||||
ApproveInvoiceType,
|
ApproveInvoiceType,
|
||||||
RequestApproveInvoiceType,
|
RequestApproveInvoiceType,
|
||||||
} from "../types/ReceiptTypes";
|
} from "../types/ReceiptTypes";
|
||||||
@@ -31,3 +32,11 @@ export const payInvoice = async (id: string) => {
|
|||||||
const { data } = await axios.post(`/invoices/${id}/pay`);
|
const { data } = await axios.post(`/invoices/${id}/pay`);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const applyDiscount = async (params: ApplyDiscountType) => {
|
||||||
|
const { data } = await axios.post(
|
||||||
|
`/invoices/${params.id}/apply-discount`,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
@@ -40,3 +40,8 @@ export type ApproveInvoiceType = {
|
|||||||
code: string;
|
code: string;
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApplyDiscountType = {
|
||||||
|
id: string;
|
||||||
|
discountCode: string;
|
||||||
|
};
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import Td from '../../components/Td'
|
import Td from '../../components/Td'
|
||||||
import Detail from './components/Detail'
|
import Detail from './components/Detail'
|
||||||
import { useGetTransactions } from './hooks/useTransactionData'
|
import { useGetTransactions } from './hooks/useTransactionData'
|
||||||
import PageLoading from '../../components/PageLoading'
|
|
||||||
import { TransactionItemType } from './types/TransactionTypes'
|
import { TransactionItemType } from './types/TransactionTypes'
|
||||||
import moment from 'moment-jalaali'
|
import moment from 'moment-jalaali'
|
||||||
import { NumberFormat } from '../../config/func'
|
import { NumberFormat } from '../../config/func'
|
||||||
import Pagination from '../../components/Pagination'
|
import Pagination from '../../components/Pagination'
|
||||||
|
import BoxSekeleton from './skeleton/BoxSekeleton'
|
||||||
|
import DefaultTableSkeleton from '../../components/DefaultTableSkeleton'
|
||||||
|
|
||||||
const TransactionList: FC = () => {
|
const TransactionList: FC = () => {
|
||||||
|
|
||||||
@@ -22,12 +23,14 @@ const TransactionList: FC = () => {
|
|||||||
{t('transaction.transaction')}
|
{t('transaction.transaction')}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<Fragment>
|
||||||
{
|
{
|
||||||
getTransactions.isPending ?
|
getTransactions.isPending ?
|
||||||
<PageLoading />
|
<BoxSekeleton />
|
||||||
:
|
:
|
||||||
<Fragment>
|
|
||||||
<div className='mt-6 flex xl:gap-6 gap-1 justify-center'>
|
<div className='mt-6 flex xl:gap-6 gap-1 justify-center'>
|
||||||
|
|
||||||
<div className='bg-white flex-1 rounded-2xl xl:p-6 py-2 px-4 xl:max-w-[253px]'>
|
<div className='bg-white flex-1 rounded-2xl xl:p-6 py-2 px-4 xl:max-w-[253px]'>
|
||||||
<div className='flex xl:flex-row flex-col gap-2 items-center'>
|
<div className='flex xl:flex-row flex-col gap-2 items-center'>
|
||||||
<div className='size-8 bg-[#EEF0F7] rounded-full flex justify-center items-center'>
|
<div className='size-8 bg-[#EEF0F7] rounded-full flex justify-center items-center'>
|
||||||
@@ -86,6 +89,7 @@ const TransactionList: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
|
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
|
||||||
<table className='w-full text-sm '>
|
<table className='w-full text-sm '>
|
||||||
@@ -99,10 +103,14 @@ const TransactionList: FC = () => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
{
|
||||||
|
getTransactions.isPending &&
|
||||||
|
<DefaultTableSkeleton tdCount={5} />
|
||||||
|
}
|
||||||
{
|
{
|
||||||
getTransactions.data?.data?.transactions?.map((item: TransactionItemType) => {
|
getTransactions.data?.data?.transactions?.map((item: TransactionItemType) => {
|
||||||
return (
|
return (
|
||||||
<tr key={item.id}>
|
<tr className='tr' key={item.id}>
|
||||||
<Td text={item.numericId + ''} />
|
<Td text={item.numericId + ''} />
|
||||||
<Td text={item.description} />
|
<Td text={item.description} />
|
||||||
<Td text={NumberFormat(+item.amount)} />
|
<Td text={NumberFormat(+item.amount)} />
|
||||||
@@ -127,7 +135,6 @@ const TransactionList: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
}
|
|
||||||
</div >
|
</div >
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { FC } from 'react'
|
||||||
|
import Skeleton from 'react-loading-skeleton'
|
||||||
|
|
||||||
|
const BoxSekeleton: FC = () => {
|
||||||
|
return (
|
||||||
|
<div className='mt-6 flex xl:gap-6 gap-1 justify-center'>
|
||||||
|
<Skeleton
|
||||||
|
containerClassName='flex-1 xl:p-6 py-2 px-4 xl:max-w-[253px]'
|
||||||
|
className='h-[111px] !rounded-2xl'
|
||||||
|
baseColor='#D9D9D9'
|
||||||
|
/>
|
||||||
|
<Skeleton
|
||||||
|
containerClassName='flex-1 xl:p-6 py-2 px-4 xl:max-w-[253px]'
|
||||||
|
className='h-[111px] !rounded-2xl'
|
||||||
|
baseColor='#D9D9D9'
|
||||||
|
/>
|
||||||
|
<Skeleton
|
||||||
|
containerClassName='flex-1 xl:p-6 py-2 px-4 xl:max-w-[253px]'
|
||||||
|
className='h-[111px] !rounded-2xl'
|
||||||
|
baseColor='#D9D9D9'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BoxSekeleton
|
||||||
+1
-1
@@ -36,7 +36,7 @@ const MainRouter: FC = () => {
|
|||||||
<Header />
|
<Header />
|
||||||
<div className='flex-1 xl:ms-[269px] mt-[68px] xl:mt-[81px]'>
|
<div className='flex-1 xl:ms-[269px] mt-[68px] xl:mt-[81px]'>
|
||||||
<div className={`overflow-auto w-[${window.innerWidth}] max-h-[calc(100vh-113px)]`}>
|
<div className={`overflow-auto w-[${window.innerWidth}] max-h-[calc(100vh-113px)]`}>
|
||||||
<div className='pb-16'>
|
<div className='pb-20'>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path={Pages.dashboard} element={<Home />} />
|
<Route path={Pages.dashboard} element={<Home />} />
|
||||||
<Route path={Pages.services.mine} element={<MyServices />} />
|
<Route path={Pages.services.mine} element={<MyServices />} />
|
||||||
|
|||||||
Reference in New Issue
Block a user