add glowing effect component for tab tiles improve tab tile appearance with new glow effect add 'none' log level and simplify log level handling simplify tab configuration store by removing developer tabs remove useDebugStatus hook and related debug functionality remove system info endpoints no longer needed
141 lines
5.5 KiB
TypeScript
141 lines
5.5 KiB
TypeScript
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
|
import { motion } from 'framer-motion';
|
|
import { useStore } from '@nanostores/react';
|
|
import { classNames } from '~/utils/classNames';
|
|
import { profileStore } from '~/lib/stores/profile';
|
|
import type { TabType, Profile } from './types';
|
|
|
|
const BetaLabel = () => (
|
|
<span className="px-1.5 py-0.5 rounded-full bg-purple-500/10 dark:bg-purple-500/20 text-[10px] font-medium text-purple-600 dark:text-purple-400 ml-2">
|
|
BETA
|
|
</span>
|
|
);
|
|
|
|
interface AvatarDropdownProps {
|
|
onSelectTab: (tab: TabType) => void;
|
|
}
|
|
|
|
export const AvatarDropdown = ({ onSelectTab }: AvatarDropdownProps) => {
|
|
const profile = useStore(profileStore) as Profile;
|
|
|
|
return (
|
|
<DropdownMenu.Root>
|
|
<DropdownMenu.Trigger asChild>
|
|
<motion.button
|
|
className="w-10 h-10 rounded-full bg-transparent flex items-center justify-center focus:outline-none"
|
|
whileHover={{ scale: 1.02 }}
|
|
whileTap={{ scale: 0.98 }}
|
|
>
|
|
{profile?.avatar ? (
|
|
<img
|
|
src={profile.avatar}
|
|
alt={profile?.username || 'Profile'}
|
|
className="w-full h-full rounded-full object-cover"
|
|
loading="eager"
|
|
decoding="sync"
|
|
/>
|
|
) : (
|
|
<div className="w-full h-full rounded-full flex items-center justify-center bg-white dark:bg-gray-800 text-gray-400 dark:text-gray-500">
|
|
<div className="i-ph:user w-6 h-6" />
|
|
</div>
|
|
)}
|
|
</motion.button>
|
|
</DropdownMenu.Trigger>
|
|
|
|
<DropdownMenu.Portal>
|
|
<DropdownMenu.Content
|
|
className={classNames(
|
|
'min-w-[240px] z-[250]',
|
|
'bg-white dark:bg-[#141414]',
|
|
'rounded-lg shadow-lg',
|
|
'border border-gray-200/50 dark:border-gray-800/50',
|
|
'animate-in fade-in-0 zoom-in-95',
|
|
'py-1',
|
|
)}
|
|
sideOffset={5}
|
|
align="end"
|
|
>
|
|
<div
|
|
className={classNames(
|
|
'px-4 py-3 flex items-center gap-3',
|
|
'border-b border-gray-200/50 dark:border-gray-800/50',
|
|
)}
|
|
>
|
|
<div className="w-10 h-10 rounded-full overflow-hidden flex-shrink-0 bg-white dark:bg-gray-800 shadow-sm">
|
|
{profile?.avatar ? (
|
|
<img
|
|
src={profile.avatar}
|
|
alt={profile?.username || 'Profile'}
|
|
className={classNames('w-full h-full', 'object-cover', 'transform-gpu', 'image-rendering-crisp')}
|
|
loading="eager"
|
|
decoding="sync"
|
|
/>
|
|
) : (
|
|
<div className="w-full h-full flex items-center justify-center text-gray-400 dark:text-gray-500 font-medium text-lg">
|
|
<div className="i-ph:user w-6 h-6" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="font-medium text-sm text-gray-900 dark:text-white truncate">
|
|
{profile?.username || 'Guest User'}
|
|
</div>
|
|
{profile?.bio && <div className="text-xs text-gray-500 dark:text-gray-400 truncate">{profile.bio}</div>}
|
|
</div>
|
|
</div>
|
|
|
|
<DropdownMenu.Item
|
|
className={classNames(
|
|
'flex items-center gap-2 px-4 py-2.5',
|
|
'text-sm text-gray-700 dark:text-gray-200',
|
|
'hover:bg-purple-50 dark:hover:bg-purple-500/10',
|
|
'hover:text-purple-500 dark:hover:text-purple-400',
|
|
'cursor-pointer transition-all duration-200',
|
|
'outline-none',
|
|
'group',
|
|
)}
|
|
onClick={() => onSelectTab('profile')}
|
|
>
|
|
<div className="i-ph:user-circle w-4 h-4 text-gray-400 group-hover:text-purple-500 dark:group-hover:text-purple-400 transition-colors" />
|
|
Edit Profile
|
|
</DropdownMenu.Item>
|
|
|
|
<DropdownMenu.Item
|
|
className={classNames(
|
|
'flex items-center gap-2 px-4 py-2.5',
|
|
'text-sm text-gray-700 dark:text-gray-200',
|
|
'hover:bg-purple-50 dark:hover:bg-purple-500/10',
|
|
'hover:text-purple-500 dark:hover:text-purple-400',
|
|
'cursor-pointer transition-all duration-200',
|
|
'outline-none',
|
|
'group',
|
|
)}
|
|
onClick={() => onSelectTab('settings')}
|
|
>
|
|
<div className="i-ph:gear-six w-4 h-4 text-gray-400 group-hover:text-purple-500 dark:group-hover:text-purple-400 transition-colors" />
|
|
Settings
|
|
</DropdownMenu.Item>
|
|
|
|
<div className="my-1 border-t border-gray-200/50 dark:border-gray-800/50" />
|
|
<DropdownMenu.Item
|
|
className={classNames(
|
|
'flex items-center gap-2 px-4 py-2.5',
|
|
'text-sm text-gray-700 dark:text-gray-200',
|
|
'hover:bg-purple-50 dark:hover:bg-purple-500/10',
|
|
'hover:text-purple-500 dark:hover:text-purple-400',
|
|
'cursor-pointer transition-all duration-200',
|
|
'outline-none',
|
|
'group',
|
|
)}
|
|
onClick={() => onSelectTab('service-status')}
|
|
>
|
|
<div className="i-ph:heartbeat w-4 h-4 text-gray-400 group-hover:text-purple-500 dark:group-hover:text-purple-400 transition-colors" />
|
|
Service Status
|
|
<BetaLabel />
|
|
</DropdownMenu.Item>
|
|
</DropdownMenu.Content>
|
|
</DropdownMenu.Portal>
|
|
</DropdownMenu.Root>
|
|
);
|
|
};
|