diff --git a/apps/web/src/components/AppSideBar.tsx b/apps/web/src/components/AppSideBar.tsx
index 247ef00..317c9ad 100644
--- a/apps/web/src/components/AppSideBar.tsx
+++ b/apps/web/src/components/AppSideBar.tsx
@@ -19,6 +19,13 @@ import {
BookOpenText,
ChartColumnBig,
ChartArea,
+ BellIcon,
+ CreditCardIcon,
+ LogOutIcon,
+ MoreVerticalIcon,
+ UserCircleIcon,
+ UsersIcon,
+ GaugeIcon,
} from "lucide-react";
import { signOut } from "next-auth/react";
@@ -41,6 +48,16 @@ import { useSession } from "next-auth/react";
import { isSelfHosted } from "~/utils/common";
import { usePathname } from "next/navigation";
import { Badge } from "@unsend/ui/src/badge";
+import { Avatar, AvatarFallback, AvatarImage } from "@unsend/ui/src/avatar";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@unsend/ui/src/dropdown-menu";
// General items
const generalItems = [
@@ -211,12 +228,6 @@ export function AppSidebar() {
-
- signOut()} tooltip="Logout">
-
- Logout
-
-
@@ -225,12 +236,116 @@ export function AppSidebar() {
-
-
-
+
);
}
+
+export function NavUser({
+ user,
+}: {
+ user: {
+ name?: string | null;
+ email?: string | null;
+ avatar?: string | null;
+ };
+}) {
+ const { isMobile } = useSidebar();
+
+ return (
+
+
+
+
+
+
+ {user.avatar ? (
+
+ ) : null}
+
+ {user.name?.charAt(0) ?? user.email?.charAt(0) ?? ""}
+
+
+
+
+ {user.name ?? user.email ?? ""}
+
+
+ {user.name ? user.email : ""}
+
+
+
+
+
+
+
+
+
+ {user.avatar ? (
+
+ ) : null}
+
+ {user.name?.charAt(0) ?? user.email?.charAt(0) ?? ""}
+
+
+
+
+ {user.name ?? user.email ?? ""}
+
+
+ {user.name ? user.email : ""}
+
+
+
+
+
+
+
+
+
+ Team
+
+
+
+
+
+ Usage
+
+
+
+
+
+
+
+ signOut()}>
+
+ Log out
+
+
+
+
+
+ );
+}
diff --git a/apps/web/src/components/theme/ThemeSwitcher.tsx b/apps/web/src/components/theme/ThemeSwitcher.tsx
index 6c6948b..e3a090e 100644
--- a/apps/web/src/components/theme/ThemeSwitcher.tsx
+++ b/apps/web/src/components/theme/ThemeSwitcher.tsx
@@ -7,7 +7,7 @@ export const ThemeSwitcher = () => {
return (
-
+
Theme
diff --git a/packages/ui/package.json b/packages/ui/package.json
index ca7877c..8139aeb 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -31,6 +31,7 @@
"dependencies": {
"@hookform/resolvers": "^5.0.1",
"@radix-ui/react-accordion": "^1.2.8",
+ "@radix-ui/react-avatar": "^1.1.9",
"@radix-ui/react-dialog": "^1.1.11",
"@radix-ui/react-dropdown-menu": "^2.1.12",
"@radix-ui/react-label": "^2.1.4",
diff --git a/packages/ui/src/avatar.tsx b/packages/ui/src/avatar.tsx
new file mode 100644
index 0000000..274be97
--- /dev/null
+++ b/packages/ui/src/avatar.tsx
@@ -0,0 +1,50 @@
+"use client";
+
+import * as React from "react";
+import * as AvatarPrimitive from "@radix-ui/react-avatar";
+
+import { cn } from "../lib/utils";
+
+const Avatar = React.forwardRef<
+ React.ComponentRef
,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+Avatar.displayName = AvatarPrimitive.Root.displayName;
+
+const AvatarImage = React.forwardRef<
+ React.ComponentRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AvatarImage.displayName = AvatarPrimitive.Image.displayName;
+
+const AvatarFallback = React.forwardRef<
+ React.ComponentRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
+
+export { Avatar, AvatarImage, AvatarFallback };
diff --git a/packages/ui/styles/globals.css b/packages/ui/styles/globals.css
index e3e0785..1f33284 100644
--- a/packages/ui/styles/globals.css
+++ b/packages/ui/styles/globals.css
@@ -12,7 +12,7 @@
--card-foreground: 222.2 84% 4.9%;
--popover: 220 2% 96%;
- --popover-foreground: 222.2 84% 4.9%;
+ --popover-foreground: 234 16% 35%;
--primary: 200 65% 14%;
--primary-foreground: 210 40% 98%;
@@ -62,7 +62,7 @@
--card-foreground: 210 40% 98%;
--popover: 240 21% 15%;
- --popover-foreground: 210 40% 98%;
+ --popover-foreground: 226 64% 88%;
--primary: 220 23% 95%;
--primary-foreground: 240 23% 9%;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1b0c4b6..b2172b8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -527,6 +527,9 @@ importers:
'@radix-ui/react-accordion':
specifier: ^1.2.8
version: 1.2.8(@types/react-dom@19.1.2)(@types/react@19.1.2)(react-dom@19.1.0)(react@19.1.0)
+ '@radix-ui/react-avatar':
+ specifier: ^1.1.9
+ version: 1.1.9(@types/react-dom@19.1.2)(@types/react@19.1.2)(react-dom@19.1.0)(react@19.1.0)
'@radix-ui/react-dialog':
specifier: ^1.1.11
version: 1.1.11(@types/react-dom@19.1.2)(@types/react@19.1.2)(react-dom@19.1.0)(react@19.1.0)
@@ -4023,6 +4026,30 @@ packages:
react-dom: 19.1.0(react@19.1.0)
dev: false
+ /@radix-ui/react-avatar@1.1.9(@types/react-dom@19.1.2)(@types/react@19.1.2)(react-dom@19.1.0)(react@19.1.0):
+ resolution: {integrity: sha512-10tQokfvZdFvnvDkcOJPjm2pWiP8A0R4T83MoD7tb15bC/k2GU7B1YBuzJi8lNQ8V1QqhP8ocNqp27ByZaNagQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@radix-ui/react-context': 1.1.2(@types/react@19.1.2)(react@19.1.0)
+ '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.2)(@types/react@19.1.2)(react-dom@19.1.0)(react@19.1.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.2)(react@19.1.0)
+ '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.1.2)(react@19.1.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.2)(react@19.1.0)
+ '@types/react': 19.1.2
+ '@types/react-dom': 19.1.2(@types/react@19.1.2)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ dev: false
+
/@radix-ui/react-collapsible@1.1.1(@types/react@19.1.2)(react-dom@19.1.0)(react@19.1.0):
resolution: {integrity: sha512-1///SnrfQHJEofLokyczERxQbWfCGQlQ2XsCZMucVs6it+lq9iw4vXy+uDn1edlb58cOZOWSldnfPAYcT4O/Yg==}
peerDependencies:
@@ -4725,6 +4752,26 @@ packages:
react-dom: 19.1.0(react@19.1.0)
dev: false
+ /@radix-ui/react-primitive@2.1.2(@types/react-dom@19.1.2)(@types/react@19.1.2)(react-dom@19.1.0)(react@19.1.0):
+ resolution: {integrity: sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@radix-ui/react-slot': 1.2.2(@types/react@19.1.2)(react@19.1.0)
+ '@types/react': 19.1.2
+ '@types/react-dom': 19.1.2(@types/react@19.1.2)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ dev: false
+
/@radix-ui/react-progress@1.1.4(@types/react-dom@19.1.2)(@types/react@19.1.2)(react-dom@19.1.0)(react@19.1.0):
resolution: {integrity: sha512-8rl9w7lJdcVPor47Dhws9mUHRHLE+8JEgyJRdNWCpGPa6HIlr3eh+Yn9gyx1CnCLbw5naHsI2gaO9dBWO50vzw==}
peerDependencies:
@@ -4889,6 +4936,20 @@ packages:
react: 19.1.0
dev: false
+ /@radix-ui/react-slot@1.2.2(@types/react@19.1.2)(react@19.1.0):
+ resolution: {integrity: sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.2)(react@19.1.0)
+ '@types/react': 19.1.2
+ react: 19.1.0
+ dev: false
+
/@radix-ui/react-switch@1.2.2(@types/react-dom@19.1.2)(@types/react@19.1.2)(react-dom@19.1.0)(react@19.1.0):
resolution: {integrity: sha512-7Z8n6L+ifMIIYZ83f28qWSceUpkXuslI2FJ34+kDMTiyj91ENdpdQ7VCidrzj5JfwfZTeano/BnGBbu/jqa5rQ==}
peerDependencies:
@@ -5116,6 +5177,20 @@ packages:
react: 19.1.0
dev: false
+ /@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.1.2)(react@19.1.0):
+ resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 19.1.2
+ react: 19.1.0
+ use-sync-external-store: 1.5.0(react@19.1.0)
+ dev: false
+
/@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.1.2)(react@19.1.0):
resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==}
peerDependencies: