refactor: improve header and user menu components with responsive plan badge

- Updated the Header component to conditionally render the PlanBadge for larger screens.
- Enhanced the UserMenu component to display a loading state for the plan badge on mobile and link to the plan page when available.
- Introduced new props in UserMenu for better plan management and localization support.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2025-12-06 01:06:18 +08:00
parent 7f9dd316f7
commit f2461f61c6
3 changed files with 48 additions and 9 deletions

View File

@@ -52,13 +52,20 @@ export function Header() {
{/* Right side - User Menu */}
{user && (
<div className="border-fill-tertiary/50 ml-2 sm:ml-auto flex items-center gap-3 border-l pl-2 sm:pl-4">
<PlanBadge
label={planLabel}
isLoading={planQuery.isLoading}
onClick={() => navigate('/plan')}
labelKey="header.plan.badge"
<div className="hidden md:block">
<PlanBadge
label={planLabel}
isLoading={planQuery.isLoading}
onClick={() => navigate('/plan')}
labelKey="header.plan.badge"
/>
</div>
<UserMenu
user={user}
planLabel={planLabel}
planLabelKey="header.plan.badge"
planLoading={planQuery.isLoading}
/>
<UserMenu user={user} />
</div>
)}
</div>

View File

@@ -9,6 +9,7 @@ import {
import { clsxm } from '@afilmory/utils'
import { LogOut, Settings, User as UserIcon } from 'lucide-react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Link } from 'react-router'
import { usePageRedirect } from '~/hooks/usePageRedirect'
@@ -16,12 +17,16 @@ import type { BetterAuthUser } from '~/modules/auth/types'
interface UserMenuProps {
user: BetterAuthUser
planLabel?: string | null
planLabelKey?: I18nKeys
planLoading?: boolean
}
export function UserMenu({ user }: UserMenuProps) {
export function UserMenu({ user, planLabel, planLabelKey = 'header.plan.badge', planLoading }: UserMenuProps) {
const { logout } = usePageRedirect()
const [isLoggingOut, setIsLoggingOut] = useState(false)
const [isOpen, setIsOpen] = useState(false)
const { t } = useTranslation()
const handleLogout = async () => {
if (isLoggingOut) return
@@ -84,6 +89,27 @@ export function UserMenu({ user }: UserMenuProps) {
<DropdownMenuSeparator />
{/* Plan badge for mobile */}
{planLoading && (
<DropdownMenuItem className="md:hidden" disabled>
<div className="flex w-full items-center justify-between text-xs text-text-tertiary">
<span className="font-medium uppercase tracking-wide">{t(planLabelKey)}</span>
<span className="bg-fill/30 h-4 w-12 animate-pulse rounded" aria-hidden="true" />
</div>
</DropdownMenuItem>
)}
{!planLoading && planLabel && (
<DropdownMenuItem className="md:hidden">
<Link to="/plan" className="flex w-full items-center justify-between text-xs">
<span className="text-text-tertiary font-medium uppercase tracking-wide">{t(planLabelKey)}</span>
<span className="text-text font-semibold capitalize">{planLabel}</span>
</Link>
</DropdownMenuItem>
)}
{(planLoading || planLabel) && <DropdownMenuSeparator className="md:hidden" />}
<DropdownMenuItem icon={<UserIcon className="size-4" />}>
<Link to="/settings/account">Account Settings</Link>
</DropdownMenuItem>

View File

@@ -1,7 +1,7 @@
import { FormError, Input, Label } from '@afilmory/ui'
import { useStore } from '@tanstack/react-form'
import type { FC, MutableRefObject } from 'react'
import { useEffect } from 'react'
import { useEffect, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import type { useRegistrationForm } from '~/modules/auth/hooks/useRegistrationForm'
@@ -34,14 +34,20 @@ export const WorkspaceStep: FC<WorkspaceStepProps> = ({
const { t } = useTranslation()
const slugValue = useStore(form.store, (state) => state.values.tenantSlug)
const tenantNameValue = useStore(form.store, (state) => state.values.tenantName)
const tenantNameAutofilledRef = useRef(false)
useEffect(() => {
if (tenantNameValue || !slugValue) {
if (tenantNameAutofilledRef.current || !slugValue) {
return
}
if (tenantNameValue) {
tenantNameAutofilledRef.current = true
return
}
const derivedName = titleCaseFromSlug(slugValue)
if (derivedName) {
form.setFieldValue('tenantName', () => derivedName)
tenantNameAutofilledRef.current = true
}
}, [form, slugValue, tenantNameValue])