Remove widget system — fully replaced by fragment composition
Delete shared/contracts/widgets.py, shared/services/widget_registry.py, and shared/services/widgets/ (empty stubs). Remove register_all_widgets() from factory and widgets Jinja global from jinja_setup. Zero consumers remain — all cross-app UI composition now uses the fragment API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
"""Widget descriptors for cross-domain UI composition.
|
||||
|
||||
Each widget type describes a UI fragment that one domain contributes to
|
||||
another domain's page. Host apps iterate widgets generically — they never
|
||||
name the contributing domain.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Callable
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class NavWidget:
|
||||
"""Renders nav items on a container page (entries, calendars, markets)."""
|
||||
domain: str
|
||||
order: int
|
||||
context_fn: Callable # async (session, *, container_type, container_id, **kw) -> dict
|
||||
template: str
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class CardWidget:
|
||||
"""Decorates content cards in listings with domain data."""
|
||||
domain: str
|
||||
order: int
|
||||
batch_fn: Callable # async (session, post_ids) -> dict[int, list]
|
||||
context_key: str # key injected into each post dict
|
||||
template: str
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class AccountPageWidget:
|
||||
"""Sub-page under /auth/<slug>/."""
|
||||
domain: str
|
||||
slug: str
|
||||
label: str
|
||||
order: int
|
||||
context_fn: Callable # async (session, *, user_id, **kw) -> dict
|
||||
template: str
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class AccountNavLink:
|
||||
"""Nav link on account page (internal or external)."""
|
||||
label: str
|
||||
order: int
|
||||
href_fn: Callable # () -> str
|
||||
external: bool = False
|
||||
@@ -67,9 +67,6 @@ def create_base_app(
|
||||
if domain_services_fn is not None:
|
||||
domain_services_fn()
|
||||
|
||||
from shared.services.widgets import register_all_widgets
|
||||
register_all_widgets()
|
||||
|
||||
app = Quart(
|
||||
name,
|
||||
static_folder=STATIC_DIR,
|
||||
|
||||
@@ -104,10 +104,6 @@ def setup_jinja(app: Quart) -> None:
|
||||
app.jinja_env.globals["page_cart_url"] = page_cart_url
|
||||
app.jinja_env.globals["market_product_url"] = market_product_url
|
||||
|
||||
# widget registry available in all templates
|
||||
from shared.services.widget_registry import widgets as _widget_registry
|
||||
app.jinja_env.globals["widgets"] = _widget_registry
|
||||
|
||||
# fragment composition helper — fetch HTML from another app's fragment API
|
||||
from shared.infrastructure.fragments import fetch_fragment_cached
|
||||
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
"""Singleton widget registry for cross-domain UI composition.
|
||||
|
||||
Usage::
|
||||
|
||||
from shared.services.widget_registry import widgets
|
||||
|
||||
# Register at app startup (after domain services)
|
||||
widgets.add_container_nav(NavWidget(...))
|
||||
|
||||
# Query in templates / context processors
|
||||
for w in widgets.container_nav:
|
||||
ctx = await w.context_fn(session, container_type="page", ...)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from shared.contracts.widgets import (
|
||||
NavWidget,
|
||||
CardWidget,
|
||||
AccountPageWidget,
|
||||
AccountNavLink,
|
||||
)
|
||||
|
||||
|
||||
class _WidgetRegistry:
|
||||
"""Central registry holding all widget descriptors.
|
||||
|
||||
Widgets are added at startup and read at request time.
|
||||
Properties return sorted-by-order copies.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._container_nav: list[NavWidget] = []
|
||||
self._container_card: list[CardWidget] = []
|
||||
self._account_pages: list[AccountPageWidget] = []
|
||||
self._account_nav: list[AccountNavLink] = []
|
||||
|
||||
# -- registration ---------------------------------------------------------
|
||||
|
||||
def add_container_nav(self, w: NavWidget) -> None:
|
||||
self._container_nav.append(w)
|
||||
|
||||
def add_container_card(self, w: CardWidget) -> None:
|
||||
self._container_card.append(w)
|
||||
|
||||
def add_account_page(self, w: AccountPageWidget) -> None:
|
||||
self._account_pages.append(w)
|
||||
# Auto-create a matching internal nav link
|
||||
slug = w.slug
|
||||
|
||||
def _href(s=slug):
|
||||
from shared.infrastructure.urls import account_url
|
||||
return account_url(f"/{s}/")
|
||||
|
||||
self._account_nav.append(AccountNavLink(
|
||||
label=w.label,
|
||||
order=w.order,
|
||||
href_fn=_href,
|
||||
external=False,
|
||||
))
|
||||
|
||||
def add_account_link(self, link: AccountNavLink) -> None:
|
||||
self._account_nav.append(link)
|
||||
|
||||
# -- read access (sorted copies) ------------------------------------------
|
||||
|
||||
@property
|
||||
def container_nav(self) -> list[NavWidget]:
|
||||
return sorted(self._container_nav, key=lambda w: w.order)
|
||||
|
||||
@property
|
||||
def container_cards(self) -> list[CardWidget]:
|
||||
return sorted(self._container_card, key=lambda w: w.order)
|
||||
|
||||
@property
|
||||
def account_pages(self) -> list[AccountPageWidget]:
|
||||
return sorted(self._account_pages, key=lambda w: w.order)
|
||||
|
||||
@property
|
||||
def account_nav(self) -> list[AccountNavLink]:
|
||||
return sorted(self._account_nav, key=lambda w: w.order)
|
||||
|
||||
def account_page_by_slug(self, slug: str) -> AccountPageWidget | None:
|
||||
for w in self._account_pages:
|
||||
if w.slug == slug:
|
||||
return w
|
||||
return None
|
||||
|
||||
|
||||
# Module-level singleton — import this everywhere.
|
||||
widgets = _WidgetRegistry()
|
||||
@@ -1,22 +0,0 @@
|
||||
"""Per-domain widget registration.
|
||||
|
||||
Called once at startup after domain services are registered.
|
||||
Only registers widgets for domains that are actually available.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def register_all_widgets() -> None:
|
||||
from shared.services.registry import services
|
||||
|
||||
if services.has("calendar"):
|
||||
from .calendar_widgets import register_calendar_widgets
|
||||
register_calendar_widgets()
|
||||
|
||||
if services.has("market"):
|
||||
from .market_widgets import register_market_widgets
|
||||
register_market_widgets()
|
||||
|
||||
if services.has("cart"):
|
||||
from .cart_widgets import register_cart_widgets
|
||||
register_cart_widgets()
|
||||
@@ -1,10 +0,0 @@
|
||||
"""Calendar-domain widgets.
|
||||
|
||||
All calendar widgets have been replaced by fragments
|
||||
(events app serves them at /internal/fragments/).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def register_calendar_widgets() -> None:
|
||||
pass
|
||||
@@ -1,10 +0,0 @@
|
||||
"""Cart-domain widgets.
|
||||
|
||||
Account nav link has been replaced by fragments
|
||||
(cart app serves account-nav-item at /internal/fragments/).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def register_cart_widgets() -> None:
|
||||
pass
|
||||
@@ -1,10 +0,0 @@
|
||||
"""Market-domain widgets.
|
||||
|
||||
Container nav widgets have been replaced by fragments
|
||||
(market app serves them at /internal/fragments/).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def register_market_widgets() -> None:
|
||||
pass
|
||||
Reference in New Issue
Block a user