feat: extract shared infrastructure from shared_lib

Phase 1-3 of decoupling plan:
- Shared DB, models, infrastructure, browser, config, utils
- Event infrastructure (domain_events outbox, bus, processor)
- Structured logging
- Generic container concept (container_type/container_id)
- Alembic migrations for all schema changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
giles
2026-02-11 12:45:56 +00:00
commit ef806f8fbb
533 changed files with 276497 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
{#
Shared admin navigation macro
Use this instead of duplicate _nav.html files
#}
{% macro admin_nav_item(href, icon='cog', label='', select_colours='', aclass=styles.nav_button) %}
{% import 'macros/links.html' as links %}
{% call links.link(href, hx_select_search, select_colours, True, aclass=aclass) %}
<i class="fa fa-{{ icon }}" aria-hidden="true"></i>
{{ label }}
{% endcall %}
{% endmacro %}
{% macro placeholder_nav() %}
{# Placeholder for admin sections without specific nav items #}
<div class="relative nav-group">
<span class="block px-3 py-2 text-stone-400 text-sm italic">
Admin options
</span>
</div>
{% endmacro %}

View File

@@ -0,0 +1,7 @@
{% macro dt(d) -%}
{{ d.astimezone().strftime('%-d %b %Y, %H:%M') if d.tzinfo else d.strftime('%-d %b %Y, %H:%M') }}
{%- endmacro %}
{% macro t(d) -%}
{{ d.astimezone().strftime('%H:%M') if d.tzinfo else d.strftime('%H:%M') }}
{%- endmacro %}

View File

@@ -0,0 +1,117 @@
{#
Unified filter macros for browse/shop pages
Consolidates duplicate mobile/desktop filter components
#}
{% macro filter_item(href, is_on, title, icon_html, count=none, variant='desktop') %}
{#
Generic filter item (works for labels, stickers, etc.)
variant: 'desktop' or 'mobile'
#}
{% set base_class = "flex flex-col items-center justify-center" %}
{% if variant == 'mobile' %}
{% set item_class = base_class ~ " p-1 rounded hover:bg-stone-50" %}
{% set count_class = "text-[10px] text-stone-500 mt-1 leading-none tabular-nums" if count != 0 else "text-md text-red-500 font-bold mt-1 leading-none tabular-nums" %}
{% else %}
{% set item_class = base_class ~ " py-2 w-full h-full" %}
{% set count_class = "text-xs text-stone-500 leading-none justify-self-end tabular-nums" if count != 0 else "text-md text-red-500 font-bold leading-none justify-self-end tabular-nums" %}
{% endif %}
<a
href="{{ href }}"
hx-get="{{ href }}"
hx-target="#main-panel"
hx-select="{{ hx_select_search }}"
hx-swap="outerHTML"
hx-push-url="true"
role="button"
aria-pressed="{{ 'true' if is_on else 'false' }}"
title="{{ title }}"
aria-label="{{ title }}"
class="{{ item_class }}"
>
{{ icon_html | safe }}
{% if count is not none %}
<span class="{{ count_class }}">{{ count }}</span>
{% endif %}
</a>
{% endmacro %}
{% macro labels_list(labels, selected_labels, current_local_href, variant='desktop') %}
{#
Unified labels filter list
variant: 'desktop' or 'mobile'
#}
{% import 'macros/stickers.html' as stick %}
{% if variant == 'mobile' %}
<nav aria-label="labels" class="px-4 pb-3">
<ul class="flex w-full items-start justify-center gap-3 overflow-x-auto overflow-y-visible pr-1 no-scrollbar">
{% else %}
<ul id="labels-details-desktop" class="flex justify-center p-0 m-0 gap-2" >
{% endif %}
{% for s in labels %}
{% set is_on = (selected_labels and (s.name|lower in selected_labels)) %}
{% set qs = {"remove_label": s.name, "page": None}|qs if is_on else {"add_label": s.name, "page": None}|qs %}
{% set href = (current_local_href ~ qs)|host %}
<li class="{{ 'list-none shrink-0' if variant == 'mobile' else '' }}">
{{ filter_item(
href, is_on, s.name,
stick.sticker(asset_url('nav-labels/' ~ s.name ~ '.svg'), s.name, is_on),
s.count, variant
) }}
</li>
{% endfor %}
</ul>
{% if variant == 'mobile' %}
</nav>
{% endif %}
{% endmacro %}
{% macro stickers_list(stickers, selected_stickers, current_local_href, variant='desktop') %}
{#
Unified stickers filter list
variant: 'desktop' or 'mobile'
#}
{% import 'macros/stickers.html' as stick %}
{% if variant == 'mobile' %}
<nav aria-label="stickers" class="px-4 pb-3">
<ul class="flex w-full items-start justify-center gap-3 overflow-x-auto overflow-y-visible pr-1 no-scrollbar">
{% else %}
<ul id="stickers-details-desktop"
class="flex flex-wrap justify-center w-full p-0 m-0 border bg-white shadow-sm rounded-xl gap-1 [&>li]:list-none [&>li]:basis-[20%] [&>li]:max-w-[20%] [&>li]:grow-0"
>
{% endif %}
{% for s in stickers %}
{% set is_on = (selected_stickers and (s.name|lower in selected_stickers)) %}
{% set qs = {"remove_sticker": s.name, "page": None}|qs if is_on else {"add_sticker": s.name, "page": None}|qs %}
{% set href = (current_local_href ~ qs)|host %}
{% set display_name = s.name|capitalize if s.name|lower != 'sugarfree' else 'Sugar' %}
<li class="{{ 'list-none shrink-0' if variant == 'mobile' else '' }}">
{% set icon_html %}
<span class="{{ 'text-sm' if variant == 'mobile' else 'text-[11px]' }}">{{ display_name }}</span>
{{ stick.sticker(asset_url('stickers/' ~ s.name ~ '.svg'), s.name, is_on) }}
{% endset %}
{{ filter_item(href, is_on, s.name, icon_html, s.count, variant) }}
</li>
{% endfor %}
</ul>
{% if variant == 'mobile' %}
</nav>
<style>
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
</style>
{% endif %}
{% endmacro %}

View File

@@ -0,0 +1,17 @@
{% macro opener(group=False) %}
<svg
class="h-4 w-4 transition-transform group-open{{ '/' + group if group else ''}}:rotate-180"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 9l6 6 6-6"
/>
</svg>
{% endmacro %}

View File

@@ -0,0 +1,51 @@
{# templates/macros/layout.html #}
{% macro details(group = '', _class='') %}
<details
class="group{{group}} p-2 {{_class}}" data-toggle-group="mobile-panels">
{{ caller() }}
</details>
{%- endmacro %}
{% macro summary(id, _class=None, oob=False) %}
<summary>
<header class="z-50">
<div
id="{{id}}"
{% if oob %}
hx-swap-oob="true"
{% endif %}
class="{{'flex justify-between items-start gap-2' if not _class else _class}}">
{{ caller() }}
</div>
</header>
</summary>
{%- endmacro %}
{% macro filter_summary(id, current_local_href, search, search_count, hx_select, oob=True) %}
<summary class="bg-white/90">
<div class="flex flex-row items-start">
<div>
{% include '_types/blog/mobile/_filter/_hamburger.html' %}
</div>
<div
id="{{id}}"
class="flex-1 md:hidden grid grid-cols-12 items-center gap-3"
>
<div class="flex flex-col items-start gap-2">
{{ caller() }}
</div>
</div>
{% import '_types/browse/mobile/_filter/search.html' as s %}
{{ s.search(current_local_href, search, search_count, hx_select) }}
</div>
</summary>
{%- endmacro %}
{% macro menu(id, _class="") %}
<div id="{{id}}" hx-swap-oob="outerHTML" class="{{_class}}">
{{ caller() }}
</div>
{%- endmacro %}

View File

@@ -0,0 +1,59 @@
{% macro link(url, select, select_colours='', highlight=True, _class='', aclass='') %}
{% set href=url|host%}
<div class="relative nav-group {{_class}}">
<a
href="{{ href }}"
hx-get="{{ href }}"
hx-target="#main-panel"
hx-select="{{select}}"
hx-swap="outerHTML"
hx-push-url="true"
aria-selected="{{ 'true' if (request.path|host).startswith(href) else 'false' }}"
{% if aclass %}
class="{{aclass}}"
{% elif select_colours %}
class="whitespace-normal flex gap-2 px-3 py-2 rounded
text-center break-words leading-snug
bg-stone-200 text-black
{{select_colours if highlight else ''}}
"
{% else %}
class="w-full whitespace-normal flex items-center gap-2 font-bold text-2xl px-3 py-2"
{% endif %}
>
{{ caller() }}
</a>
</div>
{% endmacro %}
{% macro menu_row(id=False, oob=False) %}
<div
{% if id %}
id="{{id}}"
{% endif %}
{% if oob %}
hx-swap-oob="outerHTML"
{% endif %}
class="flex flex-col items-center md:flex-row justify-center md:justify-between w-full p-1 bg-{{menu_colour}}-{{(500-(level()*100))|string}}"
>
{{ caller() }}
</div>
{{level_up()}}
{% endmacro %}
{% macro desktop_nav() %}
<nav class="hidden md:flex gap-4 text-sm ml-2 justify-end items-center flex-0">
{{ caller() }}
</nav>
{% endmacro %}
{% macro admin() %}
<i class="fa fa-cog" aria-hidden="true"></i>
<div>
settings
</div>
{% endmacro %}

View File

@@ -0,0 +1,68 @@
{#
Scrolling menu macro with arrow navigation
Creates a horizontally scrollable menu (desktop) or vertically scrollable (mobile)
with arrow buttons that appear/hide based on content overflow.
Parameters:
- container_id: Unique ID for the scroll container
- items: List of items to iterate over
- item_content: Caller block that renders each item (receives 'item' variable)
- wrapper_class: Optional additional classes for outer wrapper
- container_class: Optional additional classes for scroll container
- item_class: Optional additional classes for each item wrapper
#}
{% macro scrolling_menu(container_id, items, wrapper_class='', container_class='', item_class='') %}
{% if items %}
{# Left scroll arrow - desktop only #}
<button
class="scrolling-menu-arrow-{{ container_id }} hidden flex-shrink-0 p-2 hover:bg-stone-200 rounded"
aria-label="Scroll left"
_="on click
set #{{ container_id }}.scrollLeft to #{{ container_id }}.scrollLeft - 200">
<i class="fa fa-chevron-left"></i>
</button>
{# Scrollable container #}
<div id="{{ container_id }}"
class="overflow-y-auto sm:overflow-x-auto sm:overflow-y-visible scrollbar-hide max-h-[50vh] sm:max-h-none {{ container_class }}"
style="scroll-behavior: smooth;"
_="on load or scroll
-- Show arrows if content overflows (desktop only)
if window.innerWidth >= 640 and my.scrollWidth > my.clientWidth
remove .hidden from .scrolling-menu-arrow-{{ container_id }}
add .flex to .scrolling-menu-arrow-{{ container_id }}
else
add .hidden to .scrolling-menu-arrow-{{ container_id }}
remove .flex from .scrolling-menu-arrow-{{ container_id }}
end">
<div class="flex flex-col sm:flex-row gap-1 {{ wrapper_class }}">
{% for item in items %}
<div class="{{ item_class }}">
{{ caller(item) }}
</div>
{% endfor %}
</div>
</div>
<style>
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
</style>
{# Right scroll arrow - desktop only #}
<button
class="scrolling-menu-arrow-{{ container_id }} hidden flex-shrink-0 p-2 hover:bg-stone-200 rounded"
aria-label="Scroll right"
_="on click
set #{{ container_id }}.scrollLeft to #{{ container_id }}.scrollLeft + 200">
<i class="fa fa-chevron-right"></i>
</button>
{% endif %}
{% endmacro %}

View File

@@ -0,0 +1,24 @@
{% macro sticker(src, title, enabled, size=40, found=false) -%}
<span class="relative inline-flex items-center justify-center group"
tabindex="0" aria-label="{{ title|capitalize }}">
<!-- sticker icon -->
<img
src="{{ src }}"
width="{{size}}" height="{{size}}"
alt="{{ title|capitalize }}"
title="{{ title|capitalize }}"
class="{% if found %}border-2 border-yellow-200 bg-yellow-300{% endif %} {%if enabled %} opacity-100 {% else %} opacity-40 saturate-0 {% endif %}"
/>
<!-- tooltip -->
<span role="tooltip"
class="pointer-events-none absolute z-50 bottom-full left-1/2 -translate-x-1/2 mb-2 hidden group-hover/tt:block group-focus-visible/tt:block whitespace-nowrap rounded-md bg-stone-900 text-white text-xs px-2 py-1 shadow-lg">
{{ title|capitalize if title|lower != 'sugarfree' else 'Sugar' }}
<!-- little arrow -->
<span class="absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-stone-900"></span>
</span>
</span>
{%- endmacro -%}

View File

@@ -0,0 +1,10 @@
{% macro title(_class='') %}
<a
href="{{ coop_url('/') }}"
class="{{_class}}"
>
<h1>
{{ site().title }}
</h1>
</a>
{% endmacro %}