Skip to content

Commit 26282ef

Browse files
committed
chore(dashboard): ui updates pt5
1 parent 11dcf04 commit 26282ef

File tree

22 files changed

+1462
-404
lines changed

22 files changed

+1462
-404
lines changed

packages/apps/app-dashboard/src/components/shared/ui/LoadingLock.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { theme } from '@/components/user-dashboard/connect/ui/theme';
2+
import { useRef, useEffect, useState } from 'react';
23

34
interface LoadingLockProps {
45
text?: string;
@@ -153,7 +154,19 @@ const keyholeDots = [
153154

154155
export default function LoadingLock({ text }: LoadingLockProps) {
155156
const totalDots = lockDots.length;
156-
const animationDuration = 1.5; // seconds
157+
const animationDuration = 1.5; // seconds (1500ms)
158+
const animationDurationMs = animationDuration * 1000;
159+
160+
// Calculate the animation offset to make it appear continuous across remounts
161+
const mountTimeRef = useRef<number>(Date.now());
162+
const [animationOffset, setAnimationOffset] = useState<number>(0);
163+
164+
useEffect(() => {
165+
// Calculate how many milliseconds into the animation cycle we are
166+
const elapsed = Date.now() - mountTimeRef.current;
167+
const offset = -(elapsed % animationDurationMs) / 1000; // Convert back to seconds
168+
setAnimationOffset(offset);
169+
}, [animationDurationMs]);
157170

158171
return (
159172
<div className="flex flex-col items-center justify-center py-8 sm:py-12 lg:py-16">
@@ -164,7 +177,9 @@ export default function LoadingLock({ text }: LoadingLockProps) {
164177
>
165178
{/* Animated lock outline */}
166179
{lockDots.map((dot, index) => {
167-
const delay = -(index / totalDots) * animationDuration;
180+
// Combine the per-dot delay with the global animation offset
181+
const dotDelay = -(index / totalDots) * animationDuration;
182+
const totalDelay = dotDelay + animationOffset;
168183
return (
169184
<div
170185
key={`lock-${index}`}
@@ -175,7 +190,7 @@ export default function LoadingLock({ text }: LoadingLockProps) {
175190
left: `${dot.x * 1.5}px`,
176191
top: `${dot.y * 1.5}px`,
177192
backgroundColor: theme.brandOrange,
178-
animation: `dotChase ${animationDuration}s ease-in-out ${delay}s infinite`,
193+
animation: `dotChase ${animationDuration}s ease-in-out ${totalDelay}s infinite`,
179194
}}
180195
/>
181196
);

packages/apps/app-dashboard/src/components/shared/ui/sheet.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ function SheetContent({
5050
<SheetPrimitive.Content
5151
data-slot="sheet-content"
5252
className={cn(
53-
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
53+
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-200 data-[state=open]:duration-200',
5454
side === 'right' &&
5555
'data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm',
5656
side === 'left' &&

packages/apps/app-dashboard/src/components/user-dashboard/auth/AuthMethods.tsx

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,41 @@
1-
import { Dispatch, SetStateAction } from 'react';
2-
import { ThemeType } from '../connect/ui/theme';
1+
import { Dispatch, SetStateAction, useEffect } from 'react';
2+
import { theme as themeImport, fonts } from '../connect/ui/theme';
33

44
interface AuthMethodsProps {
55
setView: Dispatch<SetStateAction<string>>;
6-
theme: ThemeType;
6+
clearError?: () => void;
77
}
88

9-
const AuthMethods = ({ setView, theme }: AuthMethodsProps) => {
9+
const AuthMethods = ({ setView, clearError }: AuthMethodsProps) => {
10+
useEffect(() => {
11+
if (clearError) {
12+
clearError();
13+
}
14+
}, [clearError]);
15+
1016
return (
1117
<div className="space-y-3">
18+
<div className="text-center mb-6">
19+
<h2 className="text-xl font-bold text-gray-900 dark:text-white mb-2" style={fonts.heading}>
20+
Authentication Required
21+
</h2>
22+
<p className="text-xs text-gray-600 dark:text-white/60" style={fonts.body}>
23+
Please login in or sign up to continue.
24+
</p>
25+
</div>
1226
<div className="flex flex-col items-center space-y-3">
1327
<div
14-
className={`w-full sm:w-3/4 md:w-3/4 lg:w-full py-3 px-4 flex items-center justify-between ${theme.cardBg} border ${theme.cardBorder} rounded-lg ${theme.itemHoverBg} transition-colors cursor-pointer`}
28+
className={`w-full sm:w-3/4 md:w-3/4 lg:w-full py-3 px-4 flex items-center justify-between bg-white dark:bg-gray-950 border border-gray-200 dark:border-white/10 rounded-lg hover:bg-gray-100 dark:hover:bg-white/[0.05] transition-colors cursor-pointer`}
1529
onClick={() => setView('email')}
1630
>
1731
<div className="flex items-center">
18-
<div className={`w-5 h-5 mr-3 flex items-center justify-center ${theme.textMuted}`}>
19-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" className="w-4 h-4">
32+
<div className={`w-5 h-5 mr-3 flex items-center justify-center`}>
33+
<svg
34+
viewBox="0 0 24 24"
35+
fill="none"
36+
stroke={themeImport.brandOrange}
37+
className="w-4 h-4"
38+
>
2039
<path
2140
strokeLinecap="round"
2241
strokeLinejoin="round"
@@ -25,10 +44,15 @@ const AuthMethods = ({ setView, theme }: AuthMethodsProps) => {
2544
/>
2645
</svg>
2746
</div>
28-
<span className={`text-sm font-medium ${theme.text}`}>Email</span>
47+
<span
48+
className={`text-sm font-medium text-gray-900 dark:text-white`}
49+
style={fonts.heading}
50+
>
51+
Email
52+
</span>
2953
</div>
3054
<svg
31-
className={`w-4 h-4 ${theme.textMuted}`}
55+
className={`w-4 h-4 text-gray-600 dark:text-white/60`}
3256
fill="none"
3357
stroke="currentColor"
3458
viewBox="0 0 24 24"
@@ -38,12 +62,17 @@ const AuthMethods = ({ setView, theme }: AuthMethodsProps) => {
3862
</div>
3963

4064
<div
41-
className={`w-full sm:w-3/4 md:w-3/4 lg:w-full py-3 px-4 flex items-center justify-between ${theme.cardBg} border ${theme.cardBorder} rounded-lg ${theme.itemHoverBg} transition-colors cursor-pointer`}
65+
className={`w-full sm:w-3/4 md:w-3/4 lg:w-full py-3 px-4 flex items-center justify-between bg-white dark:bg-gray-950 border border-gray-200 dark:border-white/10 rounded-lg hover:bg-gray-100 dark:hover:bg-white/[0.05] transition-colors cursor-pointer`}
4266
onClick={() => setView('phone')}
4367
>
4468
<div className="flex items-center">
45-
<div className={`w-5 h-5 mr-3 flex items-center justify-center ${theme.textMuted}`}>
46-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" className="w-4 h-4">
69+
<div className={`w-5 h-5 mr-3 flex items-center justify-center`}>
70+
<svg
71+
viewBox="0 0 24 24"
72+
fill="none"
73+
stroke={themeImport.brandOrange}
74+
className="w-4 h-4"
75+
>
4776
<path
4877
strokeLinecap="round"
4978
strokeLinejoin="round"
@@ -52,10 +81,15 @@ const AuthMethods = ({ setView, theme }: AuthMethodsProps) => {
5281
/>
5382
</svg>
5483
</div>
55-
<span className={`text-sm font-medium ${theme.text}`}>Phone</span>
84+
<span
85+
className={`text-sm font-medium text-gray-900 dark:text-white`}
86+
style={fonts.heading}
87+
>
88+
Phone
89+
</span>
5690
</div>
5791
<svg
58-
className={`w-4 h-4 ${theme.textMuted}`}
92+
className={`w-4 h-4 text-gray-600 dark:text-white/60`}
5993
fill="none"
6094
stroke="currentColor"
6195
viewBox="0 0 24 24"
@@ -65,12 +99,17 @@ const AuthMethods = ({ setView, theme }: AuthMethodsProps) => {
6599
</div>
66100

67101
<div
68-
className={`w-full sm:w-3/4 md:w-3/4 lg:w-full py-3 px-4 flex items-center justify-between ${theme.cardBg} border ${theme.cardBorder} rounded-lg ${theme.itemHoverBg} transition-colors cursor-pointer`}
102+
className={`w-full sm:w-3/4 md:w-3/4 lg:w-full py-3 px-4 flex items-center justify-between bg-white dark:bg-gray-950 border border-gray-200 dark:border-white/10 rounded-lg hover:bg-gray-100 dark:hover:bg-white/[0.05] transition-colors cursor-pointer`}
69103
onClick={() => setView('wallet')}
70104
>
71105
<div className="flex items-center">
72-
<div className={`w-5 h-5 mr-3 flex items-center justify-center ${theme.textMuted}`}>
73-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" className="w-4 h-4">
106+
<div className={`w-5 h-5 mr-3 flex items-center justify-center`}>
107+
<svg
108+
viewBox="0 0 24 24"
109+
fill="none"
110+
stroke={themeImport.brandOrange}
111+
className="w-4 h-4"
112+
>
74113
<path
75114
strokeLinecap="round"
76115
strokeLinejoin="round"
@@ -79,10 +118,15 @@ const AuthMethods = ({ setView, theme }: AuthMethodsProps) => {
79118
/>
80119
</svg>
81120
</div>
82-
<span className={`text-sm font-medium ${theme.text}`}>Wallet</span>
121+
<span
122+
className={`text-sm font-medium text-gray-900 dark:text-white`}
123+
style={fonts.heading}
124+
>
125+
Wallet
126+
</span>
83127
</div>
84128
<svg
85-
className={`w-4 h-4 ${theme.textMuted}`}
129+
className={`w-4 h-4 text-gray-600 dark:text-white/60`}
86130
fill="none"
87131
stroke="currentColor"
88132
viewBox="0 0 24 24"
@@ -92,12 +136,17 @@ const AuthMethods = ({ setView, theme }: AuthMethodsProps) => {
92136
</div>
93137

94138
<div
95-
className={`w-full sm:w-3/4 md:w-3/4 lg:w-full py-3 px-4 flex items-center justify-between ${theme.cardBg} border ${theme.cardBorder} rounded-lg ${theme.itemHoverBg} transition-colors cursor-pointer`}
139+
className={`w-full sm:w-3/4 md:w-3/4 lg:w-full py-3 px-4 flex items-center justify-between bg-white dark:bg-gray-950 border border-gray-200 dark:border-white/10 rounded-lg hover:bg-gray-100 dark:hover:bg-white/[0.05] transition-colors cursor-pointer`}
96140
onClick={() => setView('webauthn')}
97141
>
98142
<div className="flex items-center">
99-
<div className={`w-5 h-5 mr-3 flex items-center justify-center ${theme.textMuted}`}>
100-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" className="w-4 h-4">
143+
<div className={`w-5 h-5 mr-3 flex items-center justify-center`}>
144+
<svg
145+
viewBox="0 0 24 24"
146+
fill="none"
147+
stroke={themeImport.brandOrange}
148+
className="w-4 h-4"
149+
>
101150
<path
102151
strokeLinecap="round"
103152
strokeLinejoin="round"
@@ -106,10 +155,15 @@ const AuthMethods = ({ setView, theme }: AuthMethodsProps) => {
106155
/>
107156
</svg>
108157
</div>
109-
<span className={`text-sm font-medium ${theme.text}`}>Passkey</span>
158+
<span
159+
className={`text-sm font-medium text-gray-900 dark:text-white`}
160+
style={fonts.heading}
161+
>
162+
Passkey
163+
</span>
110164
</div>
111165
<svg
112-
className={`w-4 h-4 ${theme.textMuted}`}
166+
className={`w-4 h-4 text-gray-600 dark:text-white/60`}
113167
fill="none"
114168
stroke="currentColor"
115169
viewBox="0 0 24 24"
Lines changed: 77 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import { useState, Dispatch, SetStateAction } from 'react';
1+
import { Dispatch, SetStateAction } from 'react';
22
import { ThemeType } from '../connect/ui/theme';
3+
import { motion, AnimatePresence } from 'framer-motion';
4+
import { AuthView } from '../connect/Connect';
35

46
import AuthMethods from './AuthMethods';
57
import WebAuthn from './WebAuthn';
@@ -21,59 +23,100 @@ interface ConnectProps {
2123
registerWithWebAuthn?: (credentialId: string) => Promise<void>;
2224
clearError?: () => void;
2325
theme: ThemeType;
26+
view: AuthView;
27+
setView: (view: AuthView) => void;
2428
}
2529

26-
type AuthView = 'default' | 'email' | 'phone' | 'wallet' | 'webauthn';
27-
2830
export default function ConnectMethods({
2931
authWithWebAuthn,
3032
authWithStytch,
3133
authWithEthWallet,
3234
registerWithWebAuthn,
3335
clearError,
3436
theme,
37+
view,
38+
setView,
3539
}: ConnectProps) {
36-
const [view, setView] = useState<AuthView>('default');
37-
3840
return (
39-
<>
41+
<AnimatePresence mode="wait">
4042
{view === 'default' && (
41-
<>
42-
<AuthMethods setView={setView as Dispatch<SetStateAction<string>>} theme={theme} />
43-
</>
43+
<motion.div
44+
key="default"
45+
initial={{ opacity: 0 }}
46+
animate={{ opacity: 1 }}
47+
exit={{ opacity: 0 }}
48+
transition={{ duration: 0.2, ease: 'easeInOut' }}
49+
>
50+
<AuthMethods
51+
setView={setView as Dispatch<SetStateAction<string>>}
52+
clearError={clearError}
53+
/>
54+
</motion.div>
4455
)}
4556
{view === 'email' && (
46-
<StytchOTP
47-
method="email"
48-
authWithStytch={authWithStytch}
49-
setView={setView as Dispatch<SetStateAction<string>>}
50-
theme={theme}
51-
/>
57+
<motion.div
58+
key="email"
59+
initial={{ opacity: 0 }}
60+
animate={{ opacity: 1 }}
61+
exit={{ opacity: 0 }}
62+
transition={{ duration: 0.2, ease: 'easeInOut' }}
63+
>
64+
<StytchOTP
65+
method="email"
66+
authWithStytch={authWithStytch}
67+
setView={setView as Dispatch<SetStateAction<string>>}
68+
theme={theme}
69+
/>
70+
</motion.div>
5271
)}
5372
{view === 'phone' && (
54-
<StytchOTP
55-
method="phone"
56-
authWithStytch={authWithStytch}
57-
setView={setView as Dispatch<SetStateAction<string>>}
58-
theme={theme}
59-
/>
73+
<motion.div
74+
key="phone"
75+
initial={{ opacity: 0 }}
76+
animate={{ opacity: 1 }}
77+
exit={{ opacity: 0 }}
78+
transition={{ duration: 0.2, ease: 'easeInOut' }}
79+
>
80+
<StytchOTP
81+
method="phone"
82+
authWithStytch={authWithStytch}
83+
setView={setView as Dispatch<SetStateAction<string>>}
84+
theme={theme}
85+
/>
86+
</motion.div>
6087
)}
6188
{view === 'wallet' && (
62-
<EthWalletAuth
63-
authWithEthWallet={authWithEthWallet}
64-
setView={setView as Dispatch<SetStateAction<string>>}
65-
theme={theme}
66-
/>
89+
<motion.div
90+
key="wallet"
91+
initial={{ opacity: 0 }}
92+
animate={{ opacity: 1 }}
93+
exit={{ opacity: 0 }}
94+
transition={{ duration: 0.2, ease: 'easeInOut' }}
95+
>
96+
<EthWalletAuth
97+
authWithEthWallet={authWithEthWallet}
98+
setView={setView as Dispatch<SetStateAction<string>>}
99+
theme={theme}
100+
/>
101+
</motion.div>
67102
)}
68103
{view === 'webauthn' && (
69-
<WebAuthn
70-
authWithWebAuthn={authWithWebAuthn}
71-
registerWithWebAuthn={registerWithWebAuthn}
72-
setView={setView as Dispatch<SetStateAction<string>>}
73-
clearError={clearError}
74-
theme={theme}
75-
/>
104+
<motion.div
105+
key="webauthn"
106+
initial={{ opacity: 0 }}
107+
animate={{ opacity: 1 }}
108+
exit={{ opacity: 0 }}
109+
transition={{ duration: 0.2, ease: 'easeInOut' }}
110+
>
111+
<WebAuthn
112+
authWithWebAuthn={authWithWebAuthn}
113+
registerWithWebAuthn={registerWithWebAuthn}
114+
setView={setView as Dispatch<SetStateAction<string>>}
115+
clearError={clearError}
116+
theme={theme}
117+
/>
118+
</motion.div>
76119
)}
77-
</>
120+
</AnimatePresence>
78121
);
79122
}

0 commit comments

Comments
 (0)