mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
feat(i18n): translate dashboard layout components
- Added `Sidebar`, `Header`, and `ModeToggle` namespaces to English dictionary. - Refactored `sidebar.tsx` and `header.tsx` to use dynamic translations. - Updated accessibility labels for navigation and theme switcher.
This commit is contained in:
parent
79565c1fbe
commit
07b20f42d4
@ -13,5 +13,51 @@
|
|||||||
"signIn": "Sign in",
|
"signIn": "Sign in",
|
||||||
"noAccount": "Don't have an account?",
|
"noAccount": "Don't have an account?",
|
||||||
"createAccount": "Create account"
|
"createAccount": "Create account"
|
||||||
|
},
|
||||||
|
"Sidebar": {
|
||||||
|
"title": "CRM Template for WhatsApp",
|
||||||
|
"dashboard": "Dashboard",
|
||||||
|
"inbox": "Inbox",
|
||||||
|
"notifications": "Notifications",
|
||||||
|
"contacts": "Contacts",
|
||||||
|
"pipelines": "Pipelines",
|
||||||
|
"broadcasts": "Broadcasts",
|
||||||
|
"automations": "Automations",
|
||||||
|
"flows": "Flows",
|
||||||
|
"aiAgents": "AI Agents",
|
||||||
|
"settings": "Settings",
|
||||||
|
"beta": "Beta",
|
||||||
|
"unreadConversations": "{count} unread {count, plural, =1 {conversation} other {conversations}}",
|
||||||
|
"unreadNotifications": "{count} unread {count, plural, =1 {notification} other {notifications}}",
|
||||||
|
"roleOwner": "Owner",
|
||||||
|
"roleAdmin": "Admin",
|
||||||
|
"roleAgent": "Agent",
|
||||||
|
"roleViewer": "Viewer",
|
||||||
|
"closeMenu": "Close menu",
|
||||||
|
"defaultUser": "User",
|
||||||
|
"defaultAvatar": "Avatar",
|
||||||
|
"menuProfile": "Profile",
|
||||||
|
"menuSettings": "Settings",
|
||||||
|
"menuSignOut": "Sign out"
|
||||||
|
},
|
||||||
|
"Header": {
|
||||||
|
"dashboard": "Dashboard",
|
||||||
|
"inbox": "Inbox",
|
||||||
|
"notifications": "Notifications",
|
||||||
|
"contacts": "Contacts",
|
||||||
|
"pipelines": "Pipelines",
|
||||||
|
"broadcasts": "Broadcasts",
|
||||||
|
"automations": "Automations",
|
||||||
|
"settings": "Settings",
|
||||||
|
"openMenu": "Open menu",
|
||||||
|
"openAccountMenu": "Open account menu",
|
||||||
|
"defaultUser": "User",
|
||||||
|
"defaultAvatar": "Avatar",
|
||||||
|
"menuProfile": "Profile",
|
||||||
|
"menuSettings": "Settings",
|
||||||
|
"menuSignOut": "Sign out"
|
||||||
|
},
|
||||||
|
"ModeToggle": {
|
||||||
|
"switchMode": "Switch to {mode} mode"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,22 +19,22 @@ import {
|
|||||||
import { ModeToggle } from "@/components/layout/mode-toggle";
|
import { ModeToggle } from "@/components/layout/mode-toggle";
|
||||||
|
|
||||||
const pageTitles: Record<string, string> = {
|
const pageTitles: Record<string, string> = {
|
||||||
"/dashboard": "Dashboard",
|
"/dashboard": "dashboard",
|
||||||
"/inbox": "Inbox",
|
"/inbox": "inbox",
|
||||||
"/notifications": "Notifications",
|
"/notifications": "notifications",
|
||||||
"/contacts": "Contacts",
|
"/contacts": "contacts",
|
||||||
"/pipelines": "Pipelines",
|
"/pipelines": "pipelines",
|
||||||
"/broadcasts": "Broadcasts",
|
"/broadcasts": "broadcasts",
|
||||||
"/automations": "Automations",
|
"/automations": "automations",
|
||||||
"/settings": "Settings",
|
"/settings": "settings",
|
||||||
};
|
};
|
||||||
|
|
||||||
function getPageTitle(pathname: string): string {
|
function getPageTitleKey(pathname: string): string {
|
||||||
if (pageTitles[pathname]) return pageTitles[pathname];
|
if (pageTitles[pathname]) return pageTitles[pathname];
|
||||||
const match = Object.entries(pageTitles).find(([path]) =>
|
const match = Object.entries(pageTitles).find(([path]) =>
|
||||||
pathname.startsWith(path),
|
pathname.startsWith(path),
|
||||||
);
|
);
|
||||||
return match ? match[1] : "Dashboard";
|
return match ? match[1] : "dashboard";
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
@ -43,10 +43,13 @@ interface HeaderProps {
|
|||||||
onOpenSidebar?: () => void;
|
onOpenSidebar?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
export function Header({ onOpenSidebar }: HeaderProps) {
|
export function Header({ onOpenSidebar }: HeaderProps) {
|
||||||
|
const t = useTranslations("Header");
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const { profile, signOut } = useAuth();
|
const { profile, signOut } = useAuth();
|
||||||
const title = getPageTitle(pathname);
|
const titleKey = getPageTitleKey(pathname);
|
||||||
|
|
||||||
const initial =
|
const initial =
|
||||||
profile?.full_name?.charAt(0)?.toUpperCase() ??
|
profile?.full_name?.charAt(0)?.toUpperCase() ??
|
||||||
@ -60,13 +63,13 @@ export function Header({ onOpenSidebar }: HeaderProps) {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onOpenSidebar}
|
onClick={onOpenSidebar}
|
||||||
aria-label="Open menu"
|
aria-label={t("openMenu")}
|
||||||
className="flex h-10 w-10 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground lg:hidden"
|
className="flex h-10 w-10 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground lg:hidden"
|
||||||
>
|
>
|
||||||
<Menu className="h-5 w-5" />
|
<Menu className="h-5 w-5" />
|
||||||
</button>
|
</button>
|
||||||
<h1 className="truncate text-base font-semibold text-foreground sm:text-lg">
|
<h1 className="truncate text-base font-semibold text-foreground sm:text-lg">
|
||||||
{title}
|
{t(titleKey as any)}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -76,13 +79,13 @@ export function Header({ onOpenSidebar }: HeaderProps) {
|
|||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger
|
<DropdownMenuTrigger
|
||||||
className="flex items-center gap-2 rounded-md px-1 py-1 transition-colors hover:bg-muted/70 focus:bg-muted/70 focus:outline-none data-popup-open:bg-muted/70 sm:gap-3 sm:pl-1 sm:pr-3"
|
className="flex items-center gap-2 rounded-md px-1 py-1 transition-colors hover:bg-muted/70 focus:bg-muted/70 focus:outline-none data-popup-open:bg-muted/70 sm:gap-3 sm:pl-1 sm:pr-3"
|
||||||
aria-label="Open account menu"
|
aria-label={t("openAccountMenu")}
|
||||||
>
|
>
|
||||||
<Avatar className="size-8">
|
<Avatar className="size-8">
|
||||||
{profile?.avatar_url ? (
|
{profile?.avatar_url ? (
|
||||||
<AvatarImage
|
<AvatarImage
|
||||||
src={profile.avatar_url}
|
src={profile.avatar_url}
|
||||||
alt={profile.full_name ?? "Avatar"}
|
alt={profile.full_name ?? t("defaultAvatar")}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<AvatarFallback className="bg-primary/10 text-sm font-medium text-primary">
|
<AvatarFallback className="bg-primary/10 text-sm font-medium text-primary">
|
||||||
@ -90,7 +93,7 @@ export function Header({ onOpenSidebar }: HeaderProps) {
|
|||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<span className="hidden text-sm font-medium text-foreground sm:inline">
|
<span className="hidden text-sm font-medium text-foreground sm:inline">
|
||||||
{profile?.full_name ?? "User"}
|
{profile?.full_name ?? t("defaultUser")}
|
||||||
</span>
|
</span>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
@ -100,7 +103,7 @@ export function Header({ onOpenSidebar }: HeaderProps) {
|
|||||||
>
|
>
|
||||||
<div className="px-2 py-1.5">
|
<div className="px-2 py-1.5">
|
||||||
<p className="truncate text-sm font-medium text-foreground">
|
<p className="truncate text-sm font-medium text-foreground">
|
||||||
{profile?.full_name ?? "User"}
|
{profile?.full_name ?? t("defaultUser")}
|
||||||
</p>
|
</p>
|
||||||
<p className="truncate text-xs text-muted-foreground">
|
<p className="truncate text-xs text-muted-foreground">
|
||||||
{profile?.email ?? ""}
|
{profile?.email ?? ""}
|
||||||
@ -116,7 +119,7 @@ export function Header({ onOpenSidebar }: HeaderProps) {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<User className="size-4" />
|
<User className="size-4" />
|
||||||
Profile
|
{t("menuProfile")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
render={
|
render={
|
||||||
@ -127,7 +130,7 @@ export function Header({ onOpenSidebar }: HeaderProps) {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<SettingsIcon className="size-4" />
|
<SettingsIcon className="size-4" />
|
||||||
Settings
|
{t("menuSettings")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator className="bg-border" />
|
<DropdownMenuSeparator className="bg-border" />
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
@ -135,7 +138,7 @@ export function Header({ onOpenSidebar }: HeaderProps) {
|
|||||||
className="text-popover-foreground focus:bg-accent focus:text-accent-foreground"
|
className="text-popover-foreground focus:bg-accent focus:text-accent-foreground"
|
||||||
>
|
>
|
||||||
<LogOut className="size-4" />
|
<LogOut className="size-4" />
|
||||||
Sign out
|
{t("menuSignOut")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import { Moon, Sun } from "lucide-react";
|
|||||||
import { useTheme } from "@/hooks/use-theme";
|
import { useTheme } from "@/hooks/use-theme";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Light/dark mode toggle — a single icon button that flips the app
|
* Light/dark mode toggle — a single icon button that flips the app
|
||||||
* between the two modes. Sun shows in light mode (click → go dark),
|
* between the two modes. Sun shows in light mode (click → go dark),
|
||||||
@ -14,14 +16,17 @@ import { cn } from "@/lib/utils";
|
|||||||
* 40×40 hit target to match the header's other touch controls.
|
* 40×40 hit target to match the header's other touch controls.
|
||||||
*/
|
*/
|
||||||
export function ModeToggle({ className }: { className?: string }) {
|
export function ModeToggle({ className }: { className?: string }) {
|
||||||
|
const t = useTranslations("ModeToggle");
|
||||||
const { mode, toggleMode } = useTheme();
|
const { mode, toggleMode } = useTheme();
|
||||||
const goingTo = mode === "dark" ? "light" : "dark";
|
const goingTo = mode === "dark" ? "light" : "dark";
|
||||||
|
const switchLabel = t("switchMode", { mode: goingTo });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={toggleMode}
|
onClick={toggleMode}
|
||||||
aria-label={`Switch to ${goingTo} mode`}
|
aria-label={switchLabel}
|
||||||
title={`Switch to ${goingTo} mode`}
|
title={switchLabel}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-10 w-10 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
"flex h-10 w-10 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
||||||
className,
|
className,
|
||||||
|
|||||||
@ -34,32 +34,32 @@ import type { AccountRole } from "@/lib/auth/roles";
|
|||||||
// wants to recolour "agent" rows, this is the one diff.
|
// wants to recolour "agent" rows, this is the one diff.
|
||||||
const ROLE_CHIP: Record<
|
const ROLE_CHIP: Record<
|
||||||
AccountRole,
|
AccountRole,
|
||||||
{ icon: typeof Crown; label: string; className: string }
|
{ icon: typeof Crown; labelKey: string; className: string }
|
||||||
> = {
|
> = {
|
||||||
owner: {
|
owner: {
|
||||||
icon: Crown,
|
icon: Crown,
|
||||||
label: "Owner",
|
labelKey: "roleOwner",
|
||||||
// Amber: scarce, immutable, "the boss" — gets visual emphasis.
|
// Amber: scarce, immutable, "the boss" — gets visual emphasis.
|
||||||
className:
|
className:
|
||||||
"border-amber-500/40 bg-amber-500/10 text-amber-300",
|
"border-amber-500/40 bg-amber-500/10 text-amber-300",
|
||||||
},
|
},
|
||||||
admin: {
|
admin: {
|
||||||
icon: Shield,
|
icon: Shield,
|
||||||
label: "Admin",
|
labelKey: "roleAdmin",
|
||||||
// Primary-tinted: significant but not as scarce as owner.
|
// Primary-tinted: significant but not as scarce as owner.
|
||||||
className:
|
className:
|
||||||
"border-primary/40 bg-primary/10 text-primary",
|
"border-primary/40 bg-primary/10 text-primary",
|
||||||
},
|
},
|
||||||
agent: {
|
agent: {
|
||||||
icon: UserCog,
|
icon: UserCog,
|
||||||
label: "Agent",
|
labelKey: "roleAgent",
|
||||||
// Neutral slate: the operational default.
|
// Neutral slate: the operational default.
|
||||||
className:
|
className:
|
||||||
"border-border bg-muted text-foreground",
|
"border-border bg-muted text-foreground",
|
||||||
},
|
},
|
||||||
viewer: {
|
viewer: {
|
||||||
icon: User,
|
icon: User,
|
||||||
label: "Viewer",
|
labelKey: "roleViewer",
|
||||||
// Muted slate: read-only role; visually quieter than agent.
|
// Muted slate: read-only role; visually quieter than agent.
|
||||||
className:
|
className:
|
||||||
"border-border bg-card text-muted-foreground",
|
"border-border bg-card text-muted-foreground",
|
||||||
@ -80,7 +80,7 @@ import {
|
|||||||
|
|
||||||
interface NavItem {
|
interface NavItem {
|
||||||
href: string;
|
href: string;
|
||||||
label: string;
|
labelKey: string;
|
||||||
icon: typeof LayoutDashboard;
|
icon: typeof LayoutDashboard;
|
||||||
/**
|
/**
|
||||||
* When true, the nav row renders a small "Beta" chip after the label.
|
* When true, the nav row renders a small "Beta" chip after the label.
|
||||||
@ -90,19 +90,19 @@ interface NavItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const navItems: NavItem[] = [
|
const navItems: NavItem[] = [
|
||||||
{ href: "/dashboard", label: "Dashboard", icon: LayoutDashboard },
|
{ href: "/dashboard", labelKey: "dashboard", icon: LayoutDashboard },
|
||||||
{ href: "/inbox", label: "Inbox", icon: MessageSquare },
|
{ href: "/inbox", labelKey: "inbox", icon: MessageSquare },
|
||||||
{ href: "/notifications", label: "Notifications", icon: Bell },
|
{ href: "/notifications", labelKey: "notifications", icon: Bell },
|
||||||
{ href: "/contacts", label: "Contacts", icon: Users },
|
{ href: "/contacts", labelKey: "contacts", icon: Users },
|
||||||
{ href: "/pipelines", label: "Pipelines", icon: GitBranch },
|
{ href: "/pipelines", labelKey: "pipelines", icon: GitBranch },
|
||||||
{ href: "/broadcasts", label: "Broadcasts", icon: Radio },
|
{ href: "/broadcasts", labelKey: "broadcasts", icon: Radio },
|
||||||
{ href: "/automations", label: "Automations", icon: Zap },
|
{ href: "/automations", labelKey: "automations", icon: Zap },
|
||||||
{ href: "/flows", label: "Flows", icon: Workflow, beta: true },
|
{ href: "/flows", labelKey: "flows", icon: Workflow, beta: true },
|
||||||
{ href: "/agents", label: "AI Agents", icon: Bot },
|
{ href: "/agents", labelKey: "aiAgents", icon: Bot },
|
||||||
];
|
];
|
||||||
|
|
||||||
const bottomNavItems = [
|
const bottomNavItems = [
|
||||||
{ href: "/settings", label: "Settings", icon: Settings },
|
{ href: "/settings", labelKey: "settings", icon: Settings },
|
||||||
];
|
];
|
||||||
|
|
||||||
interface SidebarProps {
|
interface SidebarProps {
|
||||||
@ -111,7 +111,10 @@ interface SidebarProps {
|
|||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
export function Sidebar({ open = false, onClose }: SidebarProps) {
|
export function Sidebar({ open = false, onClose }: SidebarProps) {
|
||||||
|
const t = useTranslations("Sidebar");
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const { profile, profileLoading, account, accountRole, signOut } = useAuth();
|
const { profile, profileLoading, account, accountRole, signOut } = useAuth();
|
||||||
const totalUnread = useTotalUnread();
|
const totalUnread = useTotalUnread();
|
||||||
@ -160,7 +163,7 @@ export function Sidebar({ open = false, onClose }: SidebarProps) {
|
|||||||
part of the main flex row there. */}
|
part of the main flex row there. */}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="Close menu"
|
aria-label={t("closeMenu")}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed inset-0 z-30 bg-background/70 backdrop-blur-sm transition-opacity lg:hidden",
|
"fixed inset-0 z-30 bg-background/70 backdrop-blur-sm transition-opacity lg:hidden",
|
||||||
@ -189,13 +192,13 @@ export function Sidebar({ open = false, onClose }: SidebarProps) {
|
|||||||
<MessageSquare className="h-4 w-4" />
|
<MessageSquare className="h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm font-semibold text-foreground">
|
<span className="text-sm font-semibold text-foreground">
|
||||||
CRM Template for WhatsApp
|
{t("title")}
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
aria-label="Close menu"
|
aria-label={t("closeMenu")}
|
||||||
className="flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden"
|
className="flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden"
|
||||||
>
|
>
|
||||||
<X className="h-5 w-5" />
|
<X className="h-5 w-5" />
|
||||||
@ -233,18 +236,18 @@ export function Sidebar({ open = false, onClose }: SidebarProps) {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<item.icon className="h-4 w-4" />
|
<item.icon className="h-4 w-4" />
|
||||||
<span className="flex-1">{item.label}</span>
|
<span className="flex-1">{t(item.labelKey as any)}</span>
|
||||||
{item.beta && (
|
{item.beta && (
|
||||||
<span
|
<span
|
||||||
aria-label="Beta feature"
|
aria-label={t("beta")}
|
||||||
className="rounded-full border border-amber-500/40 bg-amber-500/10 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wider text-amber-300"
|
className="rounded-full border border-amber-500/40 bg-amber-500/10 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wider text-amber-300"
|
||||||
>
|
>
|
||||||
Beta
|
{t("beta")}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{showUnreadDot && (
|
{showUnreadDot && (
|
||||||
<span
|
<span
|
||||||
aria-label={`${totalUnread} unread conversation${totalUnread === 1 ? "" : "s"}`}
|
aria-label={t("unreadConversations", { count: totalUnread })}
|
||||||
className="relative flex h-2 w-2"
|
className="relative flex h-2 w-2"
|
||||||
>
|
>
|
||||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-75" />
|
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-75" />
|
||||||
@ -253,7 +256,7 @@ export function Sidebar({ open = false, onClose }: SidebarProps) {
|
|||||||
)}
|
)}
|
||||||
{showNotificationBadge && (
|
{showNotificationBadge && (
|
||||||
<span
|
<span
|
||||||
aria-label={`${unreadNotifications} unread notification${unreadNotifications === 1 ? "" : "s"}`}
|
aria-label={t("unreadNotifications", { count: unreadNotifications })}
|
||||||
className="flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-semibold text-primary-foreground"
|
className="flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-semibold text-primary-foreground"
|
||||||
>
|
>
|
||||||
{unreadNotifications > 9 ? "9+" : unreadNotifications}
|
{unreadNotifications > 9 ? "9+" : unreadNotifications}
|
||||||
@ -282,7 +285,7 @@ export function Sidebar({ open = false, onClose }: SidebarProps) {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<item.icon className="h-4 w-4" />
|
<item.icon className="h-4 w-4" />
|
||||||
{item.label}
|
{t(item.labelKey as any)}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
@ -320,7 +323,7 @@ export function Sidebar({ open = false, onClose }: SidebarProps) {
|
|||||||
className={`ml-auto inline-flex shrink-0 items-center gap-1 rounded-full border px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wider ${meta.className}`}
|
className={`ml-auto inline-flex shrink-0 items-center gap-1 rounded-full border px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wider ${meta.className}`}
|
||||||
>
|
>
|
||||||
<Icon className="size-3" />
|
<Icon className="size-3" />
|
||||||
{meta.label}
|
{t(meta.labelKey as any)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
})()
|
})()
|
||||||
@ -333,7 +336,7 @@ export function Sidebar({ open = false, onClose }: SidebarProps) {
|
|||||||
{profile?.avatar_url ? (
|
{profile?.avatar_url ? (
|
||||||
<AvatarImage
|
<AvatarImage
|
||||||
src={profile.avatar_url}
|
src={profile.avatar_url}
|
||||||
alt={profile.full_name ?? "Avatar"}
|
alt={profile.full_name ?? t("defaultAvatar")}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<AvatarFallback className="bg-primary/10 text-sm font-medium text-primary">
|
<AvatarFallback className="bg-primary/10 text-sm font-medium text-primary">
|
||||||
@ -344,7 +347,7 @@ export function Sidebar({ open = false, onClose }: SidebarProps) {
|
|||||||
</Avatar>
|
</Avatar>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<p className="truncate text-sm font-medium text-foreground">
|
<p className="truncate text-sm font-medium text-foreground">
|
||||||
{profile?.full_name ?? "User"}
|
{profile?.full_name ?? t("defaultUser")}
|
||||||
</p>
|
</p>
|
||||||
<p className="truncate text-xs text-muted-foreground">
|
<p className="truncate text-xs text-muted-foreground">
|
||||||
{profile?.email ?? ""}
|
{profile?.email ?? ""}
|
||||||
@ -367,7 +370,7 @@ export function Sidebar({ open = false, onClose }: SidebarProps) {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<User className="size-4" />
|
<User className="size-4" />
|
||||||
Profile
|
{t("menuProfile")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
render={
|
render={
|
||||||
@ -379,7 +382,7 @@ export function Sidebar({ open = false, onClose }: SidebarProps) {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Settings className="size-4" />
|
<Settings className="size-4" />
|
||||||
Settings
|
{t("menuSettings")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator className="bg-border" />
|
<DropdownMenuSeparator className="bg-border" />
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
@ -387,7 +390,7 @@ export function Sidebar({ open = false, onClose }: SidebarProps) {
|
|||||||
className="text-popover-foreground focus:bg-accent focus:text-accent-foreground"
|
className="text-popover-foreground focus:bg-accent focus:text-accent-foreground"
|
||||||
>
|
>
|
||||||
<LogOut className="size-4" />
|
<LogOut className="size-4" />
|
||||||
Sign out
|
{t("menuSignOut")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user