diff --git a/apps/tangle-dapp/app/blueprints/loading.tsx b/apps/tangle-dapp/app/blueprints/loading.tsx
new file mode 100644
index 0000000000..0621a59da8
--- /dev/null
+++ b/apps/tangle-dapp/app/blueprints/loading.tsx
@@ -0,0 +1,24 @@
+import TopBanner from '@webb-tools/tangle-shared-ui/components/blueprints/TopBanner';
+import SkeletonLoader from '@webb-tools/webb-ui-components/components/SkeletonLoader';
+import type { FC } from 'react';
+import { twMerge } from 'tailwind-merge';
+
+const LoadingPage: FC = () => {
+ return (
+
+
+
+
+ {Array.from({ length: 6 }).map((_, idx) => (
+
+ ))}
+
+
+ );
+};
+
+export default LoadingPage;
diff --git a/apps/tangle-dapp/app/bridge/loading.tsx b/apps/tangle-dapp/app/bridge/loading.tsx
new file mode 100644
index 0000000000..df44dd05db
--- /dev/null
+++ b/apps/tangle-dapp/app/bridge/loading.tsx
@@ -0,0 +1,64 @@
+import { ArrowRight } from '@webb-tools/icons/ArrowRight';
+import Button from '@webb-tools/webb-ui-components/components/buttons/Button';
+import { Label } from '@webb-tools/webb-ui-components/components/Label';
+import SkeletonLoader from '@webb-tools/webb-ui-components/components/SkeletonLoader';
+import { type FC } from 'react';
+import { twMerge } from 'tailwind-merge';
+
+const Loading: FC = () => {
+ // TODO: Using the container style when PR https://github.com/tangle-network/dapp/pull/2664 is merged
+ return (
+
+
+
+ {/* Source Chain Selector */}
+
+
+
+
+
+
+
+
+ {/* Destination Chain Selector */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Loading;
diff --git a/apps/tangle-dapp/app/liquid-staking/loading.tsx b/apps/tangle-dapp/app/liquid-staking/loading.tsx
new file mode 100644
index 0000000000..8256c9a9d9
--- /dev/null
+++ b/apps/tangle-dapp/app/liquid-staking/loading.tsx
@@ -0,0 +1,14 @@
+import SkeletonLoader from '@webb-tools/webb-ui-components/components/SkeletonLoader';
+import type { FC } from 'react';
+
+const LoadingPage: FC = () => {
+ return (
+
+
+
+
+
+ );
+};
+
+export default LoadingPage;
diff --git a/apps/tangle-dapp/app/restake/AnimatedPageWrapper.tsx b/apps/tangle-dapp/app/restake/AnimatedPageWrapper.tsx
new file mode 100644
index 0000000000..eaa2ad1ba8
--- /dev/null
+++ b/apps/tangle-dapp/app/restake/AnimatedPageWrapper.tsx
@@ -0,0 +1,29 @@
+'use client';
+
+import { HTMLMotionProps, motion } from 'framer-motion';
+import { forwardRef, PropsWithChildren } from 'react';
+
+export type AnimatedPageWrapperProps = HTMLMotionProps<'div'>;
+
+const AnimatedPageWrapper = forwardRef<
+ HTMLDivElement,
+ PropsWithChildren
+>(({ children, ...props }, ref) => {
+ return (
+
+ {children}
+
+ );
+});
+
+AnimatedPageWrapper.displayName = 'AnimatedPageWrapper';
+
+export default AnimatedPageWrapper;
diff --git a/apps/tangle-dapp/app/restake/AnimatedTable.tsx b/apps/tangle-dapp/app/restake/AnimatedTable.tsx
new file mode 100644
index 0000000000..08eeeace7a
--- /dev/null
+++ b/apps/tangle-dapp/app/restake/AnimatedTable.tsx
@@ -0,0 +1,34 @@
+import cx from 'classnames';
+import { AnimatePresence, motion } from 'framer-motion';
+import type { PropsWithChildren } from 'react';
+
+export type AnimatedTableProps = PropsWithChildren & {
+ isTableOpen?: boolean;
+ isMediumScreen?: boolean;
+};
+
+export function AnimatedTable({
+ children,
+ isMediumScreen,
+ isTableOpen,
+}: AnimatedTableProps) {
+ return (
+
+ {(!isMediumScreen || isTableOpen) && (
+
+ {children}
+
+ )}
+
+ );
+}
diff --git a/apps/tangle-dapp/app/restake/ExpandTableButton.tsx b/apps/tangle-dapp/app/restake/ExpandTableButton.tsx
new file mode 100644
index 0000000000..7268d87aec
--- /dev/null
+++ b/apps/tangle-dapp/app/restake/ExpandTableButton.tsx
@@ -0,0 +1,70 @@
+import { DoubleArrowRightIcon } from '@radix-ui/react-icons';
+import { TooltipTrigger } from '@radix-ui/react-tooltip';
+import IconButton from '@webb-tools/webb-ui-components/components/buttons/IconButton';
+import {
+ Tooltip,
+ TooltipBody,
+} from '@webb-tools/webb-ui-components/components/Tooltip';
+import type { ComponentProps, ReactNode } from 'react';
+import { twMerge } from 'tailwind-merge';
+
+export enum NotificationVariant {
+ PENDING = 'pending',
+ SUCCESS = 'success',
+}
+
+export type ExpandTableButtonProps = ComponentProps<'button'> & {
+ notificationVariant?: NotificationVariant;
+ tooltipContent?: ReactNode;
+};
+
+const colorClasses = {
+ [NotificationVariant.PENDING]: {
+ back: twMerge('bg-amber-400'),
+ front: twMerge('bg-amber-500'),
+ },
+ [NotificationVariant.SUCCESS]: {
+ back: twMerge('bg-green-400'),
+ front: twMerge('bg-green-500'),
+ },
+} as const satisfies Record<
+ NotificationVariant,
+ { back: string; front: string }
+>;
+
+export function ExpandTableButton({
+ notificationVariant,
+ tooltipContent,
+ ...props
+}: ExpandTableButtonProps) {
+ return (
+
+
+
+
+
+ {notificationVariant && (
+ <>
+
+
+
+
+ >
+ )}
+
+
+
+ {tooltipContent}
+
+ );
+}
diff --git a/apps/tangle-dapp/app/restake/LoadingPage.tsx b/apps/tangle-dapp/app/restake/LoadingPage.tsx
new file mode 100644
index 0000000000..d48fa1f676
--- /dev/null
+++ b/apps/tangle-dapp/app/restake/LoadingPage.tsx
@@ -0,0 +1,39 @@
+import Button from '@webb-tools/webb-ui-components/components/buttons/Button';
+import { Card } from '@webb-tools/webb-ui-components/components/Card';
+import SkeletonLoader from '@webb-tools/webb-ui-components/components/SkeletonLoader';
+import { FC } from 'react';
+
+import RestakeTabs from './RestakeTabs';
+import StyleContainer from './StyleContainer';
+
+const LoadingPage: FC = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default LoadingPage;
diff --git a/apps/tangle-dapp/app/restake/StyleContainer.tsx b/apps/tangle-dapp/app/restake/StyleContainer.tsx
new file mode 100644
index 0000000000..b0edef2561
--- /dev/null
+++ b/apps/tangle-dapp/app/restake/StyleContainer.tsx
@@ -0,0 +1,21 @@
+import { ComponentProps, forwardRef, PropsWithChildren } from 'react';
+import { twMerge } from 'tailwind-merge';
+
+const StyleContainer = forwardRef<
+ HTMLDivElement,
+ PropsWithChildren>
+>(({ children, className, ...props }, ref) => {
+ return (
+
+ {children}
+
+ );
+});
+
+StyleContainer.displayName = 'StyleContainer';
+
+export default StyleContainer;
diff --git a/apps/tangle-dapp/app/restake/deposit/layout.tsx b/apps/tangle-dapp/app/restake/deposit/layout.tsx
deleted file mode 100644
index 64ad4ce10e..0000000000
--- a/apps/tangle-dapp/app/restake/deposit/layout.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import React, { PropsWithChildren } from 'react';
-
-import RestakeTabs from '../RestakeTabs';
-
-export const dynamic = 'force-static';
-
-const layout = ({ children }: PropsWithChildren) => {
- return (
-
-
-
- {children}
-
- );
-};
-
-export default layout;
diff --git a/apps/tangle-dapp/app/restake/deposit/loading.tsx b/apps/tangle-dapp/app/restake/deposit/loading.tsx
new file mode 100644
index 0000000000..0079e3b575
--- /dev/null
+++ b/apps/tangle-dapp/app/restake/deposit/loading.tsx
@@ -0,0 +1,7 @@
+import LoadingPage from '../LoadingPage';
+
+const Page = () => {
+ return ;
+};
+
+export default Page;
diff --git a/apps/tangle-dapp/app/restake/deposit/page.tsx b/apps/tangle-dapp/app/restake/deposit/page.tsx
index c54915d55a..c710cfa6ff 100644
--- a/apps/tangle-dapp/app/restake/deposit/page.tsx
+++ b/apps/tangle-dapp/app/restake/deposit/page.tsx
@@ -1,7 +1,14 @@
+import RestakeTabs from '../RestakeTabs';
+import StyleContainer from '../StyleContainer';
import DepositForm from './DepositForm';
export const dynamic = 'force-static';
export default function DepositPage() {
- return ;
+ return (
+
+
+
+
+ );
}
diff --git a/apps/tangle-dapp/app/restake/OperatorsTable.tsx b/apps/tangle-dapp/app/restake/overview/OperatorsTable.tsx
similarity index 89%
rename from apps/tangle-dapp/app/restake/OperatorsTable.tsx
rename to apps/tangle-dapp/app/restake/overview/OperatorsTable.tsx
index bed4dd47a7..bac7ffa607 100644
--- a/apps/tangle-dapp/app/restake/OperatorsTable.tsx
+++ b/apps/tangle-dapp/app/restake/overview/OperatorsTable.tsx
@@ -5,10 +5,10 @@ import type { OperatorMap } from '@webb-tools/tangle-shared-ui/types/restake';
import { Input } from '@webb-tools/webb-ui-components/components/Input';
import { type ComponentProps, type FC, useMemo, useState } from 'react';
-import OperatorsTableUI from '../../components/tables/Operators';
-import { useRestakeContext } from '../../context/RestakeContext';
-import useIdentities from '../../data/useIdentities';
-import { delegationsToVaultTokens } from './utils';
+import OperatorsTableUI from '../../../components/tables/Operators';
+import { useRestakeContext } from '../../../context/RestakeContext';
+import useIdentities from '../../../data/useIdentities';
+import { delegationsToVaultTokens } from '../utils';
type OperatorUI = NonNullable<
ComponentProps['data']
diff --git a/apps/tangle-dapp/app/restake/TableTabs.tsx b/apps/tangle-dapp/app/restake/overview/TableTabs.tsx
similarity index 92%
rename from apps/tangle-dapp/app/restake/TableTabs.tsx
rename to apps/tangle-dapp/app/restake/overview/TableTabs.tsx
index 1ee8d605c3..bcee7038af 100644
--- a/apps/tangle-dapp/app/restake/TableTabs.tsx
+++ b/apps/tangle-dapp/app/restake/overview/TableTabs.tsx
@@ -6,11 +6,11 @@ import { TableAndChartTabs } from '@webb-tools/webb-ui-components/components/Tab
import { TabContent } from '@webb-tools/webb-ui-components/components/Tabs/TabContent';
import { type ComponentProps, type FC, useMemo } from 'react';
-import VaultAssetsTable from '../../components/tables/VaultAssets';
-import VaultsTable from '../../components/tables/Vaults';
-import { useRestakeContext } from '../../context/RestakeContext';
-import useRestakeRewardConfig from '../../data/restake/useRestakeRewardConfig';
-import type { DelegatorInfo } from '../../types/restake';
+import VaultAssetsTable from '../../../components/tables/VaultAssets';
+import VaultsTable from '../../../components/tables/Vaults';
+import { useRestakeContext } from '../../../context/RestakeContext';
+import useRestakeRewardConfig from '../../../data/restake/useRestakeRewardConfig';
+import type { DelegatorInfo } from '../../../types/restake';
import OperatorsTable from './OperatorsTable';
const RESTAKE_VAULTS_TAB = 'Restake Vaults';
diff --git a/apps/tangle-dapp/app/restake/overview/loading.tsx b/apps/tangle-dapp/app/restake/overview/loading.tsx
new file mode 100644
index 0000000000..6fad6a447d
--- /dev/null
+++ b/apps/tangle-dapp/app/restake/overview/loading.tsx
@@ -0,0 +1,73 @@
+import Button from '@webb-tools/webb-ui-components/components/buttons/Button';
+import {
+ Card,
+ CardVariant,
+} from '@webb-tools/webb-ui-components/components/Card';
+import SkeletonLoader from '@webb-tools/webb-ui-components/components/SkeletonLoader';
+import { TANGLE_DOCS_RESTAKING_URL } from '@webb-tools/webb-ui-components/constants';
+import { Typography } from '@webb-tools/webb-ui-components/typography/Typography';
+import type { FC } from 'react';
+import { twMerge } from 'tailwind-merge';
+
+import { CONTENT } from './shared';
+
+const LoadingPage: FC = () => {
+ return (
+
+
+
+
+ {CONTENT.OVERVIEW}
+
+
+
+
+
+
+
+
+
+
+
+
+ How it works
+
+
+ {CONTENT.HOW_IT_WORKS}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default LoadingPage;
diff --git a/apps/tangle-dapp/app/restake/overview/page.tsx b/apps/tangle-dapp/app/restake/overview/page.tsx
new file mode 100644
index 0000000000..2ed5deffe1
--- /dev/null
+++ b/apps/tangle-dapp/app/restake/overview/page.tsx
@@ -0,0 +1,106 @@
+'use client';
+
+import useRestakeOperatorMap from '@webb-tools/tangle-shared-ui/data/restake/useRestakeOperatorMap';
+import Button from '@webb-tools/webb-ui-components/components/buttons/Button';
+import {
+ Card,
+ CardVariant,
+} from '@webb-tools/webb-ui-components/components/Card';
+import { TANGLE_DOCS_RESTAKING_URL } from '@webb-tools/webb-ui-components/constants';
+import { Typography } from '@webb-tools/webb-ui-components/typography/Typography';
+import { twMerge } from 'tailwind-merge';
+
+import StatItem from '../../../components/StatItem';
+import useRestakeDelegatorInfo from '../../../data/restake/useRestakeDelegatorInfo';
+import useRestakeTVL from '../../../data/restake/useRestakeTVL';
+import getTVLToDisplay from '../../../utils/getTVLToDisplay';
+import { CONTENT } from './shared';
+import TableTabs from './TableTabs';
+
+export const dynamic = 'force-static';
+
+export default function RestakePage() {
+ const { delegatorInfo } = useRestakeDelegatorInfo();
+ const { operatorMap } = useRestakeOperatorMap();
+
+ const {
+ delegatorTVL,
+ operatorConcentration,
+ operatorTVL,
+ vaultTVL,
+ totalDelegatorTVL,
+ totalNetworkTVL,
+ } = useRestakeTVL(operatorMap, delegatorInfo);
+
+ return (
+ <>
+
+
+
+
+ {CONTENT.OVERVIEW}
+
+
+
+
+
+
+
+
+
+
+
+
+ How it works
+
+
+ {CONTENT.HOW_IT_WORKS}
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/apps/tangle-dapp/app/restake/overview/shared.tsx b/apps/tangle-dapp/app/restake/overview/shared.tsx
new file mode 100644
index 0000000000..b320b831fa
--- /dev/null
+++ b/apps/tangle-dapp/app/restake/overview/shared.tsx
@@ -0,0 +1,10 @@
+export const CONTENT = {
+ OVERVIEW: (
+ <>
+ Operators on Tangle provide computation resources to power AVS Blueprints.
+ Deposit and Delegate liquidity to earn yields.
+ >
+ ),
+ HOW_IT_WORKS:
+ 'Tangle combines restaking with omnichain assets to provide a multi-asset crypto-economically secured compute infrastructure.',
+} as const;
diff --git a/apps/tangle-dapp/app/restake/page.tsx b/apps/tangle-dapp/app/restake/page.tsx
index 9849306f87..b1a11cef4f 100644
--- a/apps/tangle-dapp/app/restake/page.tsx
+++ b/apps/tangle-dapp/app/restake/page.tsx
@@ -1,122 +1,9 @@
-'use client';
+import { redirect } from 'next/navigation';
-import useRestakeOperatorMap from '@webb-tools/tangle-shared-ui/data/restake/useRestakeOperatorMap';
-import {
- Card,
- CardVariant,
- TANGLE_DOCS_RESTAKING_URL,
-} from '@webb-tools/webb-ui-components';
-import Button from '@webb-tools/webb-ui-components/components/buttons/Button';
-import { Typography } from '@webb-tools/webb-ui-components/typography/Typography';
-import { twMerge } from 'tailwind-merge';
-
-import StatItem from '../../components/StatItem';
-import useRestakeDelegatorInfo from '../../data/restake/useRestakeDelegatorInfo';
-import useRestakeTVL from '../../data/restake/useRestakeTVL';
-import getTVLToDisplay from '../../utils/getTVLToDisplay';
-import TableTabs from './TableTabs';
+import { PagePath } from '../../types';
export const dynamic = 'force-static';
-const CONTENT = {
- OVERVIEW: (
- <>
- Operators on Tangle provide computation resources to power AVS Blueprints.
- Deposit and Delegate liquidity to earn yields.
- >
- ),
- HOW_IT_WORKS:
- 'Tangle combines restaking with omnichain assets to provide a multi-asset crypto-economically secured compute infrastructure.',
-} as const;
-
-const minHeightClsx = 'min-h-[233px]';
-
export default function RestakePage() {
- const { delegatorInfo } = useRestakeDelegatorInfo();
- const { operatorMap } = useRestakeOperatorMap();
-
- const {
- delegatorTVL,
- operatorConcentration,
- operatorTVL,
- vaultTVL,
- totalDelegatorTVL,
- totalNetworkTVL,
- } = useRestakeTVL(operatorMap, delegatorInfo);
-
- return (
- <>
-
-
-
-
- {CONTENT.OVERVIEW}
-
-
-
-
-
-
-
-
-
-
-
-
- How it works
-
-
- {CONTENT.HOW_IT_WORKS}
-
-
-
-
-
-
-
-
- >
- );
+ return redirect(PagePath.RESTAKE_OVERVIEW);
}
diff --git a/apps/tangle-dapp/app/restake/stake/Info.tsx b/apps/tangle-dapp/app/restake/stake/Info.tsx
index dda7d15367..e1f626fb2f 100644
--- a/apps/tangle-dapp/app/restake/stake/Info.tsx
+++ b/apps/tangle-dapp/app/restake/stake/Info.tsx
@@ -1,34 +1,35 @@
import isDefined from '@webb-tools/dapp-types/utils/isDefined';
-import FeeDetails from '@webb-tools/webb-ui-components/components/FeeDetails';
import { memo } from 'react';
+import DetailsContainer from '../../../components/DetailsContainer';
+import DetailItem from '../../../components/LiquidStaking/stakeAndUnstake/DetailItem';
import useRestakeConsts from '../../../data/restake/useRestakeConsts';
const Info = memo(() => {
const { leaveDelegatorsDelay, delegationBondLessDelay } = useRestakeConsts();
return (
-
+
+
+
+
+
);
});
diff --git a/apps/tangle-dapp/app/restake/stake/layout.tsx b/apps/tangle-dapp/app/restake/stake/layout.tsx
deleted file mode 100644
index 64ad4ce10e..0000000000
--- a/apps/tangle-dapp/app/restake/stake/layout.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import React, { PropsWithChildren } from 'react';
-
-import RestakeTabs from '../RestakeTabs';
-
-export const dynamic = 'force-static';
-
-const layout = ({ children }: PropsWithChildren) => {
- return (
-
-
-
- {children}
-
- );
-};
-
-export default layout;
diff --git a/apps/tangle-dapp/app/restake/stake/loading.tsx b/apps/tangle-dapp/app/restake/stake/loading.tsx
new file mode 100644
index 0000000000..0079e3b575
--- /dev/null
+++ b/apps/tangle-dapp/app/restake/stake/loading.tsx
@@ -0,0 +1,7 @@
+import LoadingPage from '../LoadingPage';
+
+const Page = () => {
+ return ;
+};
+
+export default Page;
diff --git a/apps/tangle-dapp/app/restake/stake/page.tsx b/apps/tangle-dapp/app/restake/stake/page.tsx
index 70a78f4403..58bf694c77 100644
--- a/apps/tangle-dapp/app/restake/stake/page.tsx
+++ b/apps/tangle-dapp/app/restake/stake/page.tsx
@@ -42,6 +42,8 @@ import type { DelegationFormFields } from '../../../types/restake';
import AssetList from '../AssetList';
import Form from '../Form';
import ModalContent from '../ModalContent';
+import RestakeTabs from '../RestakeTabs';
+import StyleContainer from '../StyleContainer';
import SupportedChainModal from '../SupportedChainModal';
import useSwitchChain from '../useSwitchChain';
import ActionButton from './ActionButton';
@@ -253,70 +255,74 @@ export default function Page() {
);
return (
-
-