Skip to content

Commit

Permalink
feature/reflex app maintenance (#234)
Browse files Browse the repository at this point in the history
# PR Context

- harmonize mex-drop setup and style further with mex-editor
- so that we can (maybe) have a mex ui-kit repo at some point
- setup inspo
robert-koch-institut/mex-editor#245
- style inspo
robert-koch-institut/mex-editor#241

# Added

- update mex-common to version 0.49.3
- BREAKING: you must start the local dev mode simply with `pdm run drop`
(no 2nd run)
- move custom backend exception handler to its own module
- move custom api code to its own package `mex.drop.api`
- align general layout functions (page, logo, navbar, etc) with
mex-editor
- align login component and navbar state handling with mex-editor
- update styling with more idiomatic variable syntax and responsive
scaling

# Fixed
- decorate state handlers with `@rx.event` to satisfy new reflex
versions
  • Loading branch information
cutoffthetop authored Feb 7, 2025
1 parent f5c7004 commit 5684409
Show file tree
Hide file tree
Showing 29 changed files with 595 additions and 465 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
env:
MEX_DROP_API_KEY_DATABASE: ${{ secrets.MEX_DROP_API_KEY_DATABASE }}
run: |
pdm run drop run &
pdm run drop &
sleep 30 &&
curl --connect-timeout 1 --max-time 20 --retry 10 --retry-delay 1 http://localhost:8000/_system/check &&
make pytest
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changes

- update mex-common to version 0.49.3
- BREAKING: you must start the local dev mode simply with `pdm run drop` (no 2nd run)
- move custom backend exception handler to its own module
- move custom api code to its own package `mex.drop.api`
- align general layout functions (page, logo, navbar, etc) with mex-editor
- align login component and navbar state handling with mex-editor
- update styling with more idiomatic variable syntax and responsive scaling

### Deprecated

### Removed

### Fixed

- decorate state handlers with `@rx.event` to satisfy new reflex versions

### Security

## [0.9.1] - 2025-01-09

### Added

- mex-drop UI shows file list with name and timestamps

## [0.9.0] - 2024-12-05
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,4 @@ components of the MEx project are open-sourced under the same license as well.

### Drop

- `pdm run drop run` starts the drop service
- `pdm run drop` starts the drop service
Empty file added mex/drop/api/__init__.py
Empty file.
2 changes: 1 addition & 1 deletion mex/drop/api.py → mex/drop/api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ async def drop_data(
)
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Unsupported content typeor format.",
detail="Unsupported content type or format.",
)


Expand Down
11 changes: 11 additions & 0 deletions mex/drop/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import reflex as rx
from reflex.event import EventSpec


def custom_backend_handler(exception: Exception) -> EventSpec:
"""Custom backend exception handler."""
if str(exception) == "401: Missing authentication header X-API-Key.":
return rx.toast.error("Please enter your API Key.")
if str(exception) == "401: The provided API Key is not recognized.":
return rx.toast.error("Invalid API Key.")
return rx.toast.error(f"Backend Error: {exception}")
37 changes: 13 additions & 24 deletions mex/drop/file_history/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import reflex as rx

from mex.drop.file_history.state import ListState
from mex.drop.navigation import nav_bar
from mex.drop.layout import page


def render_file_row(file: dict) -> rx.Component:
Expand Down Expand Up @@ -39,31 +39,20 @@ def uploaded_files_display() -> rx.Component:
)


def file_history_index() -> rx.Component:
"""Return the index for the file history page."""
return rx.box(
nav_bar(),
rx.center(
rx.card(
rx.vstack(
rx.text("File Upload History", weight="bold", align="center"),
rx.divider(size="4"),
rx.scroll_area(
rx.flex(
uploaded_files_display(),
),
type="always",
scrollbars="vertical",
height=350,
def index() -> rx.Component:
"""Return the index for the file history component."""
return page(
rx.card(
rx.vstack(
rx.scroll_area(
rx.flex(
uploaded_files_display(),
),
type="hover",
scrollbars="vertical",
),
width="70%",
padding="2em",
margin="4em",
custom_attrs={"data-testid": "index-card"},
),
custom_attrs={"data-testid": "index-card"},
width="100%",
),
background_color="var(--gray-2)",
height="100%",
padding="2em",
)
12 changes: 6 additions & 6 deletions mex/drop/file_history/state.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import pathlib
from datetime import UTC, datetime
from typing import cast

import reflex as rx
from reflex.event import EventSpec
Expand All @@ -14,12 +13,13 @@ class ListState(State):

file_list: list[dict] = []

def get_uploaded_files(self) -> EventSpec | None:
"""Get the list of files uploaded by the user to X System."""
cast(State, State).check_login()
if not self.user:
return rx.toast.error("No User logged in.", close_button=True)
@rx.event
def refresh(self) -> EventSpec | None:
"""Refresh the list of files uploaded by the user to X-System."""
settings = DropSettings.get()
if not self.user: # pragma: no cover
msg = "Should have redirected to login."
raise RuntimeError(msg)
x_system_data_dir = pathlib.Path(
settings.drop_directory, str(self.user.x_system)
)
Expand Down
129 changes: 129 additions & 0 deletions mex/drop/layout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
from typing import cast

import reflex as rx

from mex.drop.models import NavItem, User
from mex.drop.state import State


def user_button() -> rx.Component:
"""Return a user button with an icon that indicates their access rights."""
return rx.button(
rx.icon(tag="user_round_cog"),
variant="ghost",
style={"marginTop": "0"},
)


def user_menu() -> rx.Component:
"""Return a user menu with a trigger, the current X-System and a logout button."""
return rx.menu.root(
rx.menu.trigger(
user_button(),
custom_attrs={"data-testid": "user-menu"},
),
rx.menu.content(
rx.menu.item(cast(User, State.user).x_system, disabled=True),
rx.menu.separator(),
rx.menu.item(
"Logout",
on_select=State.logout,
),
),
)


def nav_link(item: NavItem) -> rx.Component:
"""Return a link component for the given navigation item."""
return rx.link(
rx.text(item.title, size="4", weight="medium"),
href=item.path,
underline=item.underline,
class_name="nav-item",
)


def app_logo() -> rx.Component:
"""Return the app logo with icon and label."""
return rx.hstack(
rx.icon(
"droplets",
size=28,
),
rx.heading(
"MEx Drop",
weight="medium",
style={"userSelect": "none"},
),
custom_attrs={"data-testid": "app-logo"},
)


def nav_bar() -> rx.Component:
"""Return a navigation bar component."""
return rx.vstack(
rx.box(
style={
"height": "var(--space-6)",
"width": "100%",
"backdropFilter": " var(--backdrop-filter-panel)",
"backgroundColor": "var(--card-background-color)",
},
),
rx.card(
rx.hstack(
app_logo(),
rx.divider(orientation="vertical", size="2"),
rx.hstack(
rx.foreach(State.nav_items, nav_link),
justify="start",
spacing="4",
),
rx.divider(orientation="vertical", size="2"),
user_menu(),
rx.spacer(),
rx.color_mode.button(),
justify="between",
align_items="center",
),
size="2",
custom_attrs={"data-testid": "nav-bar"},
style={
"width": "100%",
"marginTop": "calc(-1 * var(--base-card-border-width))",
},
),
spacing="0",
style={
"maxWidth": "calc(1480px * var(--scaling))",
"minWidth": "calc(800px * var(--scaling))",
"position": "fixed",
"top": "0",
"width": "100%",
"zIndex": "1000",
},
)


def page(*children: rx.Component) -> rx.Component:
"""Return a page fragment with navigation bar and given children."""
return rx.cond(
State.user,
rx.center(
nav_bar(),
rx.hstack(
*children,
style={
"maxWidth": "calc(1480px * var(--scaling))",
"minWidth": "calc(800px * var(--scaling))",
"padding": "calc(var(--space-6) * 4) var(--space-6) var(--space-6)",
"width": "100%",
},
custom_attrs={"data-testid": "page-body"},
),
),
rx.center(
rx.spinner(size="3"),
style={"marginTop": "40vh"},
),
)
Loading

0 comments on commit 5684409

Please sign in to comment.