US-029: Add post-login loading state and update TopBar session name
This commit is contained in:
@@ -14,6 +14,7 @@ export function LoginScreen({ onComplete }: LoginScreenProps) {
|
||||
const [activeField, setActiveField] = useState<'username' | 'password' | 'done' | null>('username')
|
||||
const [buttonPressed, setButtonPressed] = useState(false)
|
||||
const [isExiting, setIsExiting] = useState(false)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [typingComplete, setTypingComplete] = useState(false)
|
||||
const [buttonHovered, setButtonHovered] = useState(false)
|
||||
const [connectionState, setConnectionState] = useState<'connecting' | 'connected'>('connecting')
|
||||
@@ -42,16 +43,19 @@ export function LoginScreen({ onComplete }: LoginScreenProps) {
|
||||
const canLogin = typingComplete && connectionState === 'connected'
|
||||
|
||||
const handleLogin = useCallback(() => {
|
||||
if (!canLogin || isExiting) return
|
||||
if (!canLogin || isExiting || isLoading) return
|
||||
setButtonPressed(true)
|
||||
addTimeout(() => {
|
||||
setIsLoading(true)
|
||||
addTimeout(() => {
|
||||
setIsExiting(true)
|
||||
addTimeout(() => {
|
||||
requestFocusAfterLogin()
|
||||
onComplete()
|
||||
}, prefersReducedMotion ? 0 : 200)
|
||||
}, prefersReducedMotion ? 0 : 600)
|
||||
}, 100)
|
||||
}, [canLogin, isExiting, onComplete, requestFocusAfterLogin, prefersReducedMotion, addTimeout])
|
||||
}, [canLogin, isExiting, isLoading, onComplete, requestFocusAfterLogin, prefersReducedMotion, addTimeout])
|
||||
|
||||
const startLoginSequence = useCallback(() => {
|
||||
if (prefersReducedMotion) {
|
||||
@@ -159,6 +163,41 @@ export function LoginScreen({ onComplete }: LoginScreenProps) {
|
||||
animate={isExiting ? { scale: 1.03, opacity: 0 } : { scale: 1, opacity: 1 }}
|
||||
transition={{ duration: 0.2, ease: 'easeOut' }}
|
||||
>
|
||||
{isLoading ? (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: '48px 0',
|
||||
gap: '16px',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="login-spinner"
|
||||
style={{
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
border: '3px solid #E5E7EB',
|
||||
borderTopColor: '#0D6E6E',
|
||||
borderRadius: '50%',
|
||||
}}
|
||||
role="status"
|
||||
aria-label="Loading clinical records"
|
||||
/>
|
||||
<span
|
||||
style={{
|
||||
fontFamily: "var(--font-ui)",
|
||||
fontSize: '12px',
|
||||
color: 'var(--text-secondary, #5B7A78)',
|
||||
}}
|
||||
>
|
||||
Loading clinical records...
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Branding Header */}
|
||||
<div
|
||||
className="flex flex-col items-center"
|
||||
@@ -369,6 +408,8 @@ export function LoginScreen({ onComplete }: LoginScreenProps) {
|
||||
Secure clinical system login
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -169,7 +169,7 @@ export function TopBar({ onSearchClick }: TopBarProps) {
|
||||
fontFamily: 'var(--font-ui)',
|
||||
}}
|
||||
>
|
||||
Dr. A.CHARLWOOD
|
||||
A.RECRUITER
|
||||
</span>
|
||||
<span
|
||||
className="font-geist hidden xs:inline"
|
||||
|
||||
@@ -250,6 +250,15 @@ html {
|
||||
}
|
||||
}
|
||||
|
||||
/* Login spinner */
|
||||
@keyframes login-spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.login-spinner {
|
||||
animation: login-spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
/* Custom scrollbar for sidebar */
|
||||
.pmr-scrollbar {
|
||||
scrollbar-width: thin;
|
||||
@@ -415,4 +424,10 @@ textarea:focus-visible {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
/* Static login spinner indicator */
|
||||
.login-spinner {
|
||||
animation: none;
|
||||
border-top-color: #0D6E6E;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user