Implement flexible entity relation system (Phases A–E)

Declarative relation registry via defrelation s-expressions with
cardinality enforcement (one-to-one, one-to-many, many-to-many),
registry-aware relate/unrelate/can-relate API endpoints, generic
container-nav fragment, and relation-driven UI components.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 08:35:17 +00:00
parent 6f1d5bac3c
commit a0a0f5ebc2
17 changed files with 928 additions and 28 deletions

View File

@@ -36,6 +36,10 @@ def load_shared_components() -> None:
register_components(_SEARCH_DESKTOP)
register_components(_MOBILE_FILTER)
register_components(_ORDER_SUMMARY_CARD)
# Relation-driven components
register_components(_RELATION_NAV)
register_components(_RELATION_ATTACH)
register_components(_RELATION_DETACH)
# ---------------------------------------------------------------------------
@@ -768,3 +772,48 @@ _ORDER_SUMMARY_CARD = r'''
(str (or currency "GBP") " " total-amount)
"\u2013"))))
'''
# ---------------------------------------------------------------------------
# ~relation-nav
# ---------------------------------------------------------------------------
_RELATION_NAV = '''
(defcomp ~relation-nav (&key href name icon nav-class relation-type)
(a :href href :class (or nav-class "flex items-center gap-3 rounded-lg py-2 px-3 text-sm text-stone-700 hover:bg-stone-100 transition-colors")
(when icon
(div :class "w-8 h-8 rounded bg-stone-200 flex items-center justify-center flex-shrink-0"
(i :class icon :aria-hidden "true")))
(div :class "flex-1 min-w-0"
(div :class "font-medium truncate" name))))
'''
# ---------------------------------------------------------------------------
# ~relation-attach
# ---------------------------------------------------------------------------
_RELATION_ATTACH = '''
(defcomp ~relation-attach (&key create-url label icon)
(a :href create-url
:hx-get create-url
:hx-target "#main-panel"
:hx-swap "outerHTML"
:hx-push-url "true"
:class "flex items-center gap-2 text-sm p-2 rounded hover:bg-stone-100 text-stone-500 hover:text-stone-700 transition-colors"
(when icon (i :class icon))
(span (or label "Add"))))
'''
# ---------------------------------------------------------------------------
# ~relation-detach
# ---------------------------------------------------------------------------
_RELATION_DETACH = '''
(defcomp ~relation-detach (&key detach-url name)
(button :hx-delete detach-url
:hx-confirm (str "Remove " (or name "this item") "?")
:class "text-red-500 hover:text-red-700 text-sm p-1 rounded hover:bg-red-50 transition-colors"
(i :class "fa fa-times" :aria-hidden "true")))
'''