Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/app/(auth)/verify-email/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@

import { useState, useRef, useEffect, useCallback } from "react"
import { useRouter } from "next/navigation"
import Link from "next/link"
import { Mail, ArrowLeft, Loader2 } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { AuthCard, AuthHeader, AuthFooter } from "@/components/auth/auth-card"
import { AUTH_API, AUTH_ROUTES } from "@/lib/auth/config"
import { useAuth } from "@/providers/auth-provider"

const RESEND_COOLDOWN_SECONDS = 60

export default function VerifyEmailPage() {
const router = useRouter()
const { logout } = useAuth()

const [otp, setOtp] = useState<string[]>(["", "", "", "", "", ""])
const [isLoading, setIsLoading] = useState(false)
Expand Down Expand Up @@ -204,12 +205,13 @@ export default function VerifyEmailPage() {
"Resend verification code"
)}
</button>
<Link
href={AUTH_ROUTES.LOGIN}
<button
type="button"
onClick={() => logout()}
className="flex items-center gap-2 text-muted-foreground hover:text-primary transition-colors"
>
<ArrowLeft className="h-4 w-4" />Back to login
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The button label says "Back to login", but calling logout() will navigate to / (see AuthProvider.logout()), not to AUTH_ROUTES.LOGIN. This changes behavior from the previous Link to the login page and can confuse users; either redirect to the login route after logout (or adjust logout to support a caller-provided redirect) or update the label to match the actual destination.

Suggested change
<ArrowLeft className="h-4 w-4" />Back to login
<ArrowLeft className="h-4 w-4" />Log out

Copilot uses AI. Check for mistakes.
</Link>
</button>
</AuthFooter>
</AuthCard>
)
Expand Down
9 changes: 8 additions & 1 deletion src/app/(dashboard)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default function DashboardLayout({
}: {
children: React.ReactNode
}) {
const { isAuthenticated, isLoading } = useAuth()
const { isAuthenticated, isLoading, user } = useAuth()
const router = useRouter()

useEffect(() => {
Expand All @@ -60,6 +60,13 @@ export default function DashboardLayout({
}
}, [isLoading, isAuthenticated, router])

// Redirect to email verification if authenticated but email not verified
useEffect(() => {
if (!isLoading && isAuthenticated && user && user.emailVerified === false) {
router.push("/verify-email")
}
}, [isLoading, isAuthenticated, user, router])
Comment on lines +63 to +68
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This redirect runs in a useEffect, so for an unverified user the dashboard layout will still render its children for at least one paint before router.push("/verify-email") executes. If the goal is to strictly block access until email verification, also gate rendering (e.g., return a skeleton/null) when user.emailVerified === false to avoid flashing protected UI/content.

Copilot uses AI. Check for mistakes.

// Show skeleton while checking auth
if (isLoading) {
return <DashboardSkeleton />
Expand Down
27 changes: 21 additions & 6 deletions src/app/(dashboard)/profile/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ActivityLogInline } from "@/components/settings/activity-log-inline"
import { ProfileEditForm } from "@/components/settings/profile-edit-form"
import { cn } from "@/lib/utils"
import { useQueryClient } from "@tanstack/react-query"
import { currentUserKeys } from "@/hooks/iam/use-current-user"
import { currentUserKeys, useCurrentUser } from "@/hooks/iam/use-current-user"
import {
User,
KeyRound,
Expand All @@ -32,12 +32,15 @@ const sidebarItems: { id: TabValue; label: string; icon: React.ElementType }[] =

export default function ProfilePage() {
const queryClient = useQueryClient()
const { data: userProfile, isLoading, error } = useUserProfile()
const { data: userProfile, isLoading: isProfileLoading } = useUserProfile()
const { data: currentUser, isLoading: isCurrentUserLoading } = useCurrentUser()
const [activeTab, setActiveTab] = useState<TabValue>("general")
const [twoFactorEnabled, setTwoFactorEnabled] = useState<boolean | undefined>(undefined)

const isLoading = isProfileLoading && isCurrentUserLoading
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isLoading is computed with isProfileLoading && isCurrentUserLoading, which becomes false as soon as either query finishes. That can cause the page to render the "Unable to load profile" state while the other query is still loading (e.g., profile finished with null but current user still fetching). Use an OR loading gate (and/or include a (!isProfileLoading && !isCurrentUserLoading) check before showing the error state) so the UI waits for both queries to settle before deciding there’s no data.

Suggested change
const isLoading = isProfileLoading && isCurrentUserLoading
const isLoading = isProfileLoading || isCurrentUserLoading

Copilot uses AI. Check for mistakes.

// Get current 2FA status - prefer local state, fallback to user data
const is2FAEnabled = twoFactorEnabled ?? userProfile?.user.twoFactorEnabled ?? false
const is2FAEnabled = twoFactorEnabled ?? userProfile?.user.twoFactorEnabled ?? currentUser?.twoFactorEnabled ?? false

const handle2FAStatusChange = (enabled: boolean) => {
setTwoFactorEnabled(enabled)
Expand All @@ -64,7 +67,7 @@ export default function ProfilePage() {
)
}

if (error || !userProfile) {
if (!userProfile && !currentUser) {
return (
<div>
<PageHeader title="Profile" subtitle="Unable to load profile" />
Expand All @@ -87,7 +90,8 @@ export default function ProfilePage() {
)
}

const profileData = {
// Use full profile if available, otherwise fall back to basic auth user data
const profileData = userProfile ? {
fullName: userProfile.detail.fullName,
firstName: userProfile.detail.firstName,
lastName: userProfile.detail.lastName,
Expand All @@ -98,9 +102,20 @@ export default function ProfilePage() {
profilePictureUrl: userProfile.detail.profilePictureUrl,
email: userProfile.user.email,
username: userProfile.user.username,
} : {
fullName: currentUser?.fullName || "",
firstName: "",
lastName: "",
phone: undefined,
position: undefined,
dateOfBirth: undefined,
address: undefined,
profilePictureUrl: currentUser?.profilePictureUrl || undefined,
email: currentUser?.email || "",
username: currentUser?.username || "",
}

const roleCodes = userProfile.roleCodes
const roleCodes = userProfile?.roleCodes ?? (currentUser?.roles || [])

return (
<div>
Expand Down
Loading