Spaces:
Sleeping
Sleeping
| import React, { useState, useEffect } from 'react'; | |
| import { useTranslation } from 'react-i18next'; | |
| import './Sidebar.css'; | |
| const Sidebar = ({ | |
| isOpen, | |
| onToggle, | |
| activePage, | |
| onPageChange, | |
| pages = [], | |
| userStats = {} | |
| }) => { | |
| const { t, i18n } = useTranslation(); | |
| const [searchTerm, setSearchTerm] = useState(''); | |
| const [filteredPages, setFilteredPages] = useState(pages); | |
| useEffect(() => { | |
| if (searchTerm) { | |
| const lowercasedSearchTerm = searchTerm.toLowerCase(); | |
| const filtered = pages.filter(page => | |
| t(page.name).toLowerCase().includes(lowercasedSearchTerm) || | |
| t(page.description).toLowerCase().includes(lowercasedSearchTerm) || | |
| (page.category && t(`sidebar.categories.${page.category}`, page.category).toLowerCase().includes(lowercasedSearchTerm)) | |
| ); | |
| setFilteredPages(filtered); | |
| } else { | |
| setFilteredPages(pages); | |
| } | |
| }, [searchTerm, pages, t]); | |
| const groupedPages = filteredPages.reduce((groups, page) => { | |
| const category = page.category || 'Tools'; | |
| if (!groups[category]) { | |
| groups[category] = []; | |
| } | |
| groups[category].push(page); | |
| return groups; | |
| }, {}); | |
| const handlePageClick = (pageId) => { | |
| onPageChange(pageId); | |
| }; | |
| const clearSearch = () => { | |
| setSearchTerm(''); | |
| }; | |
| const handleLanguageChange = (e) => { | |
| i18n.changeLanguage(e.target.value); | |
| }; | |
| return ( | |
| <div className={`terra-sidebar ${isOpen ? 'terra-sidebar--open' : 'terra-sidebar--closed'}`}> | |
| {/* Logo Section */} | |
| <div className="terra-sidebar__header"> | |
| <div className="terra-sidebar__logo"> | |
| <div className="terra-sidebar__logo-icon"> | |
| 🌿 | |
| </div> | |
| {isOpen && ( | |
| <div className="terra-sidebar__logo-text"> | |
| <h1 className="terra-sidebar__logo-title">{t('sidebar.title')}</h1> | |
| <p className="terra-sidebar__logo-subtitle">{t('sidebar.subtitle')}</p> | |
| </div> | |
| )} | |
| </div> | |
| <button | |
| className="terra-sidebar__toggle" | |
| onClick={onToggle} | |
| aria-label={isOpen ? t('sidebar.collapse') : t('sidebar.expand')} | |
| > | |
| <span className={`terra-sidebar__toggle-icon ${isOpen ? 'terra-sidebar__toggle-icon--open' : ''}`}> | |
| ▶ | |
| </span> | |
| </button> | |
| </div> | |
| {/* Search Section */} | |
| {isOpen && ( | |
| <div className="terra-sidebar__search"> | |
| <div className="terra-sidebar__search-container"> | |
| <input | |
| type="text" | |
| placeholder={t('sidebar.searchPlaceholder')} | |
| value={searchTerm} | |
| onChange={(e) => setSearchTerm(e.target.value)} | |
| className="terra-sidebar__search-input" | |
| /> | |
| <div className="terra-sidebar__search-icon"> | |
| {searchTerm ? ( | |
| <button | |
| onClick={clearSearch} | |
| className="terra-sidebar__search-clear" | |
| aria-label={t('sidebar.clearSearch')} | |
| > | |
| ✕ | |
| </button> | |
| ) : ( | |
| <span>🔍</span> | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| {/* Navigation Section */} | |
| <nav className="terra-sidebar__nav"> | |
| <div className="terra-sidebar__nav-content"> | |
| {Object.entries(groupedPages).map(([category, categoryPages]) => ( | |
| <div key={category} className="terra-sidebar__category"> | |
| {isOpen && ( | |
| <h3 className="terra-sidebar__category-title">{t(`sidebar.categories.${category}`, category)}</h3> | |
| )} | |
| <div className="terra-sidebar__category-items"> | |
| {categoryPages.map(page => ( | |
| <div | |
| key={page.id} | |
| className={`terra-sidebar__nav-item ${ | |
| activePage === page.id ? 'terra-sidebar__nav-item--active' : '' | |
| }`} | |
| onClick={() => handlePageClick(page.id)} | |
| role="button" | |
| tabIndex={0} | |
| onKeyDown={(e) => { | |
| if (e.key === 'Enter' || e.key === ' ') { | |
| e.preventDefault(); | |
| handlePageClick(page.id); | |
| } | |
| }} | |
| > | |
| <div className="terra-sidebar__nav-icon"> | |
| {page.icon} | |
| </div> | |
| {isOpen && ( | |
| <div className="terra-sidebar__nav-content"> | |
| <span className="terra-sidebar__nav-name">{t(page.name)}</span> | |
| <span className="terra-sidebar__nav-desc">{t(page.description)}</span> | |
| </div> | |
| )} | |
| {activePage === page.id && ( | |
| <div className="terra-sidebar__nav-indicator" /> | |
| )} | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </nav> | |
| {/* Stats Footer */} | |
| {isOpen && ( | |
| <div className="terra-sidebar__footer"> | |
| <div className="terra-sidebar__stats"> | |
| <div className="terra-sidebar__stat"> | |
| <span className="terra-sidebar__stat-icon">🌱</span> | |
| <div className="terra-sidebar__stat-content"> | |
| <div className="terra-sidebar__stat-value"> | |
| {userStats.toolsUsed || pages.length} | |
| </div> | |
| <div className="terra-sidebar__stat-label">{t('sidebar.tools')}</div> | |
| </div> | |
| </div> | |
| <div className="terra-sidebar__stat"> | |
| <span className="terra-sidebar__stat-icon">🌍</span> | |
| <div className="terra-sidebar__stat-content"> | |
| <div className="terra-sidebar__stat-value"> | |
| {userStats.impactScore || '2.5M+'} | |
| </div> | |
| <div className="terra-sidebar__stat-label">{t('sidebar.impact')}</div> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="terra-sidebar__language-selector-container"> | |
| <label htmlFor="language-select" className="terra-sidebar__language-label">{t('sidebar.language')}</label> | |
| <select id="language-select" onChange={handleLanguageChange} value={i18n.language} className="terra-sidebar__language-select"> | |
| <option value="vi">Tiếng Việt</option> | |
| <option value="en">English</option> | |
| </select> | |
| </div> | |
| <div className="terra-sidebar__version"> | |
| <span>{t('sidebar.version')}</span> | |
| </div> | |
| </div> | |
| )} | |
| {/* Tooltip for collapsed state */} | |
| {!isOpen && ( | |
| <div className="terra-sidebar__tooltip" id="sidebar-tooltip" role="tooltip"> | |
| <div className="terra-sidebar__tooltip-content"></div> | |
| <div className="terra-sidebar__tooltip-arrow"></div> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| }; | |
| export default Sidebar; |