mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-25 08:35:42 +02:00
Improve sidebar button ui
This commit is contained in:
1
.astro/types.d.ts
vendored
1
.astro/types.d.ts
vendored
@@ -1,2 +1 @@
|
|||||||
/// <reference types="astro/client" />
|
/// <reference types="astro/client" />
|
||||||
/// <reference path="content.d.ts" />
|
|
@@ -1,21 +1,10 @@
|
|||||||
import {
|
import { BookOpen, Compass, Plus, Star, X, Zap } from 'lucide-react';
|
||||||
BookOpen,
|
|
||||||
Compass,
|
|
||||||
CreditCardIcon,
|
|
||||||
LogOut,
|
|
||||||
LogOutIcon,
|
|
||||||
Plus,
|
|
||||||
Star,
|
|
||||||
X,
|
|
||||||
Zap,
|
|
||||||
} from 'lucide-react';
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { isLoggedIn } from '../../lib/jwt';
|
import { isLoggedIn } from '../../lib/jwt';
|
||||||
import { useIsPaidUser } from '../../queries/billing';
|
import { useIsPaidUser } from '../../queries/billing';
|
||||||
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
|
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
|
||||||
import { AITutorLogo } from '../ReactIcons/AITutorLogo';
|
import { AITutorLogo } from '../ReactIcons/AITutorLogo';
|
||||||
import { cn } from '../../lib/classname';
|
import { cn } from '../../lib/classname';
|
||||||
import { logout } from '../../lib/auth';
|
|
||||||
import { UserDropdown } from './UserDropdown';
|
import { UserDropdown } from './UserDropdown';
|
||||||
|
|
||||||
type AITutorSidebarProps = {
|
type AITutorSidebarProps = {
|
||||||
@@ -145,36 +134,9 @@ export function AITutorSidebar(props: AITutorSidebarProps) {
|
|||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{/* {isLoggedIn() && (
|
|
||||||
<div className="mt-auto mb-2 space-y-1">
|
|
||||||
<AITutorSidebarItem
|
|
||||||
item={{
|
|
||||||
key: 'billing',
|
|
||||||
label: 'Billing',
|
|
||||||
href: '/account/billing',
|
|
||||||
icon: CreditCardIcon,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<AITutorSidebarItem
|
|
||||||
item={{
|
|
||||||
key: 'logout',
|
|
||||||
label: 'Logout',
|
|
||||||
icon: LogOutIcon,
|
|
||||||
href: '/logout',
|
|
||||||
}}
|
|
||||||
as="button"
|
|
||||||
isActive={false}
|
|
||||||
onClick={logout}
|
|
||||||
className="hover:text-red-500"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)} */}
|
|
||||||
{isLoggedIn() && (
|
|
||||||
<div className="mx-2 mt-auto mb-2">
|
<div className="mx-2 mt-auto mb-2">
|
||||||
<UserDropdown />
|
<UserDropdown />
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</aside>
|
</aside>
|
||||||
{isFloating && (
|
{isFloating && (
|
||||||
<div className="fixed inset-0 z-40 bg-black/50" onClick={onClose} />
|
<div className="fixed inset-0 z-40 bg-black/50" onClick={onClose} />
|
||||||
|
@@ -1,10 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
ChevronsUpDownIcon,
|
ChevronDown,
|
||||||
CogIcon,
|
|
||||||
CreditCardIcon,
|
CreditCardIcon,
|
||||||
|
LogInIcon,
|
||||||
LogOutIcon,
|
LogOutIcon,
|
||||||
UserIcon,
|
Settings,
|
||||||
|
User2,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { useAuth } from '../../hooks/use-auth';
|
||||||
|
import { useClientMount } from '../../hooks/use-client-mount';
|
||||||
|
import { logout } from '../../lib/auth';
|
||||||
|
import { showLoginPopup } from '../../lib/popup';
|
||||||
|
import { useIsPaidUser } from '../../queries/billing';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@@ -12,16 +18,28 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '../DropdownMenu';
|
} from '../DropdownMenu';
|
||||||
import { useAuth } from '../../hooks/use-auth';
|
|
||||||
import { logout } from '../../lib/auth';
|
|
||||||
|
|
||||||
type UserDropdownProps = {};
|
type UserDropdownProps = {};
|
||||||
|
|
||||||
export function UserDropdown(props: UserDropdownProps) {
|
export function UserDropdown(props: UserDropdownProps) {
|
||||||
const currentUser = useAuth();
|
const currentUser = useAuth();
|
||||||
|
const { isPaidUser, isLoading } = useIsPaidUser();
|
||||||
|
const isMounted = useClientMount();
|
||||||
|
|
||||||
|
if (!isMounted || isLoading) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
return null;
|
return (
|
||||||
|
<button
|
||||||
|
onClick={showLoginPopup}
|
||||||
|
className="animate-fade-in inline-flex h-auto w-full items-center justify-center gap-2 rounded-lg border border-gray-700 bg-black px-4 py-2.5 text-sm font-medium text-white transition-all duration-200 outline-none hover:!opacity-80 disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
|
>
|
||||||
|
<LogInIcon className="size-4" />
|
||||||
|
Free Signup or Login
|
||||||
|
</button>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userAvatar = currentUser?.avatar
|
const userAvatar = currentUser?.avatar
|
||||||
@@ -31,58 +49,65 @@ export function UserDropdown(props: UserDropdownProps) {
|
|||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<button className="inline-flex h-auto w-full items-center justify-between gap-2 rounded-lg border border-transparent px-4 py-2 text-sm font-medium outline-none hover:bg-gray-100 hover:text-black focus-visible:border-gray-300 focus-visible:bg-gray-100 focus-visible:text-black disabled:cursor-not-allowed disabled:opacity-60 [&_svg]:pointer-events-none [&_svg]:shrink-0">
|
<button className="group flex w-full items-center gap-3 rounded-lg border border-transparent px-4 py-2.5 text-sm font-medium transition-colors hover:bg-gray-100 hover:text-black focus:outline-none data-[state=open]:bg-gray-100 data-[state=open]:text-black">
|
||||||
<div className="flex min-w-0 items-center gap-2.5">
|
|
||||||
<div className="relative size-7 shrink-0 overflow-hidden rounded-full">
|
<div className="relative size-7 shrink-0 overflow-hidden rounded-full">
|
||||||
<img src={userAvatar} className="absolute inset-0 object-cover" />
|
<img
|
||||||
|
src={userAvatar}
|
||||||
|
alt={currentUser.name}
|
||||||
|
className="absolute inset-0 h-full w-full object-cover"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex min-w-0 flex-col gap-0.5 text-left">
|
<div className="flex min-w-0 flex-1 flex-col text-left">
|
||||||
<h3 className="truncate text-sm font-medium">
|
<span className="truncate font-medium text-gray-900">
|
||||||
{currentUser.name}
|
{currentUser.name}
|
||||||
</h3>
|
</span>
|
||||||
<p className="truncate text-xs text-gray-500">
|
<span className="truncate text-xs text-gray-500">
|
||||||
{currentUser.email}
|
{isPaidUser ? 'Pro Member' : 'Free User'}
|
||||||
</p>
|
</span>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ChevronsUpDownIcon className="ml-auto size-3.5" />
|
<ChevronDown className="size-4 text-gray-400 transition-transform duration-200 group-data-[state=open]:rotate-180" />
|
||||||
</button>
|
</button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="w-[var(--radix-dropdown-menu-trigger-width)] min-w-full p-0.5">
|
|
||||||
<div className="flex h-auto flex-col justify-between gap-0.5 p-2">
|
|
||||||
<div className="flex min-w-0 items-center gap-2.5">
|
|
||||||
<div className="relative size-8 shrink-0 overflow-hidden rounded-full">
|
|
||||||
<img src={userAvatar} className="absolute inset-0 object-cover" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex min-w-0 flex-col gap-0.5 text-left">
|
<DropdownMenuContent className="w-[var(--radix-dropdown-menu-trigger-width)] min-w-52 rounded-lg border border-gray-200 bg-white p-1">
|
||||||
<h3 className="truncate text-sm font-medium">
|
<div className="space-y-1">
|
||||||
{currentUser.name}
|
|
||||||
</h3>
|
|
||||||
<p className="truncate text-xs text-gray-500">
|
|
||||||
{currentUser.email}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
|
|
||||||
<div className="space-y-0.5">
|
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<a href="/account/billing">
|
<a
|
||||||
|
href="/account"
|
||||||
|
className="flex w-full items-center gap-3 rounded px-3 py-2 text-sm font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-black"
|
||||||
|
>
|
||||||
|
<User2 className="size-4" />
|
||||||
|
Account
|
||||||
|
</a>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
|
||||||
|
<DropdownMenuItem asChild>
|
||||||
|
<a
|
||||||
|
href="/account/billing"
|
||||||
|
className="flex w-full items-center gap-3 rounded px-3 py-2 text-sm font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-black"
|
||||||
|
>
|
||||||
<CreditCardIcon className="size-4" />
|
<CreditCardIcon className="size-4" />
|
||||||
Billing
|
Billing
|
||||||
</a>
|
</a>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
|
<DropdownMenuItem asChild>
|
||||||
|
<a
|
||||||
|
href="/account/settings"
|
||||||
|
className="flex w-full items-center gap-3 rounded px-3 py-2 text-sm font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-black"
|
||||||
|
>
|
||||||
|
<Settings className="size-4" />
|
||||||
|
Settings
|
||||||
|
</a>
|
||||||
|
</DropdownMenuItem>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator className="my-1" />
|
||||||
|
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="text-red-600 focus:bg-red-100 focus:text-red-600"
|
className="flex w-full items-center gap-3 rounded px-3 py-2 text-sm font-medium text-red-600 transition-colors hover:bg-red-50 hover:text-red-700"
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
logout();
|
logout();
|
||||||
}}
|
}}
|
||||||
|
11
src/hooks/use-client-mount.ts
Normal file
11
src/hooks/use-client-mount.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export function useClientMount() {
|
||||||
|
const [isMounted, setIsMounted] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsMounted(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return isMounted;
|
||||||
|
}
|
Reference in New Issue
Block a user