diff --git a/dapp/package.json b/dapp/package.json index c8cdf2427..f5e403126 100644 --- a/dapp/package.json +++ b/dapp/package.json @@ -24,6 +24,7 @@ "@reduxjs/toolkit": "^2.2.0", "@sentry/react": "^7.98.0", "@sentry/types": "^7.102.0", + "@tabler/icons-react": "^3.1.0", "@tanstack/react-table": "^8.11.3", "@types/react-slick": "^0.23.13", "axios": "^1.6.7", diff --git a/dapp/src/acre-react/hooks/useStakeFlow.ts b/dapp/src/acre-react/hooks/useStakeFlow.ts index 169a28894..b13c3b8a5 100644 --- a/dapp/src/acre-react/hooks/useStakeFlow.ts +++ b/dapp/src/acre-react/hooks/useStakeFlow.ts @@ -67,7 +67,8 @@ export function useStakeFlow(): UseStakeFlowReturn { const stake = useCallback(async () => { if (!stakeFlow) throw new Error("Initialize stake first") - + // The current waiting time for repeat transactions is very long. + // TODO: Find the right value and pass it as additional options. await stakeFlow.stake() }, [stakeFlow]) diff --git a/dapp/src/assets/icons/Bitcoin.tsx b/dapp/src/assets/icons/BitcoinIcon.tsx similarity index 96% rename from dapp/src/assets/icons/Bitcoin.tsx rename to dapp/src/assets/icons/BitcoinIcon.tsx index c25918520..b98f2a8d3 100644 --- a/dapp/src/assets/icons/Bitcoin.tsx +++ b/dapp/src/assets/icons/BitcoinIcon.tsx @@ -1,8 +1,8 @@ import React from "react" import { createIcon } from "@chakra-ui/react" -export const Bitcoin = createIcon({ - displayName: "Bitcoin", +export const BitcoinIcon = createIcon({ + displayName: "BitcoinIcon", viewBox: "0 0 28 28", path: [ diff --git a/dapp/src/assets/icons/ConnectETHAccount.tsx b/dapp/src/assets/icons/CableWithPlugIcon.tsx similarity index 57% rename from dapp/src/assets/icons/ConnectETHAccount.tsx rename to dapp/src/assets/icons/CableWithPlugIcon.tsx index dfa2abb87..b460a843c 100644 --- a/dapp/src/assets/icons/ConnectETHAccount.tsx +++ b/dapp/src/assets/icons/CableWithPlugIcon.tsx @@ -1,8 +1,8 @@ import React from "react" import { createIcon } from "@chakra-ui/react" -export const ConnectETHAccount = createIcon({ - displayName: "ConnectETHAccount", +export const CableWithPlugIcon = createIcon({ + displayName: "CableWithPlugIcon", viewBox: "0 0 127 122", defaultProps: { fill: "none", @@ -13,35 +13,17 @@ export const ConnectETHAccount = createIcon({ stroke="url(#paint0_linear_3172_24686)" strokeWidth="2" />, - - - - - , , , , - - + + diff --git a/dapp/src/assets/icons/ConnectBTCAccount.tsx b/dapp/src/assets/icons/ConnectBTCAccount.tsx deleted file mode 100644 index 6ce8ef649..000000000 --- a/dapp/src/assets/icons/ConnectBTCAccount.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from "react" -import { createIcon } from "@chakra-ui/react" - -export const ConnectBTCAccount = createIcon({ - displayName: "ConnectBTCAccount", - viewBox: "0 0 127 122", - defaultProps: { - fill: "none", - }, - path: [ - , - - - - - , - , - , - , - , - - - - - - - - - , - ], -}) diff --git a/dapp/src/assets/icons/Ethereum.tsx b/dapp/src/assets/icons/Ethereum.tsx deleted file mode 100644 index 27cf854c6..000000000 --- a/dapp/src/assets/icons/Ethereum.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react" -import { createIcon } from "@chakra-ui/react" - -export const EthereumIcon = createIcon({ - displayName: "EthereumIcon", - viewBox: "0 0 24 25", - path: [ - - - - , - - - - - , - ], -}) diff --git a/dapp/src/assets/icons/EthereumIcon.tsx b/dapp/src/assets/icons/EthereumIcon.tsx new file mode 100644 index 000000000..633e86193 --- /dev/null +++ b/dapp/src/assets/icons/EthereumIcon.tsx @@ -0,0 +1,31 @@ +import React from "react" +import { createIcon } from "@chakra-ui/react" + +export const EthereumIcon = createIcon({ + displayName: "EthereumIcon", + viewBox: "0 0 56 57", + path: [ + + + + + , + + + + + , + ], +}) diff --git a/dapp/src/assets/icons/index.ts b/dapp/src/assets/icons/index.ts index 347ef098c..855a92790 100644 --- a/dapp/src/assets/icons/index.ts +++ b/dapp/src/assets/icons/index.ts @@ -1,12 +1,8 @@ export * from "./Info" -export * from "./Bitcoin" -export * from "./Ethereum" export * from "./ArrowUpRight" export * from "./ArrowLeft" export * from "./ArrowRight" export * from "./AcreLogo" -export * from "./ConnectBTCAccount" -export * from "./ConnectETHAccount" export * from "./AlertInfo" export * from "./stBTC" export * from "./BTC" @@ -19,3 +15,6 @@ export * from "./SortDESC" export * from "./Pause" export * from "./ChevronRight" export * from "./LoadingSpinnerSuccessIcon" +export * from "./BitcoinIcon" +export * from "./EthereumIcon" +export * from "./CableWithPlugIcon" diff --git a/dapp/src/components/Header/ConnectWallet.tsx b/dapp/src/components/Header/ConnectWallet.tsx index 26de7fbcc..9aee57748 100644 --- a/dapp/src/components/Header/ConnectWallet.tsx +++ b/dapp/src/components/Header/ConnectWallet.tsx @@ -7,7 +7,7 @@ import { } from "#/hooks" import { CurrencyBalance } from "#/components/shared/CurrencyBalance" import { TextMd } from "#/components/shared/Typography" -import { Bitcoin, EthereumIcon } from "#/assets/icons" +import { BitcoinIcon, EthereumIcon } from "#/assets/icons" import { Account } from "@ledgerhq/wallet-api-client" import { CURRENCY_ID_BITCOIN } from "#/constants" import { @@ -64,7 +64,7 @@ export default function ConnectWallet() { + + + Your funds are secure. + + + + ) +} diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakingErrorModal/ServerErrorModal.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakingErrorModal/ServerErrorModal.tsx new file mode 100644 index 000000000..ff4cc6b5e --- /dev/null +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakingErrorModal/ServerErrorModal.tsx @@ -0,0 +1,97 @@ +import React from "react" +import { + Button, + Flex, + HStack, + Icon, + Link, + ModalBody, + ModalCloseButton, + ModalFooter, + ModalHeader, + Tooltip, +} from "@chakra-ui/react" +import { CableWithPlugIcon, Info } from "#/assets/icons" +import { TextMd } from "#/components/shared/Typography" +import { EXTERNAL_HREF } from "#/constants" +import IconWrapper from "#/components/shared/IconWrapper" +import { MODAL_BASE_SIZE } from "#/components/shared/ModalBase" +import { + IconBrandDiscordFilled, + IconReload, + IconServerBolt, +} from "@tabler/icons-react" + +export default function ServerErrorModal({ + isLoading, + retry, +}: { + isLoading: boolean + retry: () => void +}) { + return ( + <> + + + We're currently facing system issues. + + + + + + + Your deposit didn't go through but no worries, your funds are + safe. + + + + + + + System status + {/* TODO: ADD a tooltip */} + + + + + Partial Outage + + + + + ) +} diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakingErrorModal/index.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakingErrorModal/index.tsx new file mode 100644 index 000000000..6ae4e1276 --- /dev/null +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakingErrorModal/index.tsx @@ -0,0 +1,50 @@ +import React, { useCallback, useState } from "react" +import { + useExecuteFunction, + useModalFlowContext, + useStakeFlowContext, +} from "#/hooks" +import { PROCESS_STATUSES } from "#/types" +import { logPromiseFailure } from "#/utils" +import ServerErrorModal from "./ServerErrorModal" +import RetryModal from "./RetryModal" +import LoadingModal from "../../LoadingModal" + +export default function StakingErrorModal() { + const { setStatus } = useModalFlowContext() + const { stake } = useStakeFlowContext() + + const [isLoading, setIsLoading] = useState(false) + const [isServerError, setIsServerError] = useState(false) + + const onStakeBTCSuccess = useCallback( + () => setStatus(PROCESS_STATUSES.SUCCEEDED), + [setStatus], + ) + + const onStakeBTCError = useCallback(() => setIsServerError(true), []) + + const handleStake = useExecuteFunction( + stake, + onStakeBTCSuccess, + onStakeBTCError, + ) + + const handleRetry = useCallback(async () => { + setIsLoading(true) + await handleStake() + setIsLoading(false) + }, [handleStake]) + + const handleRetryWrapper = useCallback( + () => logPromiseFailure(handleRetry()), + [handleRetry], + ) + + if (isServerError) + return + + if (isLoading) return + + return +} diff --git a/dapp/src/components/TransactionModal/ErrorModal.tsx b/dapp/src/components/TransactionModal/ErrorModal.tsx new file mode 100644 index 000000000..92e343c31 --- /dev/null +++ b/dapp/src/components/TransactionModal/ErrorModal.tsx @@ -0,0 +1,9 @@ +import React from "react" +import { ActionFlowType } from "#/types" +import StakingErrorModal from "./ActiveStakingStep/StakingErrorModal" + +export default function ErrorModal({ type }: { type: ActionFlowType }) { + if (type === "stake") return + // TODO: Handle the case of unstake action + return null +} diff --git a/dapp/src/components/TransactionModal/ModalContentWrapper/LoadingModal.tsx b/dapp/src/components/TransactionModal/LoadingModal.tsx similarity index 100% rename from dapp/src/components/TransactionModal/ModalContentWrapper/LoadingModal.tsx rename to dapp/src/components/TransactionModal/LoadingModal.tsx diff --git a/dapp/src/components/TransactionModal/ModalContentWrapper/MissingAccountModal.tsx b/dapp/src/components/TransactionModal/MissingAccountModal.tsx similarity index 84% rename from dapp/src/components/TransactionModal/ModalContentWrapper/MissingAccountModal.tsx rename to dapp/src/components/TransactionModal/MissingAccountModal.tsx index 826dc325f..b8826955f 100644 --- a/dapp/src/components/TransactionModal/ModalContentWrapper/MissingAccountModal.tsx +++ b/dapp/src/components/TransactionModal/MissingAccountModal.tsx @@ -12,6 +12,8 @@ import { TextMd } from "#/components/shared/Typography" import Alert from "#/components/shared/Alert" import { logPromiseFailure, getCurrencyByType } from "#/utils" import { CurrencyType, RequestAccountParams } from "#/types" +import IconWrapper from "#/components/shared/IconWrapper" +import { CableWithPlugIcon } from "#/assets/icons" type MissingAccountModalProps = { currency: CurrencyType @@ -34,8 +36,10 @@ export default function MissingAccountModal({ <> {name} account not installed - - + + + + {name} account is required to make transactions for depositing and staking your {symbol}. diff --git a/dapp/src/components/TransactionModal/ModalContentWrapper/index.tsx b/dapp/src/components/TransactionModal/ModalContentWrapper.tsx similarity index 88% rename from dapp/src/components/TransactionModal/ModalContentWrapper/index.tsx rename to dapp/src/components/TransactionModal/ModalContentWrapper.tsx index 3e06e68c7..33c94bdbe 100644 --- a/dapp/src/components/TransactionModal/ModalContentWrapper/index.tsx +++ b/dapp/src/components/TransactionModal/ModalContentWrapper.tsx @@ -6,14 +6,15 @@ import { useTransactionContext, useWalletContext, } from "#/hooks" -import { ConnectBTCAccount, ConnectETHAccount } from "#/assets/icons" +import { BitcoinIcon, EthereumIcon } from "#/assets/icons" import { ActionFlowType, PROCESS_STATUSES } from "#/types" import { isSupportedBTCAddressType } from "#/utils" import ActionFormModal from "./ActionFormModal" +import ErrorModal from "./ErrorModal" +import LoadingModal from "./LoadingModal" import MissingAccountModal from "./MissingAccountModal" import ResumeModal from "./ResumeModal" import SuccessModal from "./SuccessModal" -import LoadingModal from "./LoadingModal" export default function ModalContentWrapper({ defaultType, @@ -32,7 +33,7 @@ export default function ModalContentWrapper({ return ( ) @@ -41,7 +42,7 @@ export default function ModalContentWrapper({ return ( ) @@ -56,5 +57,7 @@ export default function ModalContentWrapper({ if (status === PROCESS_STATUSES.SUCCEEDED) return + if (status === PROCESS_STATUSES.FAILED) return + return children } diff --git a/dapp/src/components/TransactionModal/ModalContentWrapper/ResumeModal.tsx b/dapp/src/components/TransactionModal/ResumeModal.tsx similarity index 100% rename from dapp/src/components/TransactionModal/ModalContentWrapper/ResumeModal.tsx rename to dapp/src/components/TransactionModal/ResumeModal.tsx diff --git a/dapp/src/components/TransactionModal/ModalContentWrapper/SuccessModal.tsx b/dapp/src/components/TransactionModal/SuccessModal.tsx similarity index 100% rename from dapp/src/components/TransactionModal/ModalContentWrapper/SuccessModal.tsx rename to dapp/src/components/TransactionModal/SuccessModal.tsx diff --git a/dapp/src/components/shared/IconWrapper.tsx b/dapp/src/components/shared/IconWrapper.tsx new file mode 100644 index 000000000..fc35fcb42 --- /dev/null +++ b/dapp/src/components/shared/IconWrapper.tsx @@ -0,0 +1,20 @@ +import React from "react" +import { Box, HStack, Icon, IconProps } from "@chakra-ui/react" + +type IconWrapperProps = { + icon: typeof Icon + children: React.ReactNode +} & IconProps + +export default function IconWrapper({ + icon, + children, + ...props +}: IconWrapperProps) { + return ( + + + {children} + + ) +} diff --git a/dapp/src/components/shared/ModalBase/index.tsx b/dapp/src/components/shared/ModalBase/index.tsx index f8e1ad3d0..13731579c 100644 --- a/dapp/src/components/shared/ModalBase/index.tsx +++ b/dapp/src/components/shared/ModalBase/index.tsx @@ -1,9 +1,11 @@ import React from "react" import { Modal, ModalContent, ModalOverlay, ModalProps } from "@chakra-ui/react" +export const MODAL_BASE_SIZE = "lg" + export default function ModalBase({ children, ...restProps }: ModalProps) { return ( - + {children} diff --git a/dapp/src/constants/externalHref.ts b/dapp/src/constants/externalHref.ts new file mode 100644 index 000000000..cdf77bfcf --- /dev/null +++ b/dapp/src/constants/externalHref.ts @@ -0,0 +1,4 @@ +export const EXTERNAL_HREF = { + // TODO: Add a correct link + DISCORD: "https://discord.com/", +} diff --git a/dapp/src/constants/index.ts b/dapp/src/constants/index.ts index 920e2c38b..653ef4777 100644 --- a/dapp/src/constants/index.ts +++ b/dapp/src/constants/index.ts @@ -2,3 +2,4 @@ export * from "./currency" export * from "./staking" export * from "./chains" export * from "./time" +export * from "./externalHref" diff --git a/dapp/src/hooks/index.ts b/dapp/src/hooks/index.ts index 2c6a4c3db..8cbe7c13b 100644 --- a/dapp/src/hooks/index.ts +++ b/dapp/src/hooks/index.ts @@ -17,6 +17,7 @@ export * from "./useInitApp" export * from "./useCurrencyConversion" export * from "./useDepositTelemetry" export * from "./useFetchBTCPriceUSD" +export * from "./useCountdown" export * from "./useActivities" export * from "./useFetchBTCBalance" export * from "./useSize" diff --git a/dapp/src/hooks/useCountdown.ts b/dapp/src/hooks/useCountdown.ts new file mode 100644 index 000000000..69a9c91a2 --- /dev/null +++ b/dapp/src/hooks/useCountdown.ts @@ -0,0 +1,56 @@ +import { useState, useEffect } from "react" +import { TimeUnits } from "#/types" +import { ONE_SEC_IN_MILLISECONDS } from "#/constants" +import { + dateToUnixTimestamp, + unixTimestampToTimeUnits, + addLeadingZero, +} from "#/utils" + +/** + * It was decided to use an already implemented hook used by Threshold Network. + * Hook allows us to count down the time until the specified date and. + * After time has passed we can call the specified action. + * + * Source: + * https://github.com/threshold-network/components/blob/main/src/hooks/useCountdown.ts + */ +export const useCountdown = ( + targetDateInUnix: number, + addLeadingZeroes?: boolean, + onComplete?: (targetDateInUnix: number) => void, +): TimeUnits => { + const [diff, setDiff] = useState(targetDateInUnix - dateToUnixTimestamp()) + + useEffect(() => { + const countdownInterval = setInterval(() => { + const newDiff = targetDateInUnix - dateToUnixTimestamp() + if (newDiff === 0) { + if (onComplete) { + onComplete(targetDateInUnix) + } + clearInterval(countdownInterval) + } + + setDiff(newDiff) + }, ONE_SEC_IN_MILLISECONDS) + + return () => clearInterval(countdownInterval) + }, [targetDateInUnix, onComplete]) + + let { days, hours, minutes, seconds } = unixTimestampToTimeUnits(diff) + + if (addLeadingZeroes) { + days = addLeadingZero(Number(days)) + hours = addLeadingZero(Number(hours)) + minutes = addLeadingZero(Number(minutes)) + seconds = addLeadingZero(Number(seconds)) + } + + return { + days, + hours, + minutes, + seconds, + } +} diff --git a/dapp/src/theme/Button.ts b/dapp/src/theme/Button.ts index e318c7032..3174f12df 100644 --- a/dapp/src/theme/Button.ts +++ b/dapp/src/theme/Button.ts @@ -39,6 +39,13 @@ export const buttonTheme: ComponentSingleStyleConfig = { _active: { bg: "transparent", }, + _loading: { + _disabled: { + borderColor: "white", + background: "opacity.white.5", + opacity: 1, + }, + }, } if (colorScheme === "gold") { return { diff --git a/dapp/src/theme/Modal.ts b/dapp/src/theme/Modal.ts index 027c9946b..2c2d1d158 100644 --- a/dapp/src/theme/Modal.ts +++ b/dapp/src/theme/Modal.ts @@ -3,7 +3,7 @@ import { createMultiStyleConfigHelpers, defineStyle } from "@chakra-ui/react" const baseStyleDialog = defineStyle({ p: 4, - border: "2px", + borderWidth: "var(--chakra-space-modal_borderWidth)", boxShadow: "none", borderColor: "white", borderRadius: "xl", @@ -40,6 +40,11 @@ const baseStyleBody = defineStyle({ gap: 6, }) +const baseStyleFooter = defineStyle({ + flexDirection: "column", + gap: 6, +}) + const multiStyleConfig = createMultiStyleConfigHelpers(parts.keys) const baseStyle = multiStyleConfig.definePartsStyle({ @@ -48,6 +53,7 @@ const baseStyle = multiStyleConfig.definePartsStyle({ overlay: baseStyleOverlay, header: baseStyleHeader, body: baseStyleBody, + footer: baseStyleFooter, }) export const modalTheme = multiStyleConfig.defineMultiStyleConfig({ baseStyle }) diff --git a/dapp/src/theme/utils/semanticTokens.ts b/dapp/src/theme/utils/semanticTokens.ts index f58e618c6..ccdf4561b 100644 --- a/dapp/src/theme/utils/semanticTokens.ts +++ b/dapp/src/theme/utils/semanticTokens.ts @@ -2,6 +2,7 @@ export const semanticTokens = { space: { header_height: 24, modal_shift: 36, + modal_borderWidth: "2px", }, sizes: { sidebar_width: 80, diff --git a/dapp/src/types/index.ts b/dapp/src/types/index.ts index 53aaf17ed..1c99facdd 100644 --- a/dapp/src/types/index.ts +++ b/dapp/src/types/index.ts @@ -10,4 +10,5 @@ export * from "./location" export * from "./charts" export * from "./activity" export * from "./coingecko" +export * from "./time" export * from "./size" diff --git a/dapp/src/types/time.ts b/dapp/src/types/time.ts new file mode 100644 index 000000000..888fd0ffa --- /dev/null +++ b/dapp/src/types/time.ts @@ -0,0 +1,6 @@ +export type TimeUnits = { + days: string + hours: string + minutes: string + seconds: string +} diff --git a/dapp/src/utils/numbers.ts b/dapp/src/utils/numbers.ts index 70fa2c307..9cf07bcbf 100644 --- a/dapp/src/utils/numbers.ts +++ b/dapp/src/utils/numbers.ts @@ -206,3 +206,9 @@ export function getDesiredDecimals(amount: string | number, decimals: number) { const desiredDecimals = decimals - undecimaledAmount.length + 1 return desiredDecimals > 0 ? desiredDecimals : 2 } + +export const addLeadingZero = (num: number): string => + num >= 0 && num <= 9 ? `0${num}` : `${num}` + +export const getPercentValue = (value: number, maxValue: number) => + (value * 100) / maxValue diff --git a/dapp/src/utils/time.ts b/dapp/src/utils/time.ts index 30e6da46e..5fb59284c 100644 --- a/dapp/src/utils/time.ts +++ b/dapp/src/utils/time.ts @@ -7,10 +7,29 @@ import { ONE_WEEK_IN_SECONDS, ONE_YEAR_IN_SECONDS, } from "#/constants" +import { TimeUnits } from "#/types" export const dateToUnixTimestamp = (date: Date = new Date()) => Math.floor(date.getTime() / ONE_SEC_IN_MILLISECONDS) +export const unixTimestampToTimeUnits = (targetUnix: number): TimeUnits => { + const days = Math.floor(targetUnix / ONE_DAY_IN_SECONDS) + const hours = Math.floor( + (targetUnix % ONE_DAY_IN_SECONDS) / ONE_HOUR_IN_SECONDS, + ) + const minutes = Math.floor( + (targetUnix % ONE_HOUR_IN_SECONDS) / ONE_MINUTE_IN_SECONDS, + ) + const seconds = Math.floor(targetUnix % ONE_MINUTE_IN_SECONDS) + + return { + days: days.toString(), + hours: hours.toString(), + minutes: minutes.toString(), + seconds: seconds.toString(), + } +} + // unit, max diff, divisor const unitsToDivisor: [Intl.RelativeTimeFormatUnit, number, number][] = [ ["second", ONE_MINUTE_IN_SECONDS, 1], @@ -60,3 +79,14 @@ export const displayBlockTimestamp = (blockTimestamp: number) => { return getRelativeTime(blockTimestamp) } + +/** + * Returns the expiration timestamp from the start date considering the specified duration. + * If the startDate is not passed, the function will take the current time as the start date. + */ +export const getExpirationTimestamp = (duration: number, startDate?: Date) => { + const date = startDate ?? new Date() + const expirationDate = new Date(date.getTime() + duration) + + return dateToUnixTimestamp(expirationDate) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a58ff6a75..307077ecd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: '@sentry/types': specifier: ^7.102.0 version: 7.102.0 + '@tabler/icons-react': + specifier: ^3.1.0 + version: 3.1.0(react@18.2.0) '@tanstack/react-table': specifier: ^8.11.3 version: 8.11.7(react-dom@18.2.0)(react@18.2.0) @@ -6736,6 +6739,19 @@ packages: dependencies: defer-to-connect: 2.0.1 + /@tabler/icons-react@3.1.0(react@18.2.0): + resolution: {integrity: sha512-k/WTlax2vbj/LpxvaJ+BmaLAAhVUgyLj4Ftgaczz66tUSNzqrAZXCFdOU7cRMYPNVBqyqE2IdQd2rzzhDEJvkw==} + peerDependencies: + react: '>= 16' + dependencies: + '@tabler/icons': 3.1.0 + react: 18.2.0 + dev: false + + /@tabler/icons@3.1.0: + resolution: {integrity: sha512-CpZGyS1IVJKFcv88yZ2sYZIpWWhQ6oy76BQKQ5SF0fGgOqgyqKdBGG/YGyyMW632on37MX7VqQIMTzN/uQqmFg==} + dev: false + /@tanstack/react-table@8.11.7(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-ZbzfMkLjxUTzNPBXJYH38pv2VpC9WUA+Qe5USSHEBz0dysDTv4z/ARI3csOed/5gmlmrPzVUN3UXGuUMbod3Jg==} engines: {node: '>=12'}