feat: implement top navigation bar (Task 2.1)

- Add chart_tab() component for chart type pills
- Implement full top_bar() with logo, title, chart tabs, data freshness
- Heritage Blue background with white text, 64px fixed height
- Reactive data freshness indicator using rx.cond for loading states
- Added Transitions import to styles
This commit is contained in:
Andrew Charlwood
2026-02-04 13:40:31 +00:00
parent 072ce852af
commit a8d9f7b757
2 changed files with 86 additions and 20 deletions
+4 -4
View File
@@ -51,14 +51,14 @@ cd pathways_app && timeout 60 python -m reflex run 2>&1 | head -30
## Phase 2: Layout Components
### 2.1 Top Navigation Bar
- [ ] Create `top_bar()` component:
- [x] Create `top_bar()` component:
- Logo (use existing NHS person logo from assets)
- App title "HCD Analysis"
- Chart type tabs/pills (Icicle active, placeholders for future charts)
- Data freshness indicator (right side): "12,450 records (2d ago)"
- [ ] Style with Heritage Blue accents, clean typography
- [ ] Fixed height: 64px
- [ ] Verify renders correctly
- [x] Style with Heritage Blue accents, clean typography
- [x] Fixed height: 64px
- [x] Verify renders correctly
### 2.2 Filter Section
- [ ] Create `filter_section()` component with card styling
+82 -16
View File
@@ -13,6 +13,7 @@ from pathways_app.styles import (
Spacing,
Radii,
Shadows,
Transitions,
TOP_BAR_HEIGHT,
PAGE_MAX_WIDTH,
PAGE_PADDING,
@@ -47,49 +48,114 @@ class AppState(rx.State):
# Layout Components
# =============================================================================
def chart_tab(label: str, chart_type: str, is_active: bool = False) -> rx.Component:
"""
Individual chart type tab/pill for top bar navigation.
Active state: White background with Heritage Blue text
Inactive state: Transparent with white text, hover shows Vibrant Blue background
"""
return rx.box(
rx.text(
label,
font_size=Typography.BODY_SMALL_SIZE,
font_weight="500",
color=Colors.HERITAGE_BLUE if is_active else Colors.WHITE,
font_family=Typography.FONT_FAMILY,
),
background_color=Colors.WHITE if is_active else "transparent",
padding_x=Spacing.LG,
padding_y=Spacing.SM,
border_radius=Radii.FULL,
cursor="pointer",
transition=f"background-color {Transitions.COLOR}",
_hover={
"background_color": Colors.WHITE if is_active else "rgba(255,255,255,0.15)",
},
# Future: on_click handler to switch chart type
)
def top_bar() -> rx.Component:
"""
Top navigation bar component.
Contains: Logo + App Name | Chart Type Tabs | Data Freshness Indicator
Fixed height: 64px (from design system)
Will be fully implemented in Task 2.1.
Heritage Blue background with white text.
"""
return rx.box(
rx.hstack(
# Left: Logo and title (placeholder)
# Left: Logo and App Title
rx.hstack(
rx.image(
src="/logo.png",
height="36px",
alt="NHS Logo",
),
rx.text(
"HCD Analysis",
font_size=Typography.H2_SIZE,
font_weight=Typography.H2_WEIGHT,
color=Colors.WHITE,
font_family=Typography.FONT_FAMILY,
letter_spacing="-0.01em",
),
align="center",
spacing="3",
),
# Center: Chart tabs (placeholder)
# Center: Chart Type Tabs
rx.hstack(
rx.text(
"Icicle Chart",
font_size=Typography.BODY_SIZE,
color=Colors.WHITE,
opacity="0.9",
chart_tab("Icicle", "icicle", is_active=True),
chart_tab("Sankey", "sankey", is_active=False),
chart_tab("Timeline", "timeline", is_active=False),
spacing="2",
align="center",
background_color="rgba(255,255,255,0.1)",
padding=Spacing.XS,
border_radius=Radii.FULL,
),
# Right: Data Freshness Indicator
rx.hstack(
rx.icon(
"database",
size=16,
color=Colors.SKY,
),
rx.vstack(
rx.text(
rx.cond(
AppState.data_loaded,
AppState.total_records.to_string() + " records",
"Loading data...",
),
font_size=Typography.CAPTION_SIZE,
font_weight="500",
color=Colors.WHITE,
font_family=Typography.FONT_FAMILY,
),
rx.text(
rx.cond(
AppState.data_loaded,
"Last refreshed: recently",
"Connecting...",
),
font_size="11px",
color=Colors.WHITE,
opacity="0.7",
font_family=Typography.FONT_FAMILY,
),
spacing="0",
align="end",
),
spacing="2",
),
# Right: Data freshness (placeholder)
rx.text(
"Data loading...",
font_size=Typography.CAPTION_SIZE,
color=Colors.WHITE,
opacity="0.7",
align="center",
),
justify="between",
align="center",
width="100%",
max_width=PAGE_MAX_WIDTH,
margin_x="auto",
padding_x=Spacing.XL,
),
background_color=Colors.HERITAGE_BLUE,