diff options
Diffstat (limited to 'frontend/src/components')
| -rw-r--r-- | frontend/src/components/FeedList.css | 183 | ||||
| -rw-r--r-- | frontend/src/components/FeedList.test.tsx | 14 | ||||
| -rw-r--r-- | frontend/src/components/FeedList.tsx | 82 |
3 files changed, 135 insertions, 144 deletions
diff --git a/frontend/src/components/FeedList.css b/frontend/src/components/FeedList.css index fa0278a..1b83aed 100644 --- a/frontend/src/components/FeedList.css +++ b/frontend/src/components/FeedList.css @@ -1,169 +1,134 @@ .feed-list { - /* Removed card styling */ - padding: 0; - background: transparent; + padding: 1rem; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + color: var(--text-color); } -.search-section { - margin-bottom: 1.5rem; +.feed-list h1.logo { + font-size: 3rem; + margin: 0 0 1rem 0; + line-height: 1; + cursor: pointer; } -.search-form { - display: flex; +.search-section { + margin-bottom: 1.5rem; } .search-input { width: 100%; - padding: 0.5rem; + padding: 0.25rem; border: 1px solid var(--border-color, #999); background: var(--bg-color); color: var(--text-color); - font-size: 1rem; + font-size: 0.9rem; font-family: inherit; } -.search-input:focus { - outline: none; - background: var(--bg-color); - border-color: var(--link-color); -} - -.feed-list h2, -.feed-section-header { - font-size: 1.2rem; - margin-bottom: 0.5rem; - border-bottom: 1px solid var(--border-color, #999); - padding-bottom: 0.25rem; - text-transform: uppercase; - letter-spacing: 1px; +.section-header { + font-size: 1rem; + font-weight: bold; + margin: 1.5rem 0 0.5rem 0; cursor: pointer; user-select: none; - display: flex; - align-items: center; -} - -.toggle-indicator { - font-size: 0.8rem; - margin-right: 0.5rem; - display: inline-block; - width: 1rem; - text-align: center; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + color: var(--text-color); } -.feed-list-items, +.filter-list, .tag-list-items, -.filter-list { +.feed-list-items, +.nav-list { list-style: none; padding: 0; margin: 0; } -.sidebar-feed-item { - padding: 0.25rem 0; - border-bottom: none; - /* Clean look */ - display: flex; - justify-content: space-between; - align-items: center; +.filter-list li, +.nav-list li { + margin-bottom: 0.25rem; } -.feed-title { - color: var(--link-color); - text-decoration: none; - font-size: 0.9rem; - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -.feed-title:hover { - text-decoration: underline; - color: var(--link-color); -} - -.feed-category { - display: none; - /* Hide category in sidebar list to save space */ -} - -.tag-section { - margin-top: 2rem; -} - -.tag-link { - color: var(--link-color); +.filter-list a, +.nav-list a, +.tag-link, +.feed-title, +.logout-link { text-decoration: none; + color: var(--link-color, blue); font-size: 0.9rem; display: block; - padding: 0.1rem 0; - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + cursor: pointer; + background: none; + border: none; + padding: 0; + font-family: inherit; + font-variant: small-caps; + text-transform: lowercase; } -.tag-link:hover { +.filter-list a:hover, +.nav-list a:hover, +.tag-link:hover, +.feed-title:hover, +.logout-link:hover { text-decoration: underline; - background: transparent; - color: var(--link-color); } -.filter-section { - margin-bottom: 2rem; +.filter-list a.active, +.tag-link.active, +.feed-title.active { + font-weight: bold; + color: var(--text-color); } -.filter-list { - display: block; - list-style: none; - padding: 0; - margin: 0; +.tag-item, +.sidebar-feed-item { + margin-bottom: 0.1rem; } -.filter-list li a { - text-decoration: none; - color: var(--text-color); - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; - font-variant: small-caps; - text-transform: lowercase; - font-size: 1.1rem; - display: block; - margin-bottom: 0.5rem; +.feed-category { + display: none; } -.filter-list li a:hover { - color: blue; - background-color: transparent; - text-decoration: underline; +.nav-section { + margin-top: 2rem; + border-top: 1px solid var(--border-color, #ccc); + padding-top: 1rem; } -.feed-title.active, -.tag-link.active, -.filter-list li a.active, -.theme-selector button.active { - font-weight: bold !important; +.logout-link { + text-align: left; + width: 100%; } .theme-section { - margin-top: 2rem; - padding-bottom: 2rem; + margin-top: 1rem; } .theme-selector { display: flex; - justify-content: space-between; - gap: 5px; + gap: 0.5rem; } .theme-selector button { - font-size: 1.2rem; - padding: 0.5rem; - width: 48%; - background: var(--sidebar-bg); + background: transparent; border: 1px solid var(--border-color, #ccc); + cursor: pointer; + padding: 0.25rem 0.5rem; + font-size: 1rem; border-radius: 4px; } -.theme-selector button:hover { - background: var(--bg-color); +.theme-selector button.active { + background: var(--border-color, #ccc); } -.theme-selector button.active { - background: var(--bg-color); - border-color: var(--link-color); - box-shadow: 0 0 5px var(--link-color); +/* Scrollbar styling for webkit */ +.dashboard-sidebar::-webkit-scrollbar { + width: 6px; +} + +.dashboard-sidebar::-webkit-scrollbar-thumb { + background-color: var(--border-color, #ccc); }
\ No newline at end of file diff --git a/frontend/src/components/FeedList.test.tsx b/frontend/src/components/FeedList.test.tsx index daa4d69..059d8a4 100644 --- a/frontend/src/components/FeedList.test.tsx +++ b/frontend/src/components/FeedList.test.tsx @@ -13,11 +13,11 @@ describe('FeedList Component', () => { }); it('renders loading state initially', () => { - (global.fetch as any).mockImplementation(() => new Promise(() => {})); + (global.fetch as any).mockImplementation(() => new Promise(() => { })); render( <BrowserRouter> {/* @ts-ignore */} - <FeedList theme="light" setTheme={() => {}} /> + <FeedList theme="light" setTheme={() => { }} /> </BrowserRouter> ); expect(screen.getByText(/loading feeds/i)).toBeInTheDocument(); @@ -60,7 +60,7 @@ describe('FeedList Component', () => { render( <BrowserRouter> {/* @ts-ignore */} - <FeedList theme="light" setTheme={() => {}} /> + <FeedList theme="light" setTheme={() => { }} /> </BrowserRouter> ); @@ -69,7 +69,7 @@ describe('FeedList Component', () => { }); // Expand feeds - fireEvent.click(screen.getByText(/feeds/i, { selector: 'h2' })); + fireEvent.click(screen.getByText(/feeds/i, { selector: 'h4' })); await waitFor(() => { expect(screen.getByText('Feed One')).toBeInTheDocument(); @@ -85,7 +85,7 @@ describe('FeedList Component', () => { render( <BrowserRouter> {/* @ts-ignore */} - <FeedList theme="light" setTheme={() => {}} /> + <FeedList theme="light" setTheme={() => { }} setSidebarVisible={() => { }} /> </BrowserRouter> ); @@ -114,7 +114,7 @@ describe('FeedList Component', () => { render( <BrowserRouter> {/* @ts-ignore */} - <FeedList theme="light" setTheme={() => {}} /> + <FeedList theme="light" setTheme={() => { }} setSidebarVisible={() => { }} /> </BrowserRouter> ); @@ -123,7 +123,7 @@ describe('FeedList Component', () => { }); // Expand feeds - fireEvent.click(screen.getByText(/feeds/i, { selector: 'h2' })); + fireEvent.click(screen.getByText(/feeds/i, { selector: 'h4' })); await waitFor(() => { expect(screen.getByText(/no feeds found/i)).toBeInTheDocument(); diff --git a/frontend/src/components/FeedList.tsx b/frontend/src/components/FeedList.tsx index 1cd1bfd..4ce2d71 100644 --- a/frontend/src/components/FeedList.tsx +++ b/frontend/src/components/FeedList.tsx @@ -7,9 +7,11 @@ import { apiFetch } from '../utils'; export default function FeedList({ theme, setTheme, + setSidebarVisible, }: { theme: string; setTheme: (t: string) => void; + setSidebarVisible: (visible: boolean) => void; }) { const [feeds, setFeeds] = useState<Feed[]>([]); const [tags, setTags] = useState<Category[]>([]); @@ -62,42 +64,70 @@ export default function FeedList({ if (loading) return <div className="feed-list-loading">Loading feeds...</div>; if (error) return <div className="feed-list-error">Error: {error}</div>; + const handleLogout = () => { + apiFetch('/api/logout', { method: 'POST' }).then(() => (window.location.href = '/v2/login')); + }; + return ( <div className="feed-list"> + <h1 className="logo" onClick={() => setSidebarVisible(false)}> + 🐱 + </h1> + <div className="search-section"> <form onSubmit={handleSearch} className="search-form"> <input type="search" - placeholder="Search items..." + placeholder="search..." value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} className="search-input" /> </form> </div> + <div className="filter-section"> <ul className="filter-list"> - <li> + <li className="unread_filter"> <Link to="/?filter=unread" className={currentFilter === 'unread' ? 'active' : ''}> - Unread + unread </Link> </li> - <li> + <li className="all_filter"> <Link to="/?filter=all" className={currentFilter === 'all' ? 'active' : ''}> - All + all </Link> </li> - <li> + <li className="starred_filter"> <Link to="/?filter=starred" className={currentFilter === 'starred' ? 'active' : ''}> - Starred + starred </Link> </li> </ul> </div> + + <div className="tag-section"> + <h4 onClick={() => { }} className="section-header"> + Tags + </h4> + <ul className="tag-list-items"> + {tags.map((tag) => ( + <li key={tag.title} className="tag-item"> + <Link + to={`/tag/${encodeURIComponent(tag.title)}`} + className={`tag-link ${tagName === tag.title ? 'active' : ''}`} + > + {tag.title} + </Link> + </li> + ))} + </ul> + </div> + <div className="feed-section"> - <h2 onClick={toggleFeeds} className="feed-section-header"> - <span className="toggle-indicator">{feedsExpanded ? '▼' : '▶'}</span> Feeds - </h2> + <h4 onClick={toggleFeeds} className="section-header"> + Feeds + </h4> {feedsExpanded && (feeds.length === 0 ? ( <p>No feeds found.</p> @@ -111,30 +141,26 @@ export default function FeedList({ > {feed.title || feed.url} </Link> - {feed.category && <span className="feed-category">{feed.category}</span>} </li> ))} </ul> ))} </div> - {tags && tags.length > 0 && ( - <div className="tag-section"> - <h2>Tags</h2> - <ul className="tag-list-items"> - {tags.map((tag) => ( - <li key={tag.title} className="tag-item"> - <Link - to={`/tag/${encodeURIComponent(tag.title)}`} - className={`tag-link ${tagName === tag.title ? 'active' : ''}`} - > - {tag.title} - </Link> - </li> - ))} - </ul> - </div> - )} + <div className="nav-section"> + <ul className="nav-list"> + <li> + <Link to="/settings" className="nav-link"> + settings + </Link> + </li> + <li> + <button onClick={handleLogout} className="logout-link"> + logout + </button> + </li> + </ul> + </div> <div className="theme-section"> <div className="theme-selector"> |
