diff --git a/frontend/src/component/admin/billing/BillingInfo/BillingInfo.tsx b/frontend/src/component/admin/billing/BillingInfo/BillingInfo.tsx index 8f03e5e851..71065c2bb6 100644 --- a/frontend/src/component/admin/billing/BillingInfo/BillingInfo.tsx +++ b/frontend/src/component/admin/billing/BillingInfo/BillingInfo.tsx @@ -7,6 +7,7 @@ import { InstanceState } from 'interfaces/instance'; import { formatApiPath } from 'utils/formatPath'; import { useDetailedInvoices } from 'hooks/api/getters/useDetailedInvoices/useDetailedInvoices'; import { formatCurrency } from '../BillingInvoices/BillingInvoice/formatCurrency.js'; +import { BillingInfoSkeleton } from './BillingInfoSkeleton.tsx'; const PORTAL_URL = formatApiPath('api/admin/invoices'); type BillingInfoProps = {}; @@ -61,11 +62,20 @@ const GetInTouch: FC = () => ( ); export const BillingInfo: FC = () => { - const { instanceStatus } = useInstanceStatus(); + const { instanceStatus, loading: instanceStatusLoading } = + useInstanceStatus(); const { uiConfig: { billing }, } = useUiConfig(); - const { planPrice, planCurrency } = useDetailedInvoices(); + const { + planPrice, + planCurrency, + loading: invoicesLoading, + } = useDetailedInvoices(); + + if (instanceStatusLoading || invoicesLoading) { + return ; + } if (!instanceStatus) { return ( diff --git a/frontend/src/component/admin/billing/BillingInfo/BillingInfoSkeleton.tsx b/frontend/src/component/admin/billing/BillingInfo/BillingInfoSkeleton.tsx new file mode 100644 index 0000000000..614a05e0f1 --- /dev/null +++ b/frontend/src/component/admin/billing/BillingInfo/BillingInfoSkeleton.tsx @@ -0,0 +1,74 @@ +import { Paper, styled } from '@mui/material'; + +const StyledWrapper = styled(Paper)(({ theme }) => ({ + padding: theme.spacing(2), + borderRadius: theme.shape.borderRadiusLarge, + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(1), +})); + +const StyledRow = styled('div')(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + fontSize: theme.typography.body2.fontSize, +})); + +const SkeletonBox = styled('div')(({ theme }) => ({ + height: theme.spacing(2.5), + backgroundColor: theme.palette.action.hover, + borderRadius: theme.spacing(0.5), + margin: theme.spacing(0.5, 0), +})); + +const SkeletonTitle = styled('div')(({ theme }) => ({ + height: theme.spacing(4), + backgroundColor: theme.palette.action.hover, + borderRadius: theme.spacing(0.5), + margin: theme.spacing(0.5, 0), + width: theme.spacing(20), +})); + +const SkeletonButton = styled('div')(({ theme }) => ({ + height: theme.spacing(5), + backgroundColor: theme.palette.action.hover, + borderRadius: theme.spacing(0.5), + margin: theme.spacing(1, 0), + width: '100%', +})); + +const SkeletonDivider = styled('div')(({ theme }) => ({ + height: theme.spacing(0.125), + backgroundColor: theme.palette.action.hover, + borderRadius: theme.spacing(0.5), + margin: theme.spacing(1.5, 0), + width: '100%', +})); + +const SkeletonText = styled('div')(({ theme }) => ({ + height: theme.spacing(2), + backgroundColor: theme.palette.action.hover, + borderRadius: theme.spacing(0.5), + margin: theme.spacing(0.5, 0), + width: theme.spacing(25), +})); + +export const BillingInfoSkeleton = () => { + return ( + + + + theme.spacing(12) }} /> + theme.spacing(8) }} /> + + + theme.spacing(10) }} /> + theme.spacing(6) }} /> + + + + + theme.spacing(20) }} /> + + ); +}; diff --git a/frontend/src/component/admin/billing/BillingInvoices/BillingInvoice/BillingInvoiceSkeleton.tsx b/frontend/src/component/admin/billing/BillingInvoices/BillingInvoice/BillingInvoiceSkeleton.tsx new file mode 100644 index 0000000000..fcb0bec38c --- /dev/null +++ b/frontend/src/component/admin/billing/BillingInvoices/BillingInvoice/BillingInvoiceSkeleton.tsx @@ -0,0 +1,83 @@ +import { + styled, + Accordion, + AccordionSummary, + AccordionDetails, +} from '@mui/material'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; + +const StyledAccordion = styled(Accordion)(({ theme }) => ({ + background: theme.palette.background.paper, + borderRadius: theme.shape.borderRadiusLarge, +})); + +const HeaderRoot = styled(AccordionSummary)(({ theme }) => ({ + padding: theme.spacing(2, 4), + gap: theme.spacing(1.5), +})); + +const HeaderLeft = styled('div')(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1.5), + flex: 1, + minWidth: 0, +})); + +const HeaderRight = styled('div')(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(2), +})); + +const SkeletonBox = styled('div')(({ theme }) => ({ + height: theme.spacing(2.5), + backgroundColor: theme.palette.action.hover, + borderRadius: theme.spacing(0.5), + margin: theme.spacing(0.5, 0), +})); + +const SkeletonBadge = styled('div')(({ theme }) => ({ + height: theme.spacing(3), + width: theme.spacing(10), + backgroundColor: theme.palette.action.hover, + borderRadius: theme.spacing(1.5), +})); + +const SkeletonAmount = styled('div')(({ theme }) => ({ + height: theme.spacing(3), + width: theme.spacing(12.5), + backgroundColor: theme.palette.action.hover, + borderRadius: theme.spacing(0.5), +})); + +const SkeletonContent = styled('div')(({ theme }) => ({ + height: theme.spacing(25), + backgroundColor: theme.palette.action.hover, + borderRadius: theme.spacing(0.5), + margin: theme.spacing(2, 4), +})); + +export const BillingInvoiceSkeleton = () => { + return ( + + }> + + theme.spacing(15), + height: (theme) => theme.spacing(4), + }} + /> + + + + + + + + + + + ); +}; diff --git a/frontend/src/component/admin/billing/BillingInvoices/BillingInvoices.tsx b/frontend/src/component/admin/billing/BillingInvoices/BillingInvoices.tsx index e5d66f86ff..fbdee7c387 100644 --- a/frontend/src/component/admin/billing/BillingInvoices/BillingInvoices.tsx +++ b/frontend/src/component/admin/billing/BillingInvoices/BillingInvoices.tsx @@ -1,8 +1,10 @@ import { Box, styled } from '@mui/material'; import type { FC } from 'react'; import { BillingInvoice } from './BillingInvoice/BillingInvoice.tsx'; +import { BillingInvoiceSkeleton } from './BillingInvoice/BillingInvoiceSkeleton.tsx'; import { useDetailedInvoices } from 'hooks/api/getters/useDetailedInvoices/useDetailedInvoices.ts'; import { TablePlaceholder } from 'component/common/Table'; +import useLoading from 'hooks/useLoading'; const StyledContainer = styled(Box)(({ theme }) => ({ display: 'flex', @@ -12,9 +14,16 @@ const StyledContainer = styled(Box)(({ theme }) => ({ export const BillingInvoices: FC = () => { const { invoices, loading } = useDetailedInvoices(); + const ref = useLoading(loading); if (loading) { - return ; + return ( + + {[1, 2, 3].map((index) => ( + + ))} + + ); } return (