From ae2b06f7a702ea432b801985f534ade405d0299a Mon Sep 17 00:00:00 2001 From: Adam Mathes Date: Sat, 14 Feb 2026 09:55:33 -0800 Subject: ui: redesign sidebar to match v1 aesthetic and fix navigation --- frontend/src/App.tsx | 42 +------ frontend/src/components/FeedList.css | 183 ++++++++++++------------------ frontend/src/components/FeedList.test.tsx | 14 +-- frontend/src/components/FeedList.tsx | 82 ++++++++----- 4 files changed, 137 insertions(+), 184 deletions(-) (limited to 'frontend/src') diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 1c023c4..732d9ef 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { BrowserRouter, Routes, Route, Navigate, useLocation, useNavigate } from 'react-router-dom'; +import { BrowserRouter, Routes, Route, Navigate, useLocation } from 'react-router-dom'; import Login from './components/Login'; import './App.css'; import { apiFetch } from './utils'; @@ -37,53 +37,15 @@ import FeedItems from './components/FeedItems'; import Settings from './components/Settings'; function Dashboard({ theme, setTheme }: { theme: string; setTheme: (t: string) => void }) { - const navigate = useNavigate(); const [sidebarVisible, setSidebarVisible] = useState(true); return (
-
-

setSidebarVisible(!sidebarVisible)} - style={{ cursor: 'pointer' }} - > - 🐱 -

- -
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( {/* @ts-ignore */} - {}} /> + { }} /> ); expect(screen.getByText(/loading feeds/i)).toBeInTheDocument(); @@ -60,7 +60,7 @@ describe('FeedList Component', () => { render( {/* @ts-ignore */} - {}} /> + { }} /> ); @@ -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( {/* @ts-ignore */} - {}} /> + { }} setSidebarVisible={() => { }} /> ); @@ -114,7 +114,7 @@ describe('FeedList Component', () => { render( {/* @ts-ignore */} - {}} /> + { }} setSidebarVisible={() => { }} /> ); @@ -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([]); const [tags, setTags] = useState([]); @@ -62,42 +64,70 @@ export default function FeedList({ if (loading) return
Loading feeds...
; if (error) return
Error: {error}
; + const handleLogout = () => { + apiFetch('/api/logout', { method: 'POST' }).then(() => (window.location.href = '/v2/login')); + }; + return (
+

setSidebarVisible(false)}> + 🐱 +

+
setSearchQuery(e.target.value)} className="search-input" />
+
    -
  • +
  • - Unread + unread
  • -
  • +
  • - All + all
  • -
  • +
  • - Starred + starred
+ +
+

{ }} className="section-header"> + Tags +

+
    + {tags.map((tag) => ( +
  • + + {tag.title} + +
  • + ))} +
+
+
-

- {feedsExpanded ? '▼' : '▶'} Feeds -

+

+ Feeds +

{feedsExpanded && (feeds.length === 0 ? (

No feeds found.

@@ -111,30 +141,26 @@ export default function FeedList({ > {feed.title || feed.url} - {feed.category && {feed.category}} ))} ))}
- {tags && tags.length > 0 && ( -
-

Tags

-
    - {tags.map((tag) => ( -
  • - - {tag.title} - -
  • - ))} -
-
- )} +
+
    +
  • + + settings + +
  • +
  • + +
  • +
+
-- cgit v1.2.3