create-openfort 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.js +16 -16
- package/package.json +1 -1
- package/template/backend/package.json +7 -1
- package/template/backend/pnpm-lock.yaml +187 -33
- package/template/backend/src/app.ts +1 -1
- package/template/openfort-templates/firebase/biome.json +3 -3
- package/template/openfort-templates/firebase/package.json +1 -1
- package/template/openfort-templates/firebase/src/App.tsx +10 -10
- package/template/openfort-templates/firebase/src/components/cards/head.tsx +157 -145
- package/template/openfort-templates/firebase/src/components/cards/main.tsx +63 -52
- package/template/openfort-templates/firebase/src/components/ui/Sheet.tsx +14 -16
- package/template/openfort-templates/firebase/src/components/ui/Tabs.tsx +46 -36
- package/template/openfort-templates/firebase/src/components/ui/TruncateData.tsx +14 -8
- package/template/openfort-templates/firebase/src/integrations/firebase/client.ts +6 -6
- package/template/openfort-templates/firebase/src/integrations/firebase/components/FirebaseAuthCard.tsx +74 -46
- package/template/openfort-templates/firebase/src/integrations/firebase/errors.ts +57 -37
- package/template/openfort-templates/firebase/src/integrations/firebase/index.ts +3 -3
- package/template/openfort-templates/firebase/src/integrations/openfort/index.ts +1 -1
- package/template/openfort-templates/firebase/src/integrations/openfort/providers.tsx +23 -11
- package/template/openfort-templates/firebase/src/ui/openfort/blockchain/ActionsCard.tsx +47 -35
- package/template/openfort-templates/firebase/src/ui/openfort/blockchain/SignCard.tsx +30 -28
- package/template/openfort-templates/firebase/src/ui/openfort/index.ts +4 -6
- package/template/openfort-templates/firebase/src/ui/openfort/profile/UserProfileCard.tsx +32 -20
- package/template/openfort-templates/firebase/src/ui/openfort/wallets/WalletCreation.tsx +51 -27
- package/template/openfort-templates/firebase/src/ui/openfort/wallets/WalletListCard.tsx +66 -40
- package/template/openfort-templates/firebase/src/ui/openfort/wallets/WalletPasswordSheets.tsx +65 -41
- package/template/openfort-templates/firebase/vite.config.ts +3 -3
- package/template/openfort-templates/headless/biome.json +3 -3
- package/template/openfort-templates/headless/package.json +1 -1
- package/template/openfort-templates/headless/src/components/cards/auth.tsx +2 -2
- package/template/openfort-templates/headless/src/components/cards/profile.tsx +1 -3
- package/template/openfort-templates/headless/src/components/providers.tsx +4 -1
- package/template/openfort-templates/headless/src/index.css +10 -4
- package/template/openfort-templates/openfort-ui/biome.json +3 -3
- package/template/openfort-templates/openfort-ui/package.json +1 -1
- package/template/openfort-templates/openfort-ui/src/App.tsx +1 -3
- package/template/openfort-templates/openfort-ui/src/components/cards/auth.tsx +9 -11
- package/template/openfort-templates/openfort-ui/src/components/cards/head.tsx +157 -145
- package/template/openfort-templates/openfort-ui/src/components/cards/main.tsx +50 -41
- package/template/openfort-templates/openfort-ui/src/components/cards/profile.tsx +29 -35
- package/template/openfort-templates/openfort-ui/src/components/cards/sign.tsx +35 -49
- package/template/openfort-templates/openfort-ui/src/components/cards/wallets.tsx +51 -37
- package/template/openfort-templates/openfort-ui/src/components/createWallet.tsx +63 -30
- package/template/openfort-templates/openfort-ui/src/components/passwordRecovery.tsx +61 -56
- package/template/openfort-templates/openfort-ui/src/components/providers.tsx +14 -15
- package/template/openfort-templates/openfort-ui/src/components/ui/Sheet.tsx +14 -16
- package/template/openfort-templates/openfort-ui/src/components/ui/Tabs.tsx +46 -36
- package/template/openfort-templates/openfort-ui/src/components/ui/TruncateData.tsx +14 -8
- package/template/openfort-templates/openfort-ui/vite.config.ts +2 -2
|
@@ -1,39 +1,46 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { CheckCircleIcon } from '@heroicons/react/24/outline'
|
|
2
|
+
import { RecoveryMethod, type UserWallet, useWallets } from '@openfort/react'
|
|
3
|
+
import { Sheet } from './ui/Sheet'
|
|
4
4
|
|
|
5
5
|
type CreateWalletPasswordSheetProps = {
|
|
6
|
-
open: boolean
|
|
6
|
+
open: boolean
|
|
7
7
|
onClose: () => void
|
|
8
8
|
onCreateWallet?: () => void
|
|
9
|
-
}
|
|
10
|
-
export const CreateWalletPasswordSheet = ({
|
|
11
|
-
|
|
9
|
+
}
|
|
10
|
+
export const CreateWalletPasswordSheet = ({
|
|
11
|
+
open,
|
|
12
|
+
onClose,
|
|
13
|
+
onCreateWallet,
|
|
14
|
+
}: CreateWalletPasswordSheetProps) => {
|
|
15
|
+
const { createWallet, error, isCreating, reset } = useWallets()
|
|
12
16
|
|
|
13
17
|
return (
|
|
14
18
|
<Sheet
|
|
15
19
|
open={open}
|
|
16
|
-
onClose={() => {
|
|
20
|
+
onClose={() => {
|
|
21
|
+
onClose()
|
|
22
|
+
reset()
|
|
23
|
+
}}
|
|
17
24
|
title="Enter Password"
|
|
18
25
|
description="Please enter the password of your wallet."
|
|
19
26
|
>
|
|
20
27
|
<form
|
|
21
28
|
className="flex-1 w-full flex flex-col justify-center max-w-md mx-auto"
|
|
22
29
|
onSubmit={async (e) => {
|
|
23
|
-
e.preventDefault()
|
|
24
|
-
const formData = new FormData(e.target as HTMLFormElement)
|
|
25
|
-
const password = formData.get(
|
|
30
|
+
e.preventDefault()
|
|
31
|
+
const formData = new FormData(e.target as HTMLFormElement)
|
|
32
|
+
const password = formData.get('password') as string
|
|
26
33
|
|
|
27
34
|
const { error } = await createWallet({
|
|
28
35
|
recovery: {
|
|
29
36
|
recoveryMethod: RecoveryMethod.PASSWORD,
|
|
30
|
-
password
|
|
31
|
-
}
|
|
32
|
-
})
|
|
37
|
+
password,
|
|
38
|
+
},
|
|
39
|
+
})
|
|
33
40
|
|
|
34
41
|
if (!error) {
|
|
35
|
-
onCreateWallet?.()
|
|
36
|
-
onClose()
|
|
42
|
+
onCreateWallet?.()
|
|
43
|
+
onClose()
|
|
37
44
|
}
|
|
38
45
|
}}
|
|
39
46
|
>
|
|
@@ -44,7 +51,10 @@ export const CreateWalletPasswordSheet = ({ open, onClose, onCreateWallet }: Cre
|
|
|
44
51
|
</div>
|
|
45
52
|
<div className="flex items-center gap-2">
|
|
46
53
|
<CheckCircleIcon className="h-5 w-5 text-primary my-4 shrink-0" />
|
|
47
|
-
<span>
|
|
54
|
+
<span>
|
|
55
|
+
If you lose this password, you will not be able to access your
|
|
56
|
+
wallet
|
|
57
|
+
</span>
|
|
48
58
|
</div>
|
|
49
59
|
</div>
|
|
50
60
|
<input
|
|
@@ -53,88 +63,83 @@ export const CreateWalletPasswordSheet = ({ open, onClose, onCreateWallet }: Cre
|
|
|
53
63
|
placeholder="Enter your wallet's password"
|
|
54
64
|
className="w-full mt-2 p-2 border border-gray-300 rounded"
|
|
55
65
|
/>
|
|
56
|
-
{
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
{error?.message}
|
|
60
|
-
</span>
|
|
61
|
-
)
|
|
62
|
-
}
|
|
66
|
+
{error && (
|
|
67
|
+
<span className="text-red-500 text-sm mt-2">{error?.message}</span>
|
|
68
|
+
)}
|
|
63
69
|
<button
|
|
64
70
|
className="mt-4 w-full bg-zinc-700 text-white p-2 rounded cursor-pointer"
|
|
65
71
|
type="submit"
|
|
66
72
|
disabled={isCreating}
|
|
67
73
|
>
|
|
68
|
-
{
|
|
69
|
-
isCreating ? "Creating wallet..." : "Create Wallet"
|
|
70
|
-
}
|
|
74
|
+
{isCreating ? 'Creating wallet...' : 'Create Wallet'}
|
|
71
75
|
</button>
|
|
72
76
|
</form>
|
|
73
77
|
</Sheet>
|
|
74
78
|
)
|
|
75
79
|
}
|
|
76
80
|
|
|
77
|
-
|
|
78
81
|
type WalletRecoverPasswordProps = {
|
|
79
|
-
open: boolean
|
|
80
|
-
onClose: () => void
|
|
82
|
+
open: boolean
|
|
83
|
+
onClose: () => void
|
|
81
84
|
wallet: UserWallet | null
|
|
82
|
-
}
|
|
85
|
+
}
|
|
83
86
|
|
|
84
|
-
export const WalletRecoverPasswordSheet = ({
|
|
85
|
-
|
|
87
|
+
export const WalletRecoverPasswordSheet = ({
|
|
88
|
+
open,
|
|
89
|
+
onClose,
|
|
90
|
+
wallet,
|
|
91
|
+
}: WalletRecoverPasswordProps) => {
|
|
92
|
+
const { setActiveWallet, error, isConnecting, reset } = useWallets()
|
|
86
93
|
|
|
87
94
|
return (
|
|
88
95
|
<Sheet
|
|
89
96
|
open={open}
|
|
90
|
-
onClose={() => {
|
|
97
|
+
onClose={() => {
|
|
98
|
+
onClose()
|
|
99
|
+
reset()
|
|
100
|
+
}}
|
|
91
101
|
title="Enter Password"
|
|
92
102
|
description="Please enter the password of your wallet."
|
|
93
103
|
>
|
|
94
104
|
<form
|
|
95
105
|
className="w-full flex-1 flex flex-col justify-center"
|
|
96
106
|
onSubmit={(e) => {
|
|
97
|
-
e.preventDefault()
|
|
98
|
-
const formData = new FormData(e.target as HTMLFormElement)
|
|
99
|
-
const password = formData.get(
|
|
100
|
-
if (!wallet) throw new Error(
|
|
107
|
+
e.preventDefault()
|
|
108
|
+
const formData = new FormData(e.target as HTMLFormElement)
|
|
109
|
+
const password = formData.get('password') as string
|
|
110
|
+
if (!wallet) throw new Error('No wallet to recover')
|
|
101
111
|
|
|
102
112
|
setActiveWallet({
|
|
103
|
-
walletId:
|
|
113
|
+
walletId: 'xyz.openfort',
|
|
104
114
|
recovery: {
|
|
105
115
|
recoveryMethod: RecoveryMethod.PASSWORD,
|
|
106
|
-
password
|
|
116
|
+
password,
|
|
107
117
|
},
|
|
108
118
|
address: wallet.address,
|
|
109
|
-
})
|
|
119
|
+
})
|
|
110
120
|
}}
|
|
111
121
|
>
|
|
112
|
-
{
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
122
|
+
{wallet && (
|
|
123
|
+
<p>
|
|
124
|
+
Recover wallet {wallet.address.slice(0, 6)}...
|
|
125
|
+
{wallet.address.slice(-4)} with password
|
|
126
|
+
</p>
|
|
127
|
+
)}
|
|
117
128
|
<input
|
|
118
129
|
type="password"
|
|
119
130
|
name="password"
|
|
120
131
|
placeholder="Enter your wallet's password"
|
|
121
132
|
className="w-full mt-2 p-2 border border-gray-300 rounded"
|
|
122
133
|
/>
|
|
123
|
-
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
{error?.message}
|
|
127
|
-
</span>
|
|
128
|
-
)
|
|
129
|
-
}
|
|
134
|
+
{error && (
|
|
135
|
+
<span className="text-red-500 text-sm mt-2">{error?.message}</span>
|
|
136
|
+
)}
|
|
130
137
|
<button
|
|
131
138
|
className="mt-4 w-full bg-zinc-700 text-white p-2 rounded cursor-pointer"
|
|
132
139
|
type="submit"
|
|
133
140
|
disabled={isConnecting}
|
|
134
141
|
>
|
|
135
|
-
{
|
|
136
|
-
isConnecting ? "Recovering..." : "Recover Wallet"
|
|
137
|
-
}
|
|
142
|
+
{isConnecting ? 'Recovering...' : 'Recover Wallet'}
|
|
138
143
|
</button>
|
|
139
144
|
</form>
|
|
140
145
|
</Sheet>
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { useState } from 'react';
|
|
1
|
+
import { getDefaultConfig, OpenfortProvider, type Theme } from '@openfort/react'
|
|
2
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { beamTestnet, polygonAmoy } from 'viem/chains'
|
|
5
|
+
import { createConfig, WagmiProvider } from 'wagmi'
|
|
7
6
|
|
|
8
7
|
const config = createConfig(
|
|
9
8
|
getDefaultConfig({
|
|
10
9
|
appName: 'Openfort Next.js demo',
|
|
11
10
|
chains: [beamTestnet, polygonAmoy], // The chains you want to support
|
|
12
11
|
walletConnectProjectId: import.meta.env.VITE_WALLET_CONNECT_PROJECT_ID, // The WalletConnect Project ID
|
|
13
|
-
})
|
|
14
|
-
)
|
|
12
|
+
}),
|
|
13
|
+
)
|
|
15
14
|
|
|
16
15
|
export function Providers({ children }: { children: React.ReactNode }) {
|
|
17
16
|
const [queryClient] = useState(() => new QueryClient())
|
|
@@ -21,27 +20,27 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|
|
21
20
|
<OpenfortProvider
|
|
22
21
|
debugMode
|
|
23
22
|
publishableKey={import.meta.env.VITE_OPENFORT_PUBLISHABLE_KEY!}
|
|
24
|
-
|
|
25
23
|
// Set the wallet configuration. In this example, we will be using the embedded signer.
|
|
26
24
|
walletConfig={{
|
|
27
25
|
shieldPublishableKey: import.meta.env.VITE_SHIELD_PUBLISHABLE_KEY!, // The public key for your Openfort Shield account get it from https://dashboard.openfort.io
|
|
28
26
|
|
|
29
27
|
ethereumProviderPolicyId: import.meta.env.VITE_POLICY_ID, // The policy ID for sponsoring transactions
|
|
30
28
|
|
|
31
|
-
|
|
29
|
+
// If you want to use AUTOMATIC embedded wallet recovery, an encryption session is required.
|
|
30
|
+
// See: https://www.openfort.io/docs/products/embedded-wallet/react-native/quickstart/automatic
|
|
31
|
+
// For backend setup, check: https://github.com/openfort-xyz/openfort-backend-quickstart
|
|
32
|
+
createEncryptedSessionEndpoint: import.meta.env
|
|
33
|
+
.VITE_CREATE_ENCRYPTED_SESSION_ENDPOINT,
|
|
32
34
|
|
|
33
35
|
recoverWalletAutomaticallyAfterAuth: false, // We will manually call create/setActive wallet after auth
|
|
34
36
|
}}
|
|
35
|
-
|
|
36
37
|
uiConfig={{
|
|
37
38
|
theme: import.meta.env.VITE_OPENFORT_THEME as Theme,
|
|
38
39
|
}}
|
|
39
40
|
>
|
|
40
|
-
|
|
41
|
-
{children}
|
|
42
|
-
</>
|
|
41
|
+
{children}
|
|
43
42
|
</OpenfortProvider>
|
|
44
43
|
</QueryClientProvider>
|
|
45
44
|
</WagmiProvider>
|
|
46
|
-
)
|
|
45
|
+
)
|
|
47
46
|
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { ChevronLeftIcon } from
|
|
2
|
-
import { useEffect, useState } from
|
|
1
|
+
import { ChevronLeftIcon } from '@heroicons/react/24/outline'
|
|
2
|
+
import { useEffect, useState } from 'react'
|
|
3
3
|
|
|
4
4
|
type SheetProps = {
|
|
5
|
-
open: boolean
|
|
6
|
-
onClose: () => void
|
|
7
|
-
title: string
|
|
8
|
-
description: string
|
|
5
|
+
open: boolean
|
|
6
|
+
onClose: () => void
|
|
7
|
+
title: string
|
|
8
|
+
description: string
|
|
9
9
|
children: React.ReactNode
|
|
10
|
-
}
|
|
10
|
+
}
|
|
11
11
|
|
|
12
12
|
const SheetInner = ({ onClose, title, description, children }: SheetProps) => {
|
|
13
|
-
const [isClosing, setIsClosing] = useState(false)
|
|
13
|
+
const [isClosing, setIsClosing] = useState(false)
|
|
14
14
|
useEffect(() => {
|
|
15
15
|
if (isClosing) {
|
|
16
|
-
const timer = setTimeout(onClose, 300)
|
|
17
|
-
return () => clearTimeout(timer)
|
|
16
|
+
const timer = setTimeout(onClose, 300)
|
|
17
|
+
return () => clearTimeout(timer)
|
|
18
18
|
}
|
|
19
|
-
}, [isClosing, onClose])
|
|
19
|
+
}, [isClosing, onClose])
|
|
20
20
|
|
|
21
21
|
return (
|
|
22
22
|
<div
|
|
@@ -41,9 +41,7 @@ const SheetInner = ({ onClose, title, description, children }: SheetProps) => {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export const Sheet = (props: SheetProps) => {
|
|
44
|
-
if (!props.open) return null
|
|
44
|
+
if (!props.open) return null
|
|
45
45
|
|
|
46
|
-
return
|
|
47
|
-
|
|
48
|
-
)
|
|
49
|
-
}
|
|
46
|
+
return <SheetInner {...props} />
|
|
47
|
+
}
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
export type TabType = {
|
|
2
|
-
name: string
|
|
3
|
-
component: React.ReactNode
|
|
4
|
-
icon: React.ComponentType<React.SVGProps<SVGSVGElement
|
|
5
|
-
}
|
|
2
|
+
name: string
|
|
3
|
+
component: React.ReactNode
|
|
4
|
+
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>
|
|
5
|
+
}
|
|
6
6
|
|
|
7
7
|
type TabProps = {
|
|
8
|
-
onClick?: () => void
|
|
9
|
-
isActive?: boolean
|
|
10
|
-
} & TabType
|
|
8
|
+
onClick?: () => void
|
|
9
|
+
isActive?: boolean
|
|
10
|
+
} & TabType
|
|
11
11
|
|
|
12
12
|
const DesktopTab = ({ name, isActive, ...buttonProps }: TabProps) => {
|
|
13
13
|
return (
|
|
14
14
|
<button
|
|
15
15
|
className="relative h-8 mx-2.5 transition-colors cursor-pointer"
|
|
16
|
-
style={
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
style={
|
|
17
|
+
{
|
|
18
|
+
'--tab-bg-color': isActive
|
|
19
|
+
? 'var(--color-zinc-800)'
|
|
20
|
+
: 'var(--color-zinc-700)',
|
|
21
|
+
opacity: isActive ? 1 : 0.6,
|
|
22
|
+
} as React.CSSProperties
|
|
23
|
+
}
|
|
20
24
|
{...buttonProps}
|
|
21
25
|
>
|
|
22
26
|
<div className="absolute w-5 h-8 bg-(--tab-bg-color) rotate-20 transform origin-top-left top-1" />
|
|
@@ -24,7 +28,7 @@ const DesktopTab = ({ name, isActive, ...buttonProps }: TabProps) => {
|
|
|
24
28
|
<div className="absolute inset-0 rounded-md bg-(--tab-bg-color)" />
|
|
25
29
|
|
|
26
30
|
<span
|
|
27
|
-
className={`${isActive ?
|
|
31
|
+
className={`${isActive ? 'text-white' : 'text-zinc-400'} whitespace-nowrap bg-(--tab-bg-color) z-10 relative mx-2 pb-4`}
|
|
28
32
|
>
|
|
29
33
|
{name}
|
|
30
34
|
</span>
|
|
@@ -33,25 +37,30 @@ const DesktopTab = ({ name, isActive, ...buttonProps }: TabProps) => {
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
type TabGroupProps = {
|
|
36
|
-
tabs: TabType[]
|
|
37
|
-
currentTab?: TabType
|
|
40
|
+
tabs: TabType[]
|
|
41
|
+
currentTab?: TabType
|
|
38
42
|
setCurrentTab?: (tab: TabType) => void
|
|
39
43
|
showTabs?: boolean
|
|
40
44
|
}
|
|
41
45
|
|
|
42
|
-
export const DesktopTabGroup = ({
|
|
46
|
+
export const DesktopTabGroup = ({
|
|
47
|
+
tabs,
|
|
48
|
+
currentTab,
|
|
49
|
+
setCurrentTab,
|
|
50
|
+
showTabs,
|
|
51
|
+
}: TabGroupProps) => {
|
|
43
52
|
return (
|
|
44
|
-
<div
|
|
45
|
-
className="absolute left-[100%] top-2 rotate-90 transform origin-top-left hidden xs:block"
|
|
46
|
-
>
|
|
53
|
+
<div className="absolute left-[100%] top-2 rotate-90 transform origin-top-left hidden xs:block">
|
|
47
54
|
<div
|
|
48
55
|
className="flex gap-2 transition-transform duration-500"
|
|
49
|
-
style={{
|
|
56
|
+
style={{
|
|
57
|
+
transform: showTabs ? 'translateY(-100%)' : 'translateY(10px)',
|
|
58
|
+
}}
|
|
50
59
|
>
|
|
51
60
|
{tabs?.map((tab) => (
|
|
52
61
|
<DesktopTab
|
|
53
62
|
key={tab.name}
|
|
54
|
-
onClick={() => setCurrentTab
|
|
63
|
+
onClick={() => setCurrentTab?.(tab)}
|
|
55
64
|
isActive={currentTab?.name === tab.name}
|
|
56
65
|
{...tab}
|
|
57
66
|
/>
|
|
@@ -61,9 +70,12 @@ export const DesktopTabGroup = ({ tabs, currentTab, setCurrentTab, showTabs }: T
|
|
|
61
70
|
)
|
|
62
71
|
}
|
|
63
72
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
const MobileTab = ({
|
|
74
|
+
name,
|
|
75
|
+
isActive,
|
|
76
|
+
icon: Icon,
|
|
77
|
+
...buttonProps
|
|
78
|
+
}: TabProps) => {
|
|
67
79
|
return (
|
|
68
80
|
<button
|
|
69
81
|
className="relative h-8 mx-2.5 transition-colors cursor-pointer"
|
|
@@ -71,7 +83,7 @@ const MobileTab = ({ name, isActive, icon: Icon, ...buttonProps }: TabProps) =>
|
|
|
71
83
|
>
|
|
72
84
|
<Icon className="h-5 w-5 mx-auto mb-1" />
|
|
73
85
|
<span
|
|
74
|
-
className={`${isActive ?
|
|
86
|
+
className={`${isActive ? 'text-white' : 'text-zinc-400'} whitespace-nowrap`}
|
|
75
87
|
>
|
|
76
88
|
{name}
|
|
77
89
|
</span>
|
|
@@ -82,20 +94,18 @@ const MobileTab = ({ name, isActive, icon: Icon, ...buttonProps }: TabProps) =>
|
|
|
82
94
|
export const MobileTabGroup = ({
|
|
83
95
|
tabs,
|
|
84
96
|
currentTab,
|
|
85
|
-
setCurrentTab
|
|
97
|
+
setCurrentTab,
|
|
86
98
|
}: TabGroupProps) => {
|
|
87
99
|
return (
|
|
88
100
|
<div className="mt-auto xs:hidden flex pt-6 pb-2 items-end justify-between text-zinc-400 text-sm">
|
|
89
|
-
{
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
))
|
|
98
|
-
}
|
|
101
|
+
{tabs?.map((tab) => (
|
|
102
|
+
<MobileTab
|
|
103
|
+
key={tab.name}
|
|
104
|
+
onClick={() => setCurrentTab?.(tab)}
|
|
105
|
+
isActive={currentTab?.name === tab.name}
|
|
106
|
+
{...tab}
|
|
107
|
+
/>
|
|
108
|
+
))}
|
|
99
109
|
</div>
|
|
100
110
|
)
|
|
101
|
-
}
|
|
111
|
+
}
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import { useState } from
|
|
1
|
+
import { useState } from 'react'
|
|
2
2
|
|
|
3
|
-
export const TruncateData = ({
|
|
3
|
+
export const TruncateData = ({
|
|
4
|
+
className,
|
|
5
|
+
data,
|
|
6
|
+
}: {
|
|
7
|
+
className?: string
|
|
8
|
+
data?: string
|
|
9
|
+
}) => {
|
|
4
10
|
const [viewMore, setViewMore] = useState(false)
|
|
5
11
|
if (!data) return null
|
|
6
12
|
|
|
7
13
|
return (
|
|
8
|
-
<div
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
>
|
|
14
|
+
<div
|
|
15
|
+
className={`mt-4 p-2 border border-zinc-700 rounded bg-zinc-900 ${className}`}
|
|
16
|
+
>
|
|
17
|
+
<pre className="break-words whitespace-normal text-sm">
|
|
12
18
|
{viewMore ? data : data.length > 90 ? `${data.slice(0, 90)}...` : data}
|
|
13
19
|
</pre>
|
|
14
20
|
{data.length > 90 && (
|
|
@@ -17,9 +23,9 @@ export const TruncateData = ({ className, data }: { className?: string; data?: s
|
|
|
17
23
|
onClick={() => setViewMore(!viewMore)}
|
|
18
24
|
type="button"
|
|
19
25
|
>
|
|
20
|
-
{viewMore ?
|
|
26
|
+
{viewMore ? 'View less' : 'View more'}
|
|
21
27
|
</button>
|
|
22
28
|
)}
|
|
23
29
|
</div>
|
|
24
30
|
)
|
|
25
|
-
}
|
|
31
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { defineConfig } from 'vite'
|
|
2
|
-
import react from '@vitejs/plugin-react'
|
|
3
1
|
import tailwindcss from '@tailwindcss/vite'
|
|
2
|
+
import react from '@vitejs/plugin-react'
|
|
3
|
+
import { defineConfig } from 'vite'
|
|
4
4
|
|
|
5
5
|
// https://vite.dev/config/
|
|
6
6
|
export default defineConfig({
|