from __future__ import annotations import htpy as h from htpy import Node, Renderable def base_layout(*, page_title: str, stylesheet_href: str, content: Node) -> Renderable: return h.html(lang="en", class_="h-full bg-slate-100")[ h.head[ h.meta(charset="utf-8"), h.meta(name="viewport", content="width=device-width, initial-scale=1"), h.title[page_title], h.link(rel="stylesheet", href=stylesheet_href), ], h.body( class_="h-full bg-linear-to-br from-stone-100 via-amber-50 to-orange-100 text-slate-900" )[content], ] def nav_link( *, label: str, active: bool = False, badge: str | None = None ) -> Renderable: link_class = ( "group flex items-center justify-between rounded-xl px-3 py-2 text-sm font-medium transition " + ( "bg-white text-slate-950 shadow-sm ring-1 ring-white/10" if active else "text-slate-300 hover:bg-white/5 hover:text-white" ) ) badge_class = "rounded-full px-2 py-0.5 text-[11px] font-semibold " + ( "bg-amber-200 text-amber-950" if active else "bg-slate-800 text-slate-300" ) return h.a(href="#", class_=link_class)[ h.span[label], badge and h.span(class_=badge_class)[badge], ] def stat_card(*, label: str, value: str, detail: str) -> Renderable: return h.div( class_="rounded-3xl bg-white/85 p-5 shadow-sm ring-1 ring-slate-200 backdrop-blur" )[ h.dt(class_="text-sm font-medium text-slate-500")[label], h.dd(class_="mt-3 text-3xl font-semibold tracking-tight text-slate-950")[value], h.p(class_="mt-2 text-sm text-slate-600")[detail], ] def input_field( *, label: str, field_id: str, value: str = "", placeholder: str = "", help_text: str | None = None, ) -> Renderable: return h.div[ h.label(for_=field_id, class_="block text-sm font-medium text-slate-900")[ label ], h.input( id=field_id, name=field_id, type="text", value=value, placeholder=placeholder, class_="mt-2 block w-full rounded-2xl border-0 bg-white px-3.5 py-2.5 text-sm text-slate-900 shadow-sm ring-1 ring-slate-200 placeholder:text-slate-400 focus:outline-hidden focus:ring-2 focus:ring-amber-500", ), help_text and h.p(class_="mt-2 text-xs text-slate-500")[help_text], ] def select_field( *, label: str, field_id: str, options: tuple[str, ...], selected: str, help_text: str | None = None, ) -> Renderable: return h.div[ h.label(for_=field_id, class_="block text-sm font-medium text-slate-900")[ label ], h.select( id=field_id, name=field_id, class_="mt-2 block w-full rounded-2xl border-0 bg-white px-3.5 py-2.5 text-sm text-slate-900 shadow-sm ring-1 ring-slate-200 focus:outline-hidden focus:ring-2 focus:ring-amber-500", )[ ( h.option(value=option, selected=option == selected)[option] for option in options ) ], help_text and h.p(class_="mt-2 text-xs text-slate-500")[help_text], ] def textarea_field( *, label: str, field_id: str, value: str, rows: str = "4" ) -> Renderable: return h.div[ h.label(for_=field_id, class_="block text-sm font-medium text-slate-900")[ label ], h.textarea( id=field_id, name=field_id, rows=rows, class_="mt-2 block w-full rounded-2xl border-0 bg-white px-3.5 py-2.5 text-sm text-slate-900 shadow-sm ring-1 ring-slate-200 placeholder:text-slate-400 focus:outline-hidden focus:ring-2 focus:ring-amber-500", )[value], ] def toggle_field(*, label: str, description: str, checked: bool = False) -> Renderable: wrapper_class = ( "group relative inline-flex w-11 shrink-0 rounded-full p-0.5 outline-offset-2 outline-amber-500 transition " + ("bg-amber-500" if checked else "bg-slate-200") ) knob_class = ( "size-5 rounded-full bg-white shadow-xs ring-1 ring-slate-900/5 transition-transform " + ("translate-x-5" if checked else "translate-x-0") ) return h.div(class_="rounded-3xl bg-stone-50 p-4 ring-1 ring-slate-200")[ h.div(class_="flex items-start justify-between gap-4")[ h.div[ h.h3(class_="text-sm font-semibold text-slate-900")[label], h.p(class_="mt-1 text-sm text-slate-600")[description], ], h.label(class_="mt-0.5 cursor-pointer")[ h.div(class_=wrapper_class)[ h.span(class_=knob_class), h.input(type="checkbox", checked=checked, class_="sr-only"), ], ], ] ] def status_badge(*, label: str, tone: str) -> Renderable: tones = { "running": "bg-emerald-100 text-emerald-800", "scheduled": "bg-sky-100 text-sky-800", "idle": "bg-slate-200 text-slate-700", "failed": "bg-rose-100 text-rose-800", "done": "bg-amber-100 text-amber-800", } return h.span( class_=f"inline-flex rounded-full px-2.5 py-1 text-xs font-semibold {tones[tone]}" )[label]