Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/reflex app maintenance #234

Merged
merged 4 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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