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 ## Phase 2: Layout Components
### 2.1 Top Navigation Bar ### 2.1 Top Navigation Bar
- [ ] Create `top_bar()` component: - [x] Create `top_bar()` component:
- Logo (use existing NHS person logo from assets) - Logo (use existing NHS person logo from assets)
- App title "HCD Analysis" - App title "HCD Analysis"
- Chart type tabs/pills (Icicle active, placeholders for future charts) - Chart type tabs/pills (Icicle active, placeholders for future charts)
- Data freshness indicator (right side): "12,450 records (2d ago)" - Data freshness indicator (right side): "12,450 records (2d ago)"
- [ ] Style with Heritage Blue accents, clean typography - [x] Style with Heritage Blue accents, clean typography
- [ ] Fixed height: 64px - [x] Fixed height: 64px
- [ ] Verify renders correctly - [x] Verify renders correctly
### 2.2 Filter Section ### 2.2 Filter Section
- [ ] Create `filter_section()` component with card styling - [ ] Create `filter_section()` component with card styling
+82 -16
View File
@@ -13,6 +13,7 @@ from pathways_app.styles import (
Spacing, Spacing,
Radii, Radii,
Shadows, Shadows,
Transitions,
TOP_BAR_HEIGHT, TOP_BAR_HEIGHT,
PAGE_MAX_WIDTH, PAGE_MAX_WIDTH,
PAGE_PADDING, PAGE_PADDING,
@@ -47,49 +48,114 @@ class AppState(rx.State):
# Layout Components # 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: def top_bar() -> rx.Component:
""" """
Top navigation bar component. Top navigation bar component.
Contains: Logo + App Name | Chart Type Tabs | Data Freshness Indicator Contains: Logo + App Name | Chart Type Tabs | Data Freshness Indicator
Fixed height: 64px (from design system) Fixed height: 64px (from design system)
Heritage Blue background with white text.
Will be fully implemented in Task 2.1.
""" """
return rx.box( return rx.box(
rx.hstack( rx.hstack(
# Left: Logo and title (placeholder) # Left: Logo and App Title
rx.hstack( rx.hstack(
rx.image(
src="/logo.png",
height="36px",
alt="NHS Logo",
),
rx.text( rx.text(
"HCD Analysis", "HCD Analysis",
font_size=Typography.H2_SIZE, font_size=Typography.H2_SIZE,
font_weight=Typography.H2_WEIGHT, font_weight=Typography.H2_WEIGHT,
color=Colors.WHITE, color=Colors.WHITE,
font_family=Typography.FONT_FAMILY, font_family=Typography.FONT_FAMILY,
letter_spacing="-0.01em",
), ),
align="center", align="center",
spacing="3", spacing="3",
), ),
# Center: Chart tabs (placeholder) # Center: Chart Type Tabs
rx.hstack( rx.hstack(
rx.text( chart_tab("Icicle", "icicle", is_active=True),
"Icicle Chart", chart_tab("Sankey", "sankey", is_active=False),
font_size=Typography.BODY_SIZE, chart_tab("Timeline", "timeline", is_active=False),
color=Colors.WHITE, spacing="2",
opacity="0.9", 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", spacing="2",
), align="center",
# Right: Data freshness (placeholder)
rx.text(
"Data loading...",
font_size=Typography.CAPTION_SIZE,
color=Colors.WHITE,
opacity="0.7",
), ),
justify="between", justify="between",
align="center", align="center",
width="100%", width="100%",
max_width=PAGE_MAX_WIDTH,
margin_x="auto",
padding_x=Spacing.XL, padding_x=Spacing.XL,
), ),
background_color=Colors.HERITAGE_BLUE, background_color=Colors.HERITAGE_BLUE,