initial
This commit is contained in:
1
reflex_ipad/__init__.py
Normal file
1
reflex_ipad/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Base template for Reflex."""
|
||||
6
reflex_ipad/api/__init__.py
Normal file
6
reflex_ipad/api/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from . import medicine
|
||||
from . import scan
|
||||
|
||||
def register_at(app):
|
||||
medicine.register_at(app)
|
||||
scan.register_at(app)
|
||||
13
reflex_ipad/api/medicine.py
Normal file
13
reflex_ipad/api/medicine.py
Normal file
@@ -0,0 +1,13 @@
|
||||
import reflex as rx
|
||||
from reflex_ipad.models import *
|
||||
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
||||
|
||||
async def medicine_scan(uuid: str):
|
||||
with rx.session() as session:
|
||||
statement = select(Medicine).where(Medicine.uuid == uuid)
|
||||
results = session.exec(statement)
|
||||
med = results.first()
|
||||
return med
|
||||
|
||||
def register_at(app):
|
||||
app.api.add_api_route("/medicine/scan/{uuid}", medicine_scan)
|
||||
17
reflex_ipad/api/scan.py
Normal file
17
reflex_ipad/api/scan.py
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
import reflex as rx
|
||||
from reflex_ipad.models import *
|
||||
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
||||
import time
|
||||
|
||||
async def scan(uuid: str):
|
||||
print(uuid)
|
||||
with rx.session() as session:
|
||||
scan = Scan(uuid=uuid, timestamp=time.time())
|
||||
session.add(scan)
|
||||
session.commit()
|
||||
return scan.as_dict()
|
||||
|
||||
def register_at(app):
|
||||
print("register scan")
|
||||
app.api.add_api_route("/scan/{uuid}", scan)
|
||||
0
reflex_ipad/components/__init__.py
Normal file
0
reflex_ipad/components/__init__.py
Normal file
119
reflex_ipad/components/sidebar.py
Normal file
119
reflex_ipad/components/sidebar.py
Normal file
@@ -0,0 +1,119 @@
|
||||
"""Sidebar component for the app."""
|
||||
|
||||
from reflex_ipad import styles
|
||||
|
||||
import reflex as rx
|
||||
|
||||
|
||||
def sidebar_header() -> rx.Component:
|
||||
"""Sidebar header.
|
||||
|
||||
Returns:
|
||||
The sidebar header component.
|
||||
"""
|
||||
return rx.hstack(
|
||||
# The logo.
|
||||
rx.heading("Hallo"),
|
||||
width="100%",
|
||||
border_bottom=styles.border,
|
||||
padding="1em",
|
||||
)
|
||||
|
||||
|
||||
def sidebar_footer() -> rx.Component:
|
||||
"""Sidebar footer.
|
||||
|
||||
Returns:
|
||||
The sidebar footer component.
|
||||
"""
|
||||
return rx.hstack(
|
||||
rx.spacer(),
|
||||
width="100%",
|
||||
border_top=styles.border,
|
||||
padding="1em",
|
||||
)
|
||||
|
||||
|
||||
def sidebar_item(text: str, icon: str, url: str) -> rx.Component:
|
||||
"""Sidebar item.
|
||||
|
||||
Args:
|
||||
text: The text of the item.
|
||||
icon: The icon of the item.
|
||||
url: The URL of the item.
|
||||
|
||||
Returns:
|
||||
rx.Component: The sidebar item component.
|
||||
"""
|
||||
# Whether the item is active.
|
||||
active = (rx.State.router.page.path == f"/{text.lower()}") | (
|
||||
(rx.State.router.page.path == "/") & text == "Home"
|
||||
)
|
||||
|
||||
return rx.link(
|
||||
rx.hstack(
|
||||
rx.image(
|
||||
src=icon,
|
||||
height="2.5em",
|
||||
padding="0.5em",
|
||||
),
|
||||
rx.text(
|
||||
text,
|
||||
),
|
||||
bg=rx.cond(
|
||||
active,
|
||||
styles.accent_color,
|
||||
"transparent",
|
||||
),
|
||||
color=rx.cond(
|
||||
active,
|
||||
styles.accent_text_color,
|
||||
styles.text_color,
|
||||
),
|
||||
border_radius=styles.border_radius,
|
||||
box_shadow=styles.box_shadow,
|
||||
width="100%",
|
||||
padding_x="1em",
|
||||
),
|
||||
href=url,
|
||||
width="100%",
|
||||
)
|
||||
|
||||
|
||||
def sidebar() -> rx.Component:
|
||||
"""The sidebar.
|
||||
|
||||
Returns:
|
||||
The sidebar component.
|
||||
"""
|
||||
# Get all the decorated pages and add them to the sidebar.
|
||||
from reflex.page import get_decorated_pages
|
||||
|
||||
return rx.box(
|
||||
rx.vstack(
|
||||
sidebar_header(),
|
||||
rx.vstack(
|
||||
*[
|
||||
sidebar_item(
|
||||
text=page.get("title", page["route"].strip("/").capitalize()),
|
||||
icon=page.get("image", "/github.svg"),
|
||||
url=page["route"],
|
||||
)
|
||||
for page in get_decorated_pages()
|
||||
],
|
||||
width="100%",
|
||||
overflow_y="auto",
|
||||
align_items="flex-start",
|
||||
padding="1em",
|
||||
),
|
||||
rx.spacer(),
|
||||
sidebar_footer(),
|
||||
height="100dvh",
|
||||
),
|
||||
display=["none", "none", "block"],
|
||||
min_width=styles.sidebar_width,
|
||||
height="100%",
|
||||
position="sticky",
|
||||
top="0px",
|
||||
border_right=styles.border,
|
||||
)
|
||||
2
reflex_ipad/models/__init__.py
Normal file
2
reflex_ipad/models/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from .medicine import *
|
||||
from .scan import *
|
||||
6
reflex_ipad/models/base.py
Normal file
6
reflex_ipad/models/base.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import reflex as rx
|
||||
|
||||
|
||||
class BaseModel(rx.Model):
|
||||
def as_dict(self):
|
||||
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||
29
reflex_ipad/models/medicine.py
Normal file
29
reflex_ipad/models/medicine.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import reflex as rx
|
||||
from typing import Optional, List
|
||||
|
||||
from sqlmodel import Field, Session, SQLModel, create_engine, select, Relationship
|
||||
|
||||
|
||||
class Owner(rx.Model, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
name: str
|
||||
medicines: List["Medicine"] = Relationship(back_populates="owner")
|
||||
|
||||
|
||||
class Medicine(rx.Model, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
name: str
|
||||
owner_id: int = Field(default=None, foreign_key="owner.id")
|
||||
owner: Owner = Relationship(back_populates="medicines")
|
||||
pzn: str
|
||||
package_size: Optional[int] = Field(default=None)
|
||||
uuid: str = Field(index=True)
|
||||
log: List["MedicineLog"] = Relationship(back_populates="medicine")
|
||||
cron: Optional[str] = Field(default=None)
|
||||
|
||||
|
||||
class MedicineLog(rx.Model, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
medicine_id: Optional[int] = Field(default=None, foreign_key="medicine.id")
|
||||
medicine: Optional[Medicine] = Relationship(back_populates="log")
|
||||
timestamp: int
|
||||
11
reflex_ipad/models/scan.py
Normal file
11
reflex_ipad/models/scan.py
Normal file
@@ -0,0 +1,11 @@
|
||||
import reflex as rx
|
||||
from .base import *
|
||||
from typing import Optional, List
|
||||
|
||||
from sqlmodel import Field, Session, SQLModel, create_engine, select, Relationship
|
||||
|
||||
|
||||
class Scan(BaseModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
uuid: str = Field(index=True)
|
||||
timestamp: int = Field(index=True)
|
||||
6
reflex_ipad/pages/__init__.py
Normal file
6
reflex_ipad/pages/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from .dashboard import dashboard
|
||||
from .index import index
|
||||
from .settings import settings
|
||||
from .medicine import medicine
|
||||
|
||||
|
||||
21
reflex_ipad/pages/dashboard.py
Normal file
21
reflex_ipad/pages/dashboard.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""The dashboard page."""
|
||||
from reflex_ipad.templates import template
|
||||
|
||||
import reflex as rx
|
||||
|
||||
|
||||
@template(route="/dashboard", title="Dashboard")
|
||||
def dashboard() -> rx.Component:
|
||||
"""The dashboard page.
|
||||
|
||||
Returns:
|
||||
The UI for the dashboard page.
|
||||
"""
|
||||
return rx.vstack(
|
||||
rx.heading("Dashboard", font_size="3em"),
|
||||
rx.text("Welcome to Reflex!"),
|
||||
rx.text(
|
||||
"You can edit this page in ",
|
||||
rx.code("{your_app}/pages/dashboard.py"),
|
||||
),
|
||||
)
|
||||
18
reflex_ipad/pages/index.py
Normal file
18
reflex_ipad/pages/index.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""The home page of the app."""
|
||||
|
||||
from reflex_ipad import styles
|
||||
from reflex_ipad.templates import template
|
||||
|
||||
import reflex as rx
|
||||
|
||||
|
||||
@template(route="/", title="Home", image="/github.svg")
|
||||
def index() -> rx.Component:
|
||||
"""The home page.
|
||||
|
||||
Returns:
|
||||
The UI for the home page.
|
||||
"""
|
||||
with open("README.md", encoding="utf-8") as readme:
|
||||
content = readme.read()
|
||||
return rx.markdown(content, component_map=styles.markdown_style)
|
||||
246
reflex_ipad/pages/medicine.py
Normal file
246
reflex_ipad/pages/medicine.py
Normal file
@@ -0,0 +1,246 @@
|
||||
"""The meds page."""
|
||||
from reflex_ipad import styles
|
||||
from reflex_ipad.templates import template
|
||||
from reflex_ipad.state import State
|
||||
import datetime
|
||||
import asyncio
|
||||
import time
|
||||
|
||||
import reflex as rx
|
||||
from reflex_ipad.models import *
|
||||
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
||||
|
||||
|
||||
class NewMedicineState(rx.State):
|
||||
"""Define your app state here."""
|
||||
|
||||
show_med_add_form: bool = False
|
||||
medicine: Optional[Medicine] = None
|
||||
medicine_name: str = ""
|
||||
last_scan: Scan = Scan(uuid="", timestamp=0)
|
||||
last_scan_uuid: str = ""
|
||||
last_scan_time: str = ""
|
||||
updated_uuid: bool = False
|
||||
scanning: bool = False
|
||||
rate: float = 2
|
||||
lastupdatetime: int = time.time()
|
||||
|
||||
def start_scan(self):
|
||||
self.set_scanning(True)
|
||||
self.lastupdatetime = time.time()
|
||||
self.last_scan_uuid = ""
|
||||
return NewMedicineState.do_scanning
|
||||
|
||||
def load_last_scan(self):
|
||||
with rx.session() as session:
|
||||
statement = select(Scan).order_by(Scan.timestamp.desc()).limit(1)
|
||||
results = session.exec(statement)
|
||||
self.last_scan = results.first()
|
||||
if self.last_scan.timestamp > self.lastupdatetime:
|
||||
self.last_scan_uuid = self.last_scan.uuid
|
||||
if not self.show_med_add_form:
|
||||
self.update_medicine(self.last_scan.uuid)
|
||||
self.updated_uuid = True
|
||||
self.lastupdatetime = time.time()
|
||||
ts = datetime.datetime.fromtimestamp(self.last_scan.timestamp)
|
||||
self.last_scan_time = ts.strftime("%Y-%m-%d %H:%M")
|
||||
|
||||
def uuid_used(self):
|
||||
self.updated_uuid = False
|
||||
|
||||
def update_medicine(self, uuid):
|
||||
with rx.session() as session:
|
||||
statement = select(Medicine).where(Medicine.uuid == uuid)
|
||||
results = session.exec(statement)
|
||||
self.medicine = results.first()
|
||||
if self.medicine is not None:
|
||||
self.medicine_name = f"{self.medicine.name} - {self.medicine.owner.name}"
|
||||
|
||||
@rx.background
|
||||
async def do_scanning(self):
|
||||
while True:
|
||||
await asyncio.sleep(1 / self.rate)
|
||||
if not self.scanning:
|
||||
break
|
||||
|
||||
async with self:
|
||||
self.load_last_scan()
|
||||
|
||||
def do_show_med_add_form(self):
|
||||
self.set_show_med_add_form(True)
|
||||
|
||||
def stop_show_med_add_form(self):
|
||||
self.set_show_med_add_form(False)
|
||||
|
||||
def handle_submit(self, form_data: dict):
|
||||
self.form_data = form_data
|
||||
with rx.session() as session:
|
||||
statement = select(Owner).where(Owner.name == form_data["owner"])
|
||||
results = session.exec(statement)
|
||||
owner = results.first()
|
||||
medicine = Medicine(
|
||||
name=form_data["name"],
|
||||
package_size=form_data["pkg_size"],
|
||||
pzn=form_data["pzn"] or "",
|
||||
owner_id=owner.id,
|
||||
uuid=State.last_scan_uuid,
|
||||
cron=form_data["schedule"],
|
||||
)
|
||||
session.add(medicine)
|
||||
session.commit()
|
||||
self.uuid_used()
|
||||
self.set_show_med_add_form(False)
|
||||
|
||||
def handle_log(self, form_data: dict):
|
||||
with rx.session() as session:
|
||||
statement = select(Medicine).where(Medicine.uuid == form_data["uuid"])
|
||||
results = session.exec(statement)
|
||||
medicine = results.first()
|
||||
medicineLog = MedicineLog(medicine_id=medicine.id, timestamp=time.time())
|
||||
session.add(medicineLog)
|
||||
session.commit()
|
||||
self.uuid_used()
|
||||
|
||||
def cancel_log(self, form_data: dict):
|
||||
self.uuid_used()
|
||||
|
||||
|
||||
def new_medicine_form():
|
||||
with rx.session() as session:
|
||||
statement = select(Owner)
|
||||
results = session.exec(statement)
|
||||
owners = results.all()
|
||||
return rx.vstack(
|
||||
rx.form(
|
||||
rx.vstack(
|
||||
rx.input(
|
||||
placeholder="Name",
|
||||
name="name",
|
||||
),
|
||||
rx.input(
|
||||
placeholder="Packungsgröße",
|
||||
name="pkg_size",
|
||||
),
|
||||
rx.input(
|
||||
placeholder="PZN",
|
||||
name="pzn",
|
||||
),
|
||||
rx.select(
|
||||
[owner.name for owner in owners],
|
||||
placeholder="Für wen",
|
||||
name="owner",
|
||||
),
|
||||
rx.hstack(
|
||||
rx.text("Crontab lines für Einnahme"),
|
||||
rx.popover(
|
||||
rx.popover_trigger(rx.button("Help")),
|
||||
rx.popover_content(
|
||||
rx.popover_header("Crontab help"),
|
||||
rx.popover_body(
|
||||
rx.html("<pre>0 7 * * * </pre>"),
|
||||
"Täglich sieben Uhr",
|
||||
),
|
||||
rx.popover_body(
|
||||
rx.html("<pre>0 21 * * 0 </pre>"),
|
||||
"Jeden Sonntag um 21:00 Uhr.",
|
||||
),
|
||||
rx.popover_body(
|
||||
rx.html("<pre>0 7 * * 1-5 </pre>"),
|
||||
"Montags bis Freitags jeweils um 07:00",
|
||||
),
|
||||
rx.popover_close_button(),
|
||||
),
|
||||
),
|
||||
),
|
||||
rx.text_area(name="schedule"),
|
||||
rx.input(
|
||||
value=NewMedicineState.last_scan_uuid,
|
||||
name="uuid",
|
||||
placeholder="UUID",
|
||||
disabled=True,
|
||||
),
|
||||
rx.hstack(
|
||||
rx.text("Gescannt:"),
|
||||
rx.text(
|
||||
NewMedicineState.last_scan_time,
|
||||
),
|
||||
),
|
||||
rx.button("Submit", type_="submit"),
|
||||
),
|
||||
on_submit=NewMedicineState.handle_submit,
|
||||
reset_on_submit=True,
|
||||
),
|
||||
rx.divider(),
|
||||
)
|
||||
|
||||
|
||||
def taken_form():
|
||||
with rx.session() as session:
|
||||
if NewMedicineState.medicine is None:
|
||||
return rx.vstack()
|
||||
|
||||
return rx.vstack(
|
||||
rx.heading(NewMedicineState.medicine_name, size="lg", color="darkblue"),
|
||||
rx.hstack(
|
||||
rx.form(
|
||||
rx.button("Ja", type_="submit", color_scheme="green", size="lg"),
|
||||
rx.input(
|
||||
value=NewMedicineState.last_scan_uuid,
|
||||
name="uuid",
|
||||
disabled=True,
|
||||
hidden=True,
|
||||
type_="hidden",
|
||||
),
|
||||
on_submit=NewMedicineState.handle_log,
|
||||
),
|
||||
rx.form(
|
||||
rx.button("Nein", type_="submit", color_scheme="red", size="lg"),
|
||||
on_submit=NewMedicineState.cancel_log,
|
||||
),
|
||||
reset_on_submit=True,
|
||||
),
|
||||
rx.divider(),
|
||||
)
|
||||
|
||||
|
||||
@template(route="/medicine", title="Medikamente")
|
||||
def medicine() -> rx.Component:
|
||||
"""The dashboard page.
|
||||
|
||||
Returns:
|
||||
The UI for the dashboard page.
|
||||
"""
|
||||
return rx.vstack(
|
||||
rx.hstack(
|
||||
rx.spacer(),
|
||||
rx.heading("Medikamente", font_size="3em"),
|
||||
rx.spacer(),
|
||||
rx.cond(
|
||||
NewMedicineState.show_med_add_form,
|
||||
rx.button(
|
||||
rx.text(
|
||||
"-",
|
||||
),
|
||||
on_click=NewMedicineState.stop_show_med_add_form(),
|
||||
),
|
||||
rx.button(
|
||||
rx.text(
|
||||
"+",
|
||||
),
|
||||
on_click=NewMedicineState.do_show_med_add_form(),
|
||||
),
|
||||
),
|
||||
width="100%",
|
||||
border_bottom=styles.border,
|
||||
padding="1em",
|
||||
),
|
||||
rx.cond(NewMedicineState.show_med_add_form, new_medicine_form()),
|
||||
rx.cond(
|
||||
NewMedicineState.updated_uuid,
|
||||
taken_form(),
|
||||
rx.text(
|
||||
"Scan the Med",
|
||||
),
|
||||
),
|
||||
on_mount=NewMedicineState.start_scan,
|
||||
)
|
||||
20
reflex_ipad/pages/meds.py
Normal file
20
reflex_ipad/pages/meds.py
Normal file
@@ -0,0 +1,20 @@
|
||||
"""The meds page."""
|
||||
from reflex_ipad.templates import template
|
||||
|
||||
import reflex as rx
|
||||
|
||||
|
||||
@template(route="/medicine", title="Medikamente")
|
||||
def dashboard() -> rx.Component:
|
||||
"""The dashboard page.
|
||||
|
||||
Returns:
|
||||
The UI for the dashboard page.
|
||||
"""
|
||||
return rx.vstack(
|
||||
rx.heading("Medikamente", font_size="3em"),
|
||||
rx.text("Heute schon genommen?"),
|
||||
rx.text(
|
||||
"Scan the Med",
|
||||
),
|
||||
)
|
||||
22
reflex_ipad/pages/settings.py
Normal file
22
reflex_ipad/pages/settings.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""The settings page."""
|
||||
|
||||
from reflex_ipad.templates import template
|
||||
|
||||
import reflex as rx
|
||||
|
||||
|
||||
@template(route="/settings", title="Settings")
|
||||
def settings() -> rx.Component:
|
||||
"""The settings page.
|
||||
|
||||
Returns:
|
||||
The UI for the settings page.
|
||||
"""
|
||||
return rx.vstack(
|
||||
rx.heading("Settings", font_size="3em"),
|
||||
rx.text("Welcome to Reflex!"),
|
||||
rx.text(
|
||||
"You can edit this page in ",
|
||||
rx.code("{your_app}/pages/settings.py"),
|
||||
),
|
||||
)
|
||||
17
reflex_ipad/reflex_ipad.py
Normal file
17
reflex_ipad/reflex_ipad.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""Welcome to Reflex!."""
|
||||
|
||||
from reflex_ipad import styles
|
||||
|
||||
# Import all the pages.
|
||||
from reflex_ipad.pages import *
|
||||
from reflex_ipad import api
|
||||
from reflex_ipad.models import *
|
||||
|
||||
import reflex as rx
|
||||
|
||||
# Create the app and compile it.
|
||||
app = rx.App(style=styles.base_style)
|
||||
|
||||
api.register_at(app)
|
||||
|
||||
app.compile()
|
||||
9
reflex_ipad/state.py
Normal file
9
reflex_ipad/state.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import reflex as rx
|
||||
import time
|
||||
import datetime
|
||||
import asyncio
|
||||
from reflex_ipad.models import *
|
||||
|
||||
class State(rx.State):
|
||||
"""Define your app state here."""
|
||||
pass
|
||||
62
reflex_ipad/styles.py
Normal file
62
reflex_ipad/styles.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""Styles for the app."""
|
||||
|
||||
import reflex as rx
|
||||
|
||||
border_radius = "0.375rem"
|
||||
box_shadow = "0px 0px 0px 1px rgba(84, 82, 95, 0.14)"
|
||||
border = "1px solid #F4F3F6"
|
||||
text_color = "black"
|
||||
accent_text_color = "#1A1060"
|
||||
accent_color = "#F5EFFE"
|
||||
hover_accent_color = {"_hover": {"color": accent_color}}
|
||||
hover_accent_bg = {"_hover": {"bg": accent_color}}
|
||||
content_width_vw = "90vw"
|
||||
sidebar_width = "20em"
|
||||
|
||||
template_page_style = {"padding_top": "5em", "padding_x": ["auto", "2em"], "flex": "1"}
|
||||
|
||||
template_content_style = {
|
||||
"align_items": "flex-start",
|
||||
"box_shadow": box_shadow,
|
||||
"border_radius": border_radius,
|
||||
"padding": "1em",
|
||||
"margin_bottom": "2em",
|
||||
}
|
||||
|
||||
link_style = {
|
||||
"color": text_color,
|
||||
"text_decoration": "none",
|
||||
**hover_accent_color,
|
||||
}
|
||||
|
||||
overlapping_button_style = {
|
||||
"background_color": "white",
|
||||
"border": border,
|
||||
"border_radius": border_radius,
|
||||
}
|
||||
|
||||
base_style = {
|
||||
rx.MenuButton: {
|
||||
"width": "3em",
|
||||
"height": "3em",
|
||||
**overlapping_button_style,
|
||||
},
|
||||
rx.MenuItem: hover_accent_bg,
|
||||
}
|
||||
|
||||
markdown_style = {
|
||||
"code": lambda text: rx.code(text, color="#1F1944", bg="#EAE4FD"),
|
||||
"a": lambda text, **props: rx.link(
|
||||
text,
|
||||
**props,
|
||||
font_weight="bold",
|
||||
color="#03030B",
|
||||
text_decoration="underline",
|
||||
text_decoration_color="#AD9BF8",
|
||||
_hover={
|
||||
"color": "#AD9BF8",
|
||||
"text_decoration": "underline",
|
||||
"text_decoration_color": "#03030B",
|
||||
},
|
||||
),
|
||||
}
|
||||
1
reflex_ipad/templates/__init__.py
Normal file
1
reflex_ipad/templates/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .template import template
|
||||
127
reflex_ipad/templates/template.py
Normal file
127
reflex_ipad/templates/template.py
Normal file
@@ -0,0 +1,127 @@
|
||||
"""Common templates used between pages in the app."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from reflex_ipad import styles
|
||||
from reflex_ipad.components.sidebar import sidebar
|
||||
from typing import Callable
|
||||
|
||||
import reflex as rx
|
||||
|
||||
# Meta tags for the app.
|
||||
default_meta = [
|
||||
{
|
||||
"name": "viewport",
|
||||
"content": "width=device-width, shrink-to-fit=no, initial-scale=1",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def menu_button() -> rx.Component:
|
||||
"""The menu button on the top right of the page.
|
||||
|
||||
Returns:
|
||||
The menu button component.
|
||||
"""
|
||||
from reflex.page import get_decorated_pages
|
||||
|
||||
return rx.box(
|
||||
rx.menu(
|
||||
rx.menu_button(
|
||||
rx.icon(
|
||||
tag="hamburger",
|
||||
size="4em",
|
||||
color=styles.text_color,
|
||||
),
|
||||
),
|
||||
rx.menu_list(
|
||||
*[
|
||||
rx.menu_item(
|
||||
rx.link(
|
||||
page["title"],
|
||||
href=page["route"],
|
||||
width="100%",
|
||||
)
|
||||
)
|
||||
for page in get_decorated_pages()
|
||||
],
|
||||
rx.menu_divider(),
|
||||
rx.menu_item(
|
||||
rx.link("About", href="https://github.com/reflex-dev", width="100%")
|
||||
),
|
||||
rx.menu_item(
|
||||
rx.link("Contact", href="mailto:founders@=reflex.dev", width="100%")
|
||||
),
|
||||
),
|
||||
),
|
||||
position="fixed",
|
||||
right="1.5em",
|
||||
top="1.5em",
|
||||
z_index="500",
|
||||
)
|
||||
|
||||
|
||||
def template(
|
||||
route: str | None = None,
|
||||
title: str | None = None,
|
||||
image: str | None = None,
|
||||
description: str | None = None,
|
||||
meta: str | None = None,
|
||||
script_tags: list[rx.Component] | None = None,
|
||||
on_load: rx.event.EventHandler | list[rx.event.EventHandler] | None = None,
|
||||
) -> Callable[[Callable[[], rx.Component]], rx.Component]:
|
||||
"""The template for each page of the app.
|
||||
|
||||
Args:
|
||||
route: The route to reach the page.
|
||||
title: The title of the page.
|
||||
image: The favicon of the page.
|
||||
description: The description of the page.
|
||||
meta: Additionnal meta to add to the page.
|
||||
on_load: The event handler(s) called when the page load.
|
||||
script_tags: Scripts to attach to the page.
|
||||
|
||||
Returns:
|
||||
The template with the page content.
|
||||
"""
|
||||
|
||||
def decorator(page_content: Callable[[], rx.Component]) -> rx.Component:
|
||||
"""The template for each page of the app.
|
||||
|
||||
Args:
|
||||
page_content: The content of the page.
|
||||
|
||||
Returns:
|
||||
The template with the page content.
|
||||
"""
|
||||
# Get the meta tags for the page.
|
||||
all_meta = [*default_meta, *(meta or [])]
|
||||
|
||||
@rx.page(
|
||||
route=route,
|
||||
title=title,
|
||||
image=image,
|
||||
description=description,
|
||||
meta=all_meta,
|
||||
script_tags=script_tags,
|
||||
on_load=on_load,
|
||||
)
|
||||
def templated_page():
|
||||
return rx.hstack(
|
||||
sidebar(),
|
||||
rx.box(
|
||||
rx.box(
|
||||
page_content(),
|
||||
**styles.template_content_style,
|
||||
),
|
||||
**styles.template_page_style,
|
||||
),
|
||||
menu_button(),
|
||||
align_items="flex-start",
|
||||
transition="left 0.5s, width 0.5s",
|
||||
position="relative",
|
||||
)
|
||||
|
||||
return templated_page
|
||||
|
||||
return decorator
|
||||
Reference in New Issue
Block a user