| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- import * as React from 'react';
- import { NavItem, NavItemKey } from '../types';
- import { Icon } from './ui/Icon';
- import { useTranslation, Language } from '../hooks/useI18n';
- interface SidebarProps {
- activePageName: string;
- activePageThemeColor: string;
- onOpenPageManager: () => void;
- activeNavKey: NavItemKey;
- setActiveNavKey: (page: NavItemKey) => void;
- theme: 'light' | 'dark';
- setTheme: (theme: 'light' | 'dark') => void;
- currentUser: string;
- onLogout: () => void;
- }
- const NavMenu: React.FC<{
- activeKey: NavItemKey;
- onSelect: (key: NavItemKey) => void;
- }> = ({ activeKey, onSelect }) => {
- const { t } = useTranslation();
- const navItems: NavItem[] = [
- {
- name: t('nav.page'),
- key: 'Page',
- icon: <path strokeLinecap="round" strokeLinejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />,
- children: [
- { name: t('nav.page.link'), key: 'Page.Link' },
- { name: t('nav.page.media'), key: 'Page.Media' },
- { name: t('nav.page.design'), key: 'Page.Design' },
- ],
- },
- {
- name: t('nav.ai_assistant'),
- key: 'AI Assistant',
- icon: <path strokeLinecap="round" strokeLinejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />,
- children: [
- { name: t('nav.ai_assistant.persona'), key: 'AI Assistant.Persona' },
- { name: t('nav.ai_assistant.knowledge'), key: 'AI Assistant.Knowledge' },
- { name: t('nav.ai_assistant.sensitivity'), key: 'AI Assistant.Sensitivity' },
- ],
- },
- {
- name: t('nav.analytics'),
- key: 'Analytics',
- icon: <path strokeLinecap="round" strokeLinejoin="round" d="M7 12l3-3 3 3 4-4M8 21l4-4 4 4m-4-11v11" />,
- children: [
- { name: t('nav.analytics.page'), key: 'Analytics.Page' },
- { name: t('nav.analytics.interactions'), key: 'Analytics.Interactions' },
- { name: t('nav.analytics.crm'), key: 'Analytics.CRM' },
- ],
- },
- {
- name: t('nav.seo'),
- key: 'SEO',
- icon: <path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />,
- children: [
- { name: t('nav.seo.short_links'), key: 'SEO.ShortLinks' },
- { name: t('nav.seo.hosting'), key: 'SEO.Hosting' },
- { name: t('nav.seo.services'), key: 'SEO.Services' },
- ],
- },
- {
- name: t('nav.aigc'),
- key: 'AIGC',
- icon: <path strokeLinecap="round" strokeLinejoin="round" d="M9.75 3.104v5.714a2.25 2.25 0 01-.659 1.591L5 14.5M9.75 3.104c.251.023.501.05.75.082a9.75 9.75 0 016 6.062c.313.958.5 1.965.5 3.004v.75a2.25 2.25 0 01-2.25 2.25H3.75a2.25 2.25 0 01-2.25-2.25v-.75c0-1.04.187-2.046.5-3.004a9.75 9.75 0 016-6.062 12.312 12.312 0 01.75-.082zM9.75 18.75c-2.482 0-4.72-1.22-6.16-3.223" />,
- children: [
- { name: t('nav.aigc.creator'), key: 'AIGC.Creator' },
- { name: t('nav.aigc.news'), key: 'AIGC.News' },
- { name: t('nav.aigc.scheduler'), key: 'AIGC.Scheduler' },
- ]
- },
- {
- name: t('nav.showcase'),
- key: 'Showcase',
- icon: <path strokeLinecap="round" strokeLinejoin="round" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />,
- children: [
- { name: t('nav.showcase.personal'), key: 'Showcase.Personal' },
- { name: t('nav.showcase.enterprise'), key: 'Showcase.Enterprise' },
- ],
- },
- ];
- return (
- <nav>
- {navItems.map(item => {
- const isActive = activeKey.startsWith(item.key);
- return (
- <div key={item.key} className="mb-2">
- <button
- onClick={() => onSelect(item.children ? item.children[0].key : item.key)}
- className={`w-full flex items-center gap-3 p-3 rounded-lg text-left transition-colors ${isActive ? 'bg-brand-primary/10 text-brand-primary' : 'hover:bg-gray-200 dark:hover:bg-gray-700'}`}
- >
- <Icon className="h-6 w-6">{item.icon}</Icon>
- <span className="font-semibold">{item.name}</span>
- </button>
- {item.children && isActive && (
- <div className="mt-2 pl-9 space-y-1">
- {item.children.map(child => (
- <button
- key={child.key}
- onClick={() => onSelect(child.key)}
- className={`block w-full text-left text-sm p-2 rounded-md transition-colors ${activeKey === child.key ? 'text-brand-primary font-semibold' : 'text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white'}`}
- >
- {child.name}
- </button>
- ))}
- </div>
- )}
- </div>
- );
- })}
- </nav>
- );
- };
- export const Sidebar: React.FC<SidebarProps> = ({ activePageName, activePageThemeColor, onOpenPageManager, activeNavKey, setActiveNavKey, theme, setTheme, currentUser, onLogout }) => {
- const { t, language, setLanguage } = useTranslation();
- return (
- <aside className="w-64 bg-white dark:bg-gray-800 p-4 flex flex-col border-r border-gray-200 dark:border-gray-700">
- <div className="flex-shrink-0 mb-6">
- <button onClick={onOpenPageManager} className={`w-full bg-gradient-to-br ${activePageThemeColor} p-3 rounded-lg text-left shadow-lg`}>
- <div className="font-bold text-white truncate">{activePageName}</div>
- <div className="text-xs text-white/80">{t('sidebar.select_page')}</div>
- </button>
- </div>
- <div className="flex-1 overflow-y-auto pr-2 -mr-4">
- <NavMenu activeKey={activeNavKey} onSelect={setActiveNavKey} />
- </div>
- <div className="flex-shrink-0 space-y-4 pt-4">
- <div className="p-3 bg-gray-100 dark:bg-gray-900 rounded-lg flex items-center justify-between">
- <div className="min-w-0">
- <p className="text-sm font-semibold truncate text-gray-800 dark:text-gray-200" title={currentUser}>{currentUser}</p>
- </div>
- <button onClick={onLogout} title={t('sidebar.logout')} className="text-gray-400 dark:text-gray-500 hover:text-red-500">
- <Icon className="h-5 w-5"><path strokeLinecap="round" strokeLinejoin="round" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" /></Icon>
- </button>
- </div>
- <div className="flex items-center justify-center gap-2 p-2 bg-gray-100 dark:bg-gray-900 rounded-lg">
- <button onClick={() => setTheme('light')} className={`flex-1 p-2 text-sm rounded-md ${theme === 'light' ? 'bg-brand-primary text-white' : ''}`}>{t('sidebar.light_theme')}</button>
- <button onClick={() => setTheme('dark')} className={`flex-1 p-2 text-sm rounded-md ${theme === 'dark' ? 'bg-brand-primary text-white' : ''}`}>{t('sidebar.dark_theme')}</button>
- </div>
- <div>
- <select
- value={language}
- onChange={(e) => setLanguage(e.target.value as Language)}
- aria-label={t('sidebar.language')}
- className="w-full bg-gray-100 dark:bg-gray-900 p-2 rounded-lg text-sm text-gray-700 dark:text-gray-300 border border-gray-200 dark:border-gray-700 focus:ring-brand-primary focus:border-brand-primary"
- >
- <option value="en">English</option>
- <option value="ja">日本語</option>
- <option value="zh">中文</option>
- <option value="ko">한국어</option>
- </select>
- </div>
- </div>
- </aside>
- );
- };
|