diff --git a/frontend/src/components/AppSidebar.css b/frontend/src/components/AppSidebar.css index 1c56bbf3..85f66d6f 100644 --- a/frontend/src/components/AppSidebar.css +++ b/frontend/src/components/AppSidebar.css @@ -30,14 +30,68 @@ letter-spacing: 0; } -.brand-text { - flex: 1 1 auto; +.brand-block { + display: inline-flex; + flex-direction: column; + align-items: center; + min-width: 0; + line-height: 1.1; } -.sider-brand-collapsed .brand-text { +.brand-text { + display: block; +} + +.brand-version { + display: block; + width: 100%; + text-align: center; + font-size: 10px; + font-weight: 500; + letter-spacing: 0; + opacity: 0.6; + margin-top: 2px; +} + +.sider-brand-collapsed .brand-block { + align-items: center; flex: 0 0 auto; } +.brand-actions { + display: inline-flex; + align-items: center; + gap: 2px; + flex-shrink: 0; +} + +.sidebar-donate { + background: transparent; + border: none; + width: 30px; + height: 30px; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + color: rgba(0, 0, 0, 0.75); + text-decoration: none; + flex-shrink: 0; + transition: background-color 0.2s, transform 0.15s, color 0.2s; +} + +.sidebar-donate:hover, +.sidebar-donate:focus-visible { + background-color: rgba(236, 72, 153, 0.12); + color: #ec4899; + transform: scale(1.08); + outline: none; +} + +.sidebar-donate .anticon { + font-size: 16px; +} + .sidebar-theme-cycle { background: transparent; border: none; @@ -197,6 +251,14 @@ html[data-theme='ultra-dark'] .sidebar-theme-cycle { color: rgba(255, 255, 255, 0.92); } +body.dark .sidebar-donate { + color: rgba(255, 255, 255, 0.85); +} + +html[data-theme='ultra-dark'] .sidebar-donate { + color: rgba(255, 255, 255, 0.92); +} + body.dark .ant-drawer .ant-drawer-content, body.dark .ant-drawer .ant-drawer-body { background: #252526 !important; diff --git a/frontend/src/components/AppSidebar.tsx b/frontend/src/components/AppSidebar.tsx index f763216e..27dc0571 100644 --- a/frontend/src/components/AppSidebar.tsx +++ b/frontend/src/components/AppSidebar.tsx @@ -8,6 +8,7 @@ import { ClusterOutlined, CloseOutlined, DashboardOutlined, + HeartOutlined, LogoutOutlined, MenuOutlined, SettingOutlined, @@ -21,6 +22,7 @@ import { pauseAnimationsUntilLeave, useTheme } from '@/hooks/useTheme'; import './AppSidebar.css'; const SIDEBAR_COLLAPSED_KEY = 'isSidebarCollapsed'; +const DONATE_URL = 'https://donate.sanaei.dev/'; interface AppSidebarProps { basePath?: string; @@ -48,6 +50,21 @@ function readCollapsed(): boolean { } } +function DonateButton({ ariaLabel }: { ariaLabel: string }) { + return ( + + + + ); +} + function ThemeCycleButton({ id, isDark, isUltra, onCycle, ariaLabel }: { id: string; isDark: boolean; @@ -92,6 +109,7 @@ export default function AppSidebar({ basePath = '', requestUri = '' }: AppSideba const prefix = basePath.startsWith('/') ? basePath : `/${basePath || ''}`; const currentTheme: 'light' | 'dark' = isDark ? 'dark' : 'light'; + const panelVersion = window.X_UI_CUR_VER || ''; const tabs = useMemo<{ key: string; icon: IconName; title: string }[]>(() => [ { key: `${prefix}panel/`, icon: 'dashboard', title: t('menu.dashboard') }, @@ -165,15 +183,23 @@ export default function AppSidebar({ basePath = '', requestUri = '' }: AppSideba onCollapse={onSiderCollapse} >
- {collapsed ? '3X' : '3X-UI'} +
+ {collapsed ? '3X' : '3X-UI'} + {!collapsed && panelVersion && ( + v{panelVersion} + )} +
{!collapsed && ( - cycleTheme('theme-cycle')} - ariaLabel={t('menu.theme')} - /> +
+ + cycleTheme('theme-cycle')} + ariaLabel={t('menu.theme')} + /> +
)}
setDrawerOpen(false)} >
- 3X-UI +
+ 3X-UI + {panelVersion && v{panelVersion}} +
+ LanguageManager.supportedLanguages.map((l: { value: string; name: string; icon: string }) => ({ - value: l.value, - label: ( - <> - {l.icon} -   {l.name} - - ), - })), + const langList = useMemo( + () => LanguageManager.supportedLanguages as { value: string; name: string; icon: string }[], [], ); @@ -154,26 +144,31 @@ export default function LoginPage() { -