"use client";
import {
Check,
ChevronUp,
Download,
ExternalLink,
Info,
Languages,
LogOut,
Megaphone,
Monitor,
Moon,
Sun,
UserCog,
} from "lucide-react";
import Image from "next/image";
import { useTranslations } from "next-intl";
import { useState } from "react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { useLocaleContext } from "@/contexts/LocaleContext";
import { useMediaQuery } from "@/hooks/use-media-query";
import { usePlatform } from "@/hooks/use-platform";
import { GITHUB_RELEASES_URL, usePrimaryDownload } from "@/lib/desktop-download-utils";
import { APP_VERSION } from "@/lib/env-config";
import { trackDesktopDownloadClicked } from "@/lib/posthog/events";
import { getUserAvatarColor, getUserInitials } from "@/lib/user-avatar";
import { cn } from "@/lib/utils";
import type { User } from "../../types/layout.types";
// Supported languages configuration
const LANGUAGES = [
{ code: "en" as const, name: "English", flag: "🇺🇸" },
{ code: "es" as const, name: "Español", flag: "🇪🇸" },
{ code: "pt" as const, name: "Português", flag: "🇧🇷" },
{ code: "hi" as const, name: "हिन्दी", flag: "🇮🇳" },
{ code: "zh" as const, name: "简体中文", flag: "🇨🇳" },
];
// Supported themes configuration
const THEMES = [
{ value: "light" as const, name: "Light", icon: Sun },
{ value: "dark" as const, name: "Dark", icon: Moon },
{ value: "system" as const, name: "System", icon: Monitor },
];
const LEARN_MORE_LINKS = [
{ key: "documentation" as const, href: "https://www.surfsense.com/docs" },
{ key: "github" as const, href: "https://github.com/MODSetter/SurfSense" },
];
interface SidebarUserProfileProps {
user: User;
onUserSettings?: () => void;
onAnnouncements?: () => void;
announcementUnreadCount?: number;
onLogout?: () => void;
isCollapsed?: boolean;
theme?: string;
setTheme?: (theme: "light" | "dark" | "system") => void;
}
function formatAnnouncementCount(count: number): string {
if (count <= 999) {
return count.toString();
}
const thousands = Math.floor(count / 1000);
return `${thousands}k+`;
}
/**
* User avatar component - shows image if available, otherwise falls back to initials
*/
function UserAvatar({
avatarUrl,
initials,
bgColor,
size = "sm",
}: {
avatarUrl?: string;
initials: string;
bgColor: string;
size?: "sm" | "md";
}) {
const sizeClass = size === "md" ? "h-10 w-10" : "h-8 w-8";
if (avatarUrl) {
return (
);
}
return (
{initials}
);
}
export function SidebarUserProfile({
user,
onUserSettings,
onAnnouncements,
announcementUnreadCount = 0,
onLogout,
isCollapsed = false,
theme,
setTheme,
}: SidebarUserProfileProps) {
const t = useTranslations("sidebar");
const { locale, setLocale } = useLocaleContext();
const { isDesktop } = usePlatform();
const isDesktopViewport = useMediaQuery("(min-width: 768px)");
const { os, primary } = usePrimaryDownload();
const [isLoggingOut, setIsLoggingOut] = useState(false);
const bgColor = getUserAvatarColor(user.email);
const initials = getUserInitials(user.email);
const displayName = user.name || user.email.split("@")[0];
const downloadUrl = primary?.url ?? GITHUB_RELEASES_URL;
const downloadLabel = t("download_for_os", { os });
const showDownloadCta = !isDesktop && isDesktopViewport;
const handleLanguageChange = (newLocale: "en" | "es" | "pt" | "hi" | "zh") => {
setLocale(newLocale);
};
const handleThemeChange = (newTheme: "light" | "dark" | "system") => {
setTheme?.(newTheme);
};
const handleLogout = async () => {
if (isLoggingOut || !onLogout) return;
setIsLoggingOut(true);
try {
await onLogout();
} finally {
setIsLoggingOut(false);
}
};
// Collapsed view - just show avatar with dropdown
if (isCollapsed) {
return (
{showDownloadCta && (
{downloadLabel}
)}
{displayName}
{user.email}
{t("user_settings")}
{onAnnouncements && (
What's New
{announcementUnreadCount > 0 && (
{formatAnnouncementCount(announcementUnreadCount)}
)}
)}
{setTheme && (
{t("theme")}
{THEMES.map((themeOption) => {
const Icon = themeOption.icon;
const isSelected = theme === themeOption.value;
return (
handleThemeChange(themeOption.value)}
className={cn(
"mb-1 last:mb-0 transition-all",
"hover:bg-accent hover:text-accent-foreground",
isSelected && "text-primary"
)}
>
{t(themeOption.value)}
{isSelected && }
);
})}
)}
{t("language")}
{LANGUAGES.map((language) => {
const isSelected = locale === language.code;
return (
handleLanguageChange(language.code)}
className={cn(
"mb-1 last:mb-0 transition-all",
"hover:bg-accent hover:text-accent-foreground",
isSelected && "text-primary"
)}
>
{language.flag}
{language.name}
{isSelected && }
);
})}
{t("learn_more")}
{LEARN_MORE_LINKS.map((link) => (
{t(link.key)}
))}
v{APP_VERSION}
{!isDesktop && (
{downloadLabel}
)}
{isLoggingOut ? (
) : (
)}
{isLoggingOut ? t("loggingOut") : t("logout")}
);
}
// Expanded view
return (
{showDownloadCta && (
)}
{displayName}
{user.email}
{t("user_settings")}
{onAnnouncements && (
What's New
{announcementUnreadCount > 0 && (
{formatAnnouncementCount(announcementUnreadCount)}
)}
)}
{setTheme && (
{t("theme")}
{THEMES.map((themeOption) => {
const Icon = themeOption.icon;
const isSelected = theme === themeOption.value;
return (
handleThemeChange(themeOption.value)}
className={cn(
"mb-1 last:mb-0 transition-all",
"hover:bg-accent hover:text-accent-foreground",
isSelected && "text-primary"
)}
>
{t(themeOption.value)}
{isSelected && }
);
})}
)}
{t("language")}
{LANGUAGES.map((language) => {
const isSelected = locale === language.code;
return (
handleLanguageChange(language.code)}
className={cn(
"mb-1 last:mb-0 transition-all",
"hover:bg-accent hover:text-accent-foreground",
isSelected && "text-primary"
)}
>
{language.flag}
{language.name}
{isSelected && }
);
})}
{t("learn_more")}
{LEARN_MORE_LINKS.map((link) => (
{t(link.key)}
))}
v{APP_VERSION}
{!isDesktop && (
{downloadLabel}
)}
{isLoggingOut ? : }
{isLoggingOut ? t("loggingOut") : t("logout")}
);
}