From 9cdd2195df3b68fee2ec75f3fae0820d7f92cb61 Mon Sep 17 00:00:00 2001 From: giles Date: Sun, 22 Feb 2026 19:26:48 +0000 Subject: [PATCH] Restore all 33 deleted shared templates Templates were incorrectly identified as dead code because individual apps override them, but other apps still depend on the shared versions. Co-Authored-By: Claude Opus 4.6 --- .../_types/blog/_action_buttons.html | 51 +++ .../templates/_types/blog/_main_panel.html | 48 +++ .../_types/browse/_oob_elements.html | 37 ++ .../_types/calendar/_main_panel.html | 180 ++++++++++ .../_types/calendar/admin/_description.html | 33 ++ .../calendar/admin/_description_edit.html | 43 +++ .../_types/calendar/admin/_main_panel.html | 46 +++ .../_types/calendar/admin/header/_header.html | 14 + .../_types/calendar/header/_header.html | 23 ++ browser/templates/_types/calendar/index.html | 20 ++ browser/templates/_types/day/_add.html | 301 ++++++++++++++++ browser/templates/_types/day/_add_button.html | 17 + browser/templates/_types/day/_nav.html | 50 +++ browser/templates/_types/day/_row.html | 76 ++++ .../_types/day/admin/_nav_entries_oob.html | 34 ++ .../_types/day/admin/header/_header.html | 21 ++ .../templates/_types/day/header/_header.html | 27 ++ browser/templates/_types/day/index.html | 18 + browser/templates/_types/entry/_edit.html | 334 ++++++++++++++++++ .../templates/_types/entry/_main_panel.html | 126 +++++++ browser/templates/_types/entry/_nav.html | 48 +++ browser/templates/_types/entry/_options.html | 98 +++++ browser/templates/_types/entry/_posts.html | 74 ++++ browser/templates/_types/entry/_tickets.html | 105 ++++++ .../templates/_types/entry/admin/_nav.html | 18 + .../_types/entry/admin/header/_header.html | 22 ++ .../_types/entry/header/_header.html | 28 ++ browser/templates/_types/entry/index.html | 20 ++ .../_types/market/_oob_elements.html | 30 ++ browser/templates/_types/market/index.html | 25 ++ browser/templates/_types/post/_meta.html | 128 +++++++ browser/templates/_types/post/_nav.html | 8 + browser/templates/_types/post/admin/_nav.html | 18 + .../_types/post/admin/_oob_elements.html | 22 ++ .../templates/_types/post/admin/index.html | 18 + .../templates/_types/post/header/_header.html | 19 + .../_types/post_entries/_main_panel.html | 48 +++ .../_types/post_entries/header/_header.html | 17 + browser/templates/_types/product/_cart.html | 278 +++++++++++++++ 39 files changed, 2523 insertions(+) create mode 100644 browser/templates/_types/blog/_action_buttons.html create mode 100644 browser/templates/_types/blog/_main_panel.html create mode 100644 browser/templates/_types/browse/_oob_elements.html create mode 100644 browser/templates/_types/calendar/_main_panel.html create mode 100644 browser/templates/_types/calendar/admin/_description.html create mode 100644 browser/templates/_types/calendar/admin/_description_edit.html create mode 100644 browser/templates/_types/calendar/admin/_main_panel.html create mode 100644 browser/templates/_types/calendar/admin/header/_header.html create mode 100644 browser/templates/_types/calendar/header/_header.html create mode 100644 browser/templates/_types/calendar/index.html create mode 100644 browser/templates/_types/day/_add.html create mode 100644 browser/templates/_types/day/_add_button.html create mode 100644 browser/templates/_types/day/_nav.html create mode 100644 browser/templates/_types/day/_row.html create mode 100644 browser/templates/_types/day/admin/_nav_entries_oob.html create mode 100644 browser/templates/_types/day/admin/header/_header.html create mode 100644 browser/templates/_types/day/header/_header.html create mode 100644 browser/templates/_types/day/index.html create mode 100644 browser/templates/_types/entry/_edit.html create mode 100644 browser/templates/_types/entry/_main_panel.html create mode 100644 browser/templates/_types/entry/_nav.html create mode 100644 browser/templates/_types/entry/_options.html create mode 100644 browser/templates/_types/entry/_posts.html create mode 100644 browser/templates/_types/entry/_tickets.html create mode 100644 browser/templates/_types/entry/admin/_nav.html create mode 100644 browser/templates/_types/entry/admin/header/_header.html create mode 100644 browser/templates/_types/entry/header/_header.html create mode 100644 browser/templates/_types/entry/index.html create mode 100644 browser/templates/_types/market/_oob_elements.html create mode 100644 browser/templates/_types/market/index.html create mode 100644 browser/templates/_types/post/_meta.html create mode 100644 browser/templates/_types/post/_nav.html create mode 100644 browser/templates/_types/post/admin/_nav.html create mode 100644 browser/templates/_types/post/admin/_oob_elements.html create mode 100644 browser/templates/_types/post/admin/index.html create mode 100644 browser/templates/_types/post/header/_header.html create mode 100644 browser/templates/_types/post_entries/_main_panel.html create mode 100644 browser/templates/_types/post_entries/header/_header.html create mode 100644 browser/templates/_types/product/_cart.html diff --git a/browser/templates/_types/blog/_action_buttons.html b/browser/templates/_types/blog/_action_buttons.html new file mode 100644 index 0000000..0ea7fa2 --- /dev/null +++ b/browser/templates/_types/blog/_action_buttons.html @@ -0,0 +1,51 @@ +{# New Post + Drafts toggle — shown in aside (desktop + mobile) #} +
+ {% if has_access('blog.new_post') %} + {% set new_href = url_for('blog.new_post')|host %} + + New Post + + {% endif %} + {% if g.user and (draft_count or drafts) %} + {% if drafts %} + {% set drafts_off_href = (current_local_href ~ {'drafts': None}|qs)|host %} + + Drafts + {{ draft_count }} + + {% else %} + {% set drafts_on_href = (current_local_href ~ {'drafts': '1'}|qs)|host %} + + Drafts + {{ draft_count }} + + {% endif %} + {% endif %} +
diff --git a/browser/templates/_types/blog/_main_panel.html b/browser/templates/_types/blog/_main_panel.html new file mode 100644 index 0000000..350999d --- /dev/null +++ b/browser/templates/_types/blog/_main_panel.html @@ -0,0 +1,48 @@ + + {# View toggle bar - desktop only #} + + + {# Cards container - list or grid based on view #} + {% if view == 'tile' %} +
+ {% include "_types/blog/_cards.html" %} +
+ {% else %} +
+ {% include "_types/blog/_cards.html" %} +
+ {% endif %} +
diff --git a/browser/templates/_types/browse/_oob_elements.html b/browser/templates/_types/browse/_oob_elements.html new file mode 100644 index 0000000..d32fd78 --- /dev/null +++ b/browser/templates/_types/browse/_oob_elements.html @@ -0,0 +1,37 @@ +{% extends 'oob_elements.html' %} + +{# OOB elements for HTMX navigation - all elements that need updating #} + +{# Import shared OOB macros #} +{% from '_types/root/header/_oob.html' import root_header_start, root_header_end with context %} +{% from '_types/root/_oob_menu.html' import mobile_menu with context %} + +{# Header with app title - includes cart-mini, navigation, and market-specific header #} + +{% block oobs %} + + {% from '_types/root/_n/macros.html' import oob_header with context %} + {{oob_header('root-header-child', 'market-header-child', '_types/market/header/_header.html')}} + + {% from '_types/root/header/_header.html' import header_row with context %} + {{ header_row(oob=True) }} +{% endblock %} + + +{% block mobile_menu %} + {% include '_types/market/mobile/_nav_panel.html' %} +{% endblock %} + +{# Filter container with child summary - from browse/index.html child_summary block #} +{% block filter %} + {% include "_types/browse/mobile/_filter/summary.html" %} +{% endblock %} + +{% block aside %} + {% include "_types/browse/desktop/menu.html" %} +{% endblock %} + + +{% block content %} + {% include "_types/browse/_main_panel.html" %} +{% endblock %} diff --git a/browser/templates/_types/calendar/_main_panel.html b/browser/templates/_types/calendar/_main_panel.html new file mode 100644 index 0000000..48ae736 --- /dev/null +++ b/browser/templates/_types/calendar/_main_panel.html @@ -0,0 +1,180 @@ +
+
+ + {# Month / year navigation #} + +
+ + {# Calendar grid #} +
+ {# Weekday header: only show on sm+ (desktop/tablet) #} + + + {# On mobile: 1 column; on sm+: 7 columns #} +
+ {% for week in weeks %} + {% for day in week %} +
+
+
+ + {{ day.date.strftime('%a') }} + + + {# Clickable day number: goes to day detail view #} + + {{ day.date.day }} + +
+
+ {# Entries for this day: merged, chronological #} +
+ {# Build a list of entries for this specific day. + month_entries is already sorted by start_at in Python. #} + {% for e in month_entries %} + {% if e.start_at.date() == day.date %} + {# Decide colour: highlight "mine" differently if you want #} + {% set is_mine = (g.user and e.user_id == g.user.id) + or (not g.user and e.session_id == qsession.get('calendar_sid')) %} +
+ + {{ e.name }} + + + {{ (e.state or 'pending')|replace('_', ' ') }} + +
+ {% endif %} + {% endfor %} + +
+
+ {% endfor %} + {% endfor %} +
+
diff --git a/browser/templates/_types/calendar/admin/_description.html b/browser/templates/_types/calendar/admin/_description.html new file mode 100644 index 0000000..7fc96f0 --- /dev/null +++ b/browser/templates/_types/calendar/admin/_description.html @@ -0,0 +1,33 @@ +
+ {% if calendar.description %} +

+ {{ calendar.description }} +

+ {% else %} +

+ No description yet. +

+ {% endif %} + + +
+ +{% if oob %} + + {% from '_types/calendar/_description.html' import description %} + {{description(calendar, oob=True)}} +{% endif %} + + diff --git a/browser/templates/_types/calendar/admin/_description_edit.html b/browser/templates/_types/calendar/admin/_description_edit.html new file mode 100644 index 0000000..61c5ff0 --- /dev/null +++ b/browser/templates/_types/calendar/admin/_description_edit.html @@ -0,0 +1,43 @@ +
+
+ + + + +
+ + + +
+
+
diff --git a/browser/templates/_types/calendar/admin/_main_panel.html b/browser/templates/_types/calendar/admin/_main_panel.html new file mode 100644 index 0000000..9696f47 --- /dev/null +++ b/browser/templates/_types/calendar/admin/_main_panel.html @@ -0,0 +1,46 @@ + +
+ +
+

Calendar configuration

+
+
+ + {% include '_types/calendar/admin/_description.html' %} +
+ + +
+ +
+ +
diff --git a/browser/templates/_types/calendar/admin/header/_header.html b/browser/templates/_types/calendar/admin/header/_header.html new file mode 100644 index 0000000..d383373 --- /dev/null +++ b/browser/templates/_types/calendar/admin/header/_header.html @@ -0,0 +1,14 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='calendar-admin-row', oob=oob) %} + {% call links.link( + url_for('calendars.calendar.admin.admin', slug=post.slug, calendar_slug=calendar.slug), + hx_select_search + ) %} + {{ links.admin() }} + {% endcall %} + {% call links.desktop_nav() %} + {% include '_types/calendar/admin/_nav.html' %} + {% endcall %} + {% endcall %} +{% endmacro %} \ No newline at end of file diff --git a/browser/templates/_types/calendar/header/_header.html b/browser/templates/_types/calendar/header/_header.html new file mode 100644 index 0000000..73fe115 --- /dev/null +++ b/browser/templates/_types/calendar/header/_header.html @@ -0,0 +1,23 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='calendar-row', oob=oob) %} + +
+
+ +
+ {{ calendar.name }} +
+
+ {% from '_types/calendar/_description.html' import description %} + {{description(calendar)}} +
+
+ {% call links.desktop_nav() %} + {% include '_types/calendar/_nav.html' %} + {% endcall %} + {% endcall %} +{% endmacro %} + + + diff --git a/browser/templates/_types/calendar/index.html b/browser/templates/_types/calendar/index.html new file mode 100644 index 0000000..802c45c --- /dev/null +++ b/browser/templates/_types/calendar/index.html @@ -0,0 +1,20 @@ +{% extends '_types/post/index.html' %} + +{% block post_header_child %} + {% from '_types/root/_n/macros.html' import index_row with context %} + {% call index_row('calendar-header-child', '_types/calendar/header/_header.html') %} + {% block calendar_header_child %} + {% endblock %} + {% endcall %} +{% endblock %} + + +{% block _main_mobile_menu %} + {% include '_types/calendar/_nav.html' %} +{% endblock %} + + + +{% block content %} + {% include '_types/calendar/_main_panel.html' %} +{% endblock %} diff --git a/browser/templates/_types/day/_add.html b/browser/templates/_types/day/_add.html new file mode 100644 index 0000000..df02331 --- /dev/null +++ b/browser/templates/_types/day/_add.html @@ -0,0 +1,301 @@ +
+ +
+ + + {# 1) Entry name #} + + + {# 2) Slot picker for this weekday (required) #} + {% if day_slots %} + + {% else %} +
+ No slots defined for this day. +
+ {% endif %} + + {# 3) Time entry + cost display #} +
+ {# Time inputs — hidden until a flexible slot is selected #} + + + {# Cost display — shown when a slot is selected #} + + + {# Summary of fixed times — shown for non-flexible slots #} + +
+ + {# Ticket Configuration #} +
+

Ticket Configuration (Optional)

+
+
+ + +
+
+ + +
+
+
+ +
+ + + +
+
+ +{# --- Behaviour: lock / unlock times based on slot.flexible --- #} + \ No newline at end of file diff --git a/browser/templates/_types/day/_add_button.html b/browser/templates/_types/day/_add_button.html new file mode 100644 index 0000000..46726e5 --- /dev/null +++ b/browser/templates/_types/day/_add_button.html @@ -0,0 +1,17 @@ + + diff --git a/browser/templates/_types/day/_nav.html b/browser/templates/_types/day/_nav.html new file mode 100644 index 0000000..cfb8aca --- /dev/null +++ b/browser/templates/_types/day/_nav.html @@ -0,0 +1,50 @@ +{% import 'macros/links.html' as links %} + +{# Confirmed Entries - vertical on mobile, horizontal with arrows on desktop #} +
+ {% from 'macros/scrolling_menu.html' import scrolling_menu with context %} + {% call(entry) scrolling_menu('day-entries-container', confirmed_entries) %} + +
+
{{ entry.name }}
+
+ {{ entry.start_at.strftime('%H:%M') }} + {% if entry.end_at %} – {{ entry.end_at.strftime('%H:%M') }}{% endif %} +
+
+
+ {% endcall %} +
+ +{# Container nav widgets (market links, etc.) #} +{% if container_nav_widgets %} + {% for wdata in container_nav_widgets %} + {% with ctx=wdata.ctx %} + {% include wdata.widget.template with context %} + {% endwith %} + {% endfor %} +{% endif %} + +{# Admin link #} +{% if g.rights.admin %} + {% from 'macros/admin_nav.html' import admin_nav_item %} + {{admin_nav_item( + url_for( + 'calendars.calendar.day.admin.admin', + slug=post.slug, + calendar_slug=calendar.slug, + year=day_date.year, + month=day_date.month, + day=day_date.day + ) + )}} +{% endif %} \ No newline at end of file diff --git a/browser/templates/_types/day/_row.html b/browser/templates/_types/day/_row.html new file mode 100644 index 0000000..87aead7 --- /dev/null +++ b/browser/templates/_types/day/_row.html @@ -0,0 +1,76 @@ +{% import 'macros/links.html' as links %} + + +
+ {% call links.link( + url_for( + 'calendars.calendar.day.calendar_entries.calendar_entry.get', + slug=post.slug, + calendar_slug=calendar.slug, + day=day, + month=month, + year=year, + entry_id=entry.id + ), + hx_select_search, + aclass=styles.pill + ) %} + {{ entry.name }} + {% endcall %} +
+ + + {% if entry.slot %} +
+ {% call links.link( + url_for( + 'calendars.calendar.slots.slot.get', + slug=post.slug, + calendar_slug=calendar.slug, + slot_id=entry.slot.id + ), + hx_select_search, + aclass=styles.pill + ) %} + {{ entry.slot.name }} + {% endcall %} + + ({{ entry.slot.time_start.strftime('%H:%M') }}{% if entry.slot.time_end %} → {{ entry.slot.time_end.strftime('%H:%M') }}{% endif %}) + +
+ {% else %} +
+ {% include '_types/entry/_times.html' %} +
+ {% endif %} + + +
+ {% include '_types/entry/_state.html' %} +
+ + + + £{{ ('%.2f'|format(entry.cost)) if entry.cost is not none else '0.00' }} + + + + {% if entry.ticket_price is not none %} +
+
£{{ ('%.2f'|format(entry.ticket_price)) }}
+
+ {% if entry.ticket_count is not none %} + {{ entry.ticket_count }} tickets + {% else %} + Unlimited + {% endif %} +
+
+ {% else %} + No tickets + {% endif %} + + + {% include '_types/entry/_options.html' %} + + \ No newline at end of file diff --git a/browser/templates/_types/day/admin/_nav_entries_oob.html b/browser/templates/_types/day/admin/_nav_entries_oob.html new file mode 100644 index 0000000..d72fc90 --- /dev/null +++ b/browser/templates/_types/day/admin/_nav_entries_oob.html @@ -0,0 +1,34 @@ +{# OOB swap for day confirmed entries nav when entries are edited #} +{% import 'macros/links.html' as links %} + +{# Confirmed Entries - vertical on mobile, horizontal with arrows on desktop #} +{% if confirmed_entries %} +
+ {% from 'macros/scrolling_menu.html' import scrolling_menu with context %} + {% call(entry) scrolling_menu('day-entries-container', confirmed_entries) %} + +
+
{{ entry.name }}
+
+ {{ entry.start_at.strftime('%H:%M') }} + {% if entry.end_at %} – {{ entry.end_at.strftime('%H:%M') }}{% endif %} +
+
+
+ {% endcall %} +
+{% else %} + {# Empty placeholder to remove nav entries when none are confirmed #} +
+{% endif %} diff --git a/browser/templates/_types/day/admin/header/_header.html b/browser/templates/_types/day/admin/header/_header.html new file mode 100644 index 0000000..b5f583c --- /dev/null +++ b/browser/templates/_types/day/admin/header/_header.html @@ -0,0 +1,21 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='day-admin-row', oob=oob) %} + {% call links.link( + url_for( + 'calendars.calendar.day.admin.admin', + slug=post.slug, + calendar_slug=calendar.slug, + year=day_date.year, + month=day_date.month, + day=day_date.day + ), + hx_select_search + ) %} + {{ links.admin() }} + {% endcall %} + {% call links.desktop_nav() %} + {% include '_types/day/admin/_nav.html' %} + {% endcall %} + {% endcall %} +{% endmacro %} \ No newline at end of file diff --git a/browser/templates/_types/day/header/_header.html b/browser/templates/_types/day/header/_header.html new file mode 100644 index 0000000..e53a815 --- /dev/null +++ b/browser/templates/_types/day/header/_header.html @@ -0,0 +1,27 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='day-row', oob=oob) %} + {% call links.link( + url_for( + 'calendars.calendar.day.show_day', + slug=post.slug, + calendar_slug=calendar.slug, + year=day_date.year, + month=day_date.month, + day=day_date.day + ), + hx_select_search, + ) %} +
+ + {{ day_date.strftime('%A %d %B %Y') }} +
+ {% endcall %} + {% call links.desktop_nav() %} + {% include '_types/day/_nav.html' %} + {% endcall %} + {% endcall %} +{% endmacro %} + + + diff --git a/browser/templates/_types/day/index.html b/browser/templates/_types/day/index.html new file mode 100644 index 0000000..655ee55 --- /dev/null +++ b/browser/templates/_types/day/index.html @@ -0,0 +1,18 @@ +{% extends '_types/calendar/index.html' %} + +{% block calendar_header_child %} + {% from '_types/root/_n/macros.html' import index_row with context %} + {% call index_row('day-header-child', '_types/day/header/_header.html') %} + {% block day_header_child %} + {% endblock %} + {% endcall %} +{% endblock %} + +{% block _main_mobile_menu %} + {% include '_types/day/_nav.html' %} +{% endblock %} + + +{% block content %} + {% include '_types/day/_main_panel.html' %} +{% endblock %} diff --git a/browser/templates/_types/entry/_edit.html b/browser/templates/_types/entry/_edit.html new file mode 100644 index 0000000..628f239 --- /dev/null +++ b/browser/templates/_types/entry/_edit.html @@ -0,0 +1,334 @@ +
+ + +
+ +
+ + + + +
+ + +
+ + +
+ + {% if day_slots %} + + {% else %} +
+ No slots defined for this day. +
+ {% endif %} +
+ + + + + + + + + + + +
+

Ticket Configuration

+ +
+
+ + +

Leave empty if no tickets needed

+
+ +
+ + +

Leave empty for unlimited

+
+
+
+ +
+ + + + + + + +
+ +
+
+ +{# --- Behaviour: lock / unlock times based on slot.flexible --- #} + \ No newline at end of file diff --git a/browser/templates/_types/entry/_main_panel.html b/browser/templates/_types/entry/_main_panel.html new file mode 100644 index 0000000..403605b --- /dev/null +++ b/browser/templates/_types/entry/_main_panel.html @@ -0,0 +1,126 @@ +
+ + +
+
+ Name +
+
+ {{ entry.name }} +
+
+ + +
+
+ Slot +
+
+ {% if entry.slot %} + + {{ entry.slot.name }} + + {% if entry.slot.flexible %} + (flexible) + {% else %} + (fixed) + {% endif %} + {% else %} + No slot assigned + {% endif %} +
+
+ + +
+
+ Time Period +
+
+ {{ entry.start_at.strftime('%H:%M') }} + {% if entry.end_at %} + – {{ entry.end_at.strftime('%H:%M') }} + {% else %} + – open-ended + {% endif %} +
+
+ + +
+
+ State +
+
+
+ {% include '_types/entry/_state.html' %} +
+
+
+ + +
+
+ Cost +
+
+ + £{{ ('%.2f'|format(entry.cost)) if entry.cost is not none else '0.00' }} + +
+
+ + +
+
+ Tickets +
+
+ {% include '_types/entry/_tickets.html' %} +
+
+ + +
+
+ Date +
+
+ {{ entry.start_at.strftime('%A, %B %d, %Y') }} +
+
+ + +
+
+ Associated Posts +
+
+ {% include '_types/entry/_posts.html' %} +
+
+ + +
+ {% include '_types/entry/_options.html' %} + + +
+ +
\ No newline at end of file diff --git a/browser/templates/_types/entry/_nav.html b/browser/templates/_types/entry/_nav.html new file mode 100644 index 0000000..388fc2e --- /dev/null +++ b/browser/templates/_types/entry/_nav.html @@ -0,0 +1,48 @@ +{% import 'macros/links.html' as links %} + +{# Associated Posts - vertical on mobile, horizontal with arrows on desktop #} +
+ {% from 'macros/scrolling_menu.html' import scrolling_menu with context %} + {% call(entry_post) scrolling_menu('entry-posts-container', entry_posts) %} + + {% if entry_post.feature_image %} + {{ entry_post.title }} + {% else %} +
+ {% endif %} +
+
{{ entry_post.title }}
+
+
+ {% endcall %} +
+ +{% if container_nav_widgets %} + {% for wdata in container_nav_widgets %} + {% with ctx=wdata.ctx %} + {% include wdata.widget.template with context %} + {% endwith %} + {% endfor %} +{% endif %} + +{# Admin link #} +{% if g.rights.admin %} + + {% from 'macros/admin_nav.html' import admin_nav_item %} + {{admin_nav_item( + url_for( + 'calendars.calendar.day.calendar_entries.calendar_entry.admin.admin', + slug=post.slug, + calendar_slug=calendar.slug, + day=day, + month=month, + year=year, + entry_id=entry.id + ) + )}} +{% endif %} diff --git a/browser/templates/_types/entry/_options.html b/browser/templates/_types/entry/_options.html new file mode 100644 index 0000000..6c42952 --- /dev/null +++ b/browser/templates/_types/entry/_options.html @@ -0,0 +1,98 @@ +
+ {% if entry.state == 'provisional' %} +
+ + +
+
+ + +
+ {% endif %} + {% if entry.state == 'confirmed' %} +
+ + + +
+ {% endif %} +
\ No newline at end of file diff --git a/browser/templates/_types/entry/_posts.html b/browser/templates/_types/entry/_posts.html new file mode 100644 index 0000000..9a88936 --- /dev/null +++ b/browser/templates/_types/entry/_posts.html @@ -0,0 +1,74 @@ + +
+ {% if entry_posts %} +
+ {% for entry_post in entry_posts %} +
+ {% if entry_post.feature_image %} + {{ entry_post.title }} + {% else %} +
+ {% endif %} + {{ entry_post.title }} + +
+ {% endfor %} +
+ {% else %} +

No posts associated

+ {% endif %} + + +
+ + +
+
+
diff --git a/browser/templates/_types/entry/_tickets.html b/browser/templates/_types/entry/_tickets.html new file mode 100644 index 0000000..ee5dc38 --- /dev/null +++ b/browser/templates/_types/entry/_tickets.html @@ -0,0 +1,105 @@ +{% if entry.ticket_price is not none %} + {# Tickets are configured #} +
+
+ Price: + + £{{ ('%.2f'|format(entry.ticket_price)) }} + +
+
+ Available: + + {% if entry.ticket_count is not none %} + {{ entry.ticket_count }} tickets + {% else %} + Unlimited + {% endif %} + +
+ +
+{% else %} + {# No tickets configured #} +
+ No tickets configured + +
+{% endif %} + +{# Ticket configuration form (hidden by default) #} +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
diff --git a/browser/templates/_types/entry/admin/_nav.html b/browser/templates/_types/entry/admin/_nav.html new file mode 100644 index 0000000..ff658d3 --- /dev/null +++ b/browser/templates/_types/entry/admin/_nav.html @@ -0,0 +1,18 @@ +{% import 'macros/links.html' as links %} +{% call links.link( + url_for( + 'calendars.calendar.day.calendar_entries.calendar_entry.ticket_types.get', + slug=post.slug, + calendar_slug=calendar.slug, + entry_id=entry.id, + year=year, + month=month, + day=day + ), + hx_select_search, + select_colours, + True, + aclass=styles.nav_button, +)%} + ticket_types +{% endcall %} diff --git a/browser/templates/_types/entry/admin/header/_header.html b/browser/templates/_types/entry/admin/header/_header.html new file mode 100644 index 0000000..ea06833 --- /dev/null +++ b/browser/templates/_types/entry/admin/header/_header.html @@ -0,0 +1,22 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='entry-admin-row', oob=oob) %} + {% call links.link( + url_for( + 'calendars.calendar.day.calendar_entries.calendar_entry.admin.admin', + slug=post.slug, + calendar_slug=calendar.slug, + day=day, + month=month, + year=year, + entry_id=entry.id + ), + hx_select_search + ) %} + {{ links.admin() }} + {% endcall %} + {% call links.desktop_nav() %} + {% include '_types/entry/admin/_nav.html' %} + {% endcall %} + {% endcall %} +{% endmacro %} \ No newline at end of file diff --git a/browser/templates/_types/entry/header/_header.html b/browser/templates/_types/entry/header/_header.html new file mode 100644 index 0000000..6d0680c --- /dev/null +++ b/browser/templates/_types/entry/header/_header.html @@ -0,0 +1,28 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='entry-row', oob=oob) %} + {% call links.link( + url_for( + 'calendars.calendar.day.calendar_entries.calendar_entry.get', + slug=post.slug, + calendar_slug=calendar.slug, + day=day, + month=month, + year=year, + entry_id=entry.id + ), + hx_select_search, + ) %} +
+ {% include '_types/entry/_title.html' %} + {% include '_types/entry/_times.html' %} +
+ {% endcall %} + {% call links.desktop_nav() %} + {% include '_types/entry/_nav.html' %} + {% endcall %} + {% endcall %} +{% endmacro %} + + + diff --git a/browser/templates/_types/entry/index.html b/browser/templates/_types/entry/index.html new file mode 100644 index 0000000..a980f46 --- /dev/null +++ b/browser/templates/_types/entry/index.html @@ -0,0 +1,20 @@ +{% extends '_types/day/index.html' %} + +{% block day_header_child %} + {% from '_types/root/_n/macros.html' import index_row with context %} + {% call index_row('entry-header-child', '_types/entry/header/_header.html') %} + {% block entry_header_child %} + {% endblock %} + {% endcall %} +{% endblock %} + + +{% block _main_mobile_menu %} + {% include '_types/entry/_nav.html' %} +{% endblock %} + + + +{% block content %} +{% include '_types/entry/_main_panel.html' %} +{% endblock %} \ No newline at end of file diff --git a/browser/templates/_types/market/_oob_elements.html b/browser/templates/_types/market/_oob_elements.html new file mode 100644 index 0000000..b37eea0 --- /dev/null +++ b/browser/templates/_types/market/_oob_elements.html @@ -0,0 +1,30 @@ +{% extends 'oob_elements.html' %} + +{# OOB elements for HTMX navigation - all elements that need updating #} + +{# Import shared OOB macros #} +{% from '_types/root/header/_oob.html' import root_header_start, root_header_end with context %} +{% from '_types/root/_oob_menu.html' import mobile_menu with context %} + +{# Header with app title - includes cart-mini, navigation, and market-specific header #} + +{% block oobs %} + + {% from '_types/root/_n/macros.html' import oob_header with context %} + {{oob_header('root-header-child', 'market-header-child', '_types/market/header/_header.html')}} + + {% from '_types/root/header/_header.html' import header_row with context %} + {{ header_row(oob=True) }} +{% endblock %} + + +{% block mobile_menu %} + {% include '_types/market/mobile/_nav_panel.html' %} +{% endblock %} + + +{% block content %} + {% include "_types/market/_main_panel.html" %} +{% endblock %} + + diff --git a/browser/templates/_types/market/index.html b/browser/templates/_types/market/index.html new file mode 100644 index 0000000..df1ec4c --- /dev/null +++ b/browser/templates/_types/market/index.html @@ -0,0 +1,25 @@ +{% extends '_types/root/_index.html' %} + + +{% block root_header_child %} + {% from '_types/root/_n/macros.html' import index_row with context %} + {% call index_row('market-header-child', '_types/market/header/_header.html') %} + {% block market_header_child %} + {% endblock %} + {% endcall %} +{% endblock %} + + +{% block _main_mobile_menu %} + {% include '_types/market/mobile/_nav_panel.html' %} +{% endblock %} + + + +{% block aside %} +{# No aside on landing page #} +{% endblock %} + +{% block content %} + {% include "_types/market/_main_panel.html" %} +{% endblock %} diff --git a/browser/templates/_types/post/_meta.html b/browser/templates/_types/post/_meta.html new file mode 100644 index 0000000..fc752d4 --- /dev/null +++ b/browser/templates/_types/post/_meta.html @@ -0,0 +1,128 @@ +{# --- social/meta_post.html --- #} +{# Context expected: + site, post, request +#} +{% if post is not defined %} + {% include 'social/meta_base.html' %} +{% else %} + +{# Visibility → robots #} +{% set is_public = (post.visibility == 'public') %} +{% set is_published = (post.status == 'published') %} +{% set robots_here = 'index,follow' if (is_public and is_published and not post.email_only) else 'noindex,nofollow' %} + +{# Compute canonical early so both this file and base can use it #} +{% set _site_url = site().url.rstrip('/') if site and site().url else '' %} +{% set _post_path = request.path if request else ('/posts/' ~ (post.slug or post.uuid)) %} +{% set canonical = post.canonical_url or (_site_url ~ _post_path if _site_url else (request.url if request else None)) %} + +{# Include common base (charset, viewport, robots default, RSS, Org/WebSite JSON-LD) #} +{% set robots_override = robots_here %} +{% include 'social/meta_base.html' %} + +{# ---- Titles / descriptions ---- #} +{% set og_title = post.og_title or base_title %} +{% set tw_title = post.twitter_title or base_title %} + +{# Description best-effort, trimmed #} +{% set desc_source = post.meta_description + or post.og_description + or post.twitter_description + or post.custom_excerpt + or post.excerpt + or (post.plaintext if post.plaintext else (post.html|striptags if post.html else '')) %} +{% set description = (desc_source|trim|replace('\n',' ')|replace('\r',' ')|striptags)|truncate(160, True, '…') %} + +{# Image priority #} +{% set image_url = post.og_image + or post.twitter_image + or post.feature_image + or (site().default_image if site and site().default_image else None) %} + +{# Dates #} +{% set published_iso = post.published_at.isoformat() if post.published_at else None %} +{% set updated_iso = post.updated_at.isoformat() if post.updated_at + else (post.created_at.isoformat() if post.created_at else None) %} + +{# Authors / tags #} +{% set primary_author = post.primary_author %} +{% set authors = post.authors or ([primary_author] if primary_author else []) %} +{% set tag_names = (post.tags or []) | map(attribute='name') | list %} +{% set is_article = not post.is_page %} + +{{ base_title }} + +{% if canonical %}{% endif %} + +{# ---- Open Graph ---- #} + + + + +{% if canonical %}{% endif %} +{% if image_url %}{% endif %} +{% if is_article and published_iso %}{% endif %} +{% if is_article and updated_iso %} + + +{% endif %} +{% if is_article and post.primary_tag and post.primary_tag.name %} + +{% endif %} +{% if is_article %} + {% for t in tag_names %} + + {% endfor %} +{% endif %} + +{# ---- Twitter ---- #} + +{% if site and site().twitter_site %}{% endif %} +{% if primary_author and primary_author.twitter %} + +{% endif %} + + +{% if image_url %}{% endif %} + +{# ---- JSON-LD author value (no list comprehensions) ---- #} +{% if authors and authors|length == 1 %} + {% set author_value = {"@type": "Person", "name": authors[0].name} %} +{% elif authors %} + {% set ns = namespace(arr=[]) %} + {% for a in authors %} + {% set _ = ns.arr.append({"@type": "Person", "name": a.name}) %} + {% endfor %} + {% set author_value = ns.arr %} +{% else %} + {% set author_value = none %} +{% endif %} + +{# ---- JSON-LD using combine for optionals ---- #} +{% set jsonld = { + "@context": "https://schema.org", + "@type": "BlogPosting" if is_article else "WebPage", + "mainEntityOfPage": canonical, + "headline": base_title, + "description": description, + "image": image_url, + "datePublished": published_iso, + "author": author_value, + "publisher": { + "@type": "Organization", + "name": site().title if site and site().title else "", + "logo": {"@type": "ImageObject", "url": site().logo if site and site().logo else image_url} + } +} %} + +{% if updated_iso %} + {% set jsonld = jsonld | combine({"dateModified": updated_iso}) %} +{% endif %} +{% if tag_names %} + {% set jsonld = jsonld | combine({"keywords": tag_names | join(", ")}) %} +{% endif %} + + +{% endif %} diff --git a/browser/templates/_types/post/_nav.html b/browser/templates/_types/post/_nav.html new file mode 100644 index 0000000..db8cdc4 --- /dev/null +++ b/browser/templates/_types/post/_nav.html @@ -0,0 +1,8 @@ +{% import 'macros/links.html' as links %} + {# Widget-driven container nav — entries, calendars, markets #} + {% if container_nav_widgets %} +
+ {% include '_types/post/admin/_nav_entries.html' %} +
+ {% endif %} diff --git a/browser/templates/_types/post/admin/_nav.html b/browser/templates/_types/post/admin/_nav.html new file mode 100644 index 0000000..7296d15 --- /dev/null +++ b/browser/templates/_types/post/admin/_nav.html @@ -0,0 +1,18 @@ +{% import 'macros/links.html' as links %} + +{% call links.link(url_for('blog.post.admin.entries', slug=post.slug), hx_select_search, select_colours, True, aclass=styles.nav_button) %} + entries +{% endcall %} +{% call links.link(url_for('blog.post.admin.data', slug=post.slug), hx_select_search, select_colours, True, aclass=styles.nav_button) %} + data +{% endcall %} +{% call links.link(url_for('blog.post.admin.edit', slug=post.slug), hx_select_search, select_colours, True, aclass=styles.nav_button) %} + edit +{% endcall %} +{% call links.link(url_for('blog.post.admin.settings', slug=post.slug), hx_select_search, select_colours, True, aclass=styles.nav_button) %} + settings +{% endcall %} \ No newline at end of file diff --git a/browser/templates/_types/post/admin/_oob_elements.html b/browser/templates/_types/post/admin/_oob_elements.html new file mode 100644 index 0000000..4bd3b74 --- /dev/null +++ b/browser/templates/_types/post/admin/_oob_elements.html @@ -0,0 +1,22 @@ +{% extends "oob_elements.html" %} +{# OOB elements for post admin page #} + +{# Import shared OOB macros #} +{% from '_types/root/header/_oob.html' import root_header_start, root_header_end with context %} +{% from '_types/root/_oob_menu.html' import mobile_menu with context %} + +{% block oobs %} + {% from '_types/root/_n/macros.html' import oob_header with context %} + {{oob_header('post-header-child', 'post-admin-header-child', '_types/post/admin/header/_header.html')}} + + {% from '_types/post/header/_header.html' import header_row with context %} + {{ header_row(oob=True) }} +{% endblock %} + +{% block mobile_menu %} + {% include '_types/post/admin/_nav.html' %} +{% endblock %} + +{% block content %} +nowt +{% endblock %} \ No newline at end of file diff --git a/browser/templates/_types/post/admin/index.html b/browser/templates/_types/post/admin/index.html new file mode 100644 index 0000000..fb1de5f --- /dev/null +++ b/browser/templates/_types/post/admin/index.html @@ -0,0 +1,18 @@ +{% extends '_types/post/index.html' %} +{% import 'macros/layout.html' as layout %} + +{% block post_header_child %} + {% from '_types/root/_n/macros.html' import index_row with context %} + {% call index_row('post-admin-header-child', '_types/post/admin/header/_header.html') %} + {% block post_admin_header_child %} + {% endblock %} + {% endcall %} +{% endblock %} + +{% block _main_mobile_menu %} + {% include '_types/post/admin/_nav.html' %} +{% endblock %} + +{% block content %} +nowt +{% endblock %} diff --git a/browser/templates/_types/post/header/_header.html b/browser/templates/_types/post/header/_header.html new file mode 100644 index 0000000..a75eda3 --- /dev/null +++ b/browser/templates/_types/post/header/_header.html @@ -0,0 +1,19 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='post-row', oob=oob) %} + + {% if post.feature_image %} + + {% endif %} + + {{ post.title | truncate(160, True, '…') }} + + + {% call links.desktop_nav() %} + {% include '_types/post/_nav.html' %} + {% endcall %} + {% endcall %} +{% endmacro %} \ No newline at end of file diff --git a/browser/templates/_types/post_entries/_main_panel.html b/browser/templates/_types/post_entries/_main_panel.html new file mode 100644 index 0000000..342041e --- /dev/null +++ b/browser/templates/_types/post_entries/_main_panel.html @@ -0,0 +1,48 @@ +
+ + {# Associated Entries List #} + {% include '_types/post/admin/_associated_entries.html' %} + + {# Calendars Browser #} +
+

Browse Calendars

+ {% for calendar in all_calendars %} +
+ + {% if calendar.post.feature_image %} + {{ calendar.post.title }} + {% else %} +
+ {% endif %} +
+
+ + {{ calendar.name }} +
+
+ {{ calendar.post.title }} +
+
+
+
+
Loading calendar...
+
+
+ {% else %} +
No calendars found.
+ {% endfor %} +
+
\ No newline at end of file diff --git a/browser/templates/_types/post_entries/header/_header.html b/browser/templates/_types/post_entries/header/_header.html new file mode 100644 index 0000000..019c000 --- /dev/null +++ b/browser/templates/_types/post_entries/header/_header.html @@ -0,0 +1,17 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='post_entries-row', oob=oob) %} + {% call links.link(url_for('blog.post.admin.entries', slug=post.slug), hx_select_search) %} + +
+ entries +
+ {% endcall %} + {% call links.desktop_nav() %} + {% include '_types/post_entries/_nav.html' %} + {% endcall %} + {% endcall %} +{% endmacro %} + + + diff --git a/browser/templates/_types/product/_cart.html b/browser/templates/_types/product/_cart.html new file mode 100644 index 0000000..4947d86 --- /dev/null +++ b/browser/templates/_types/product/_cart.html @@ -0,0 +1,278 @@ +{% macro add(slug, cart, oob='false') %} +{% set quantity = cart + | selectattr('product.slug', 'equalto', slug) + | sum(attribute='quantity') %} + +
+ + {% if not quantity %} +
+ + + + +
+ + {% else %} +
+ +
+ + + +
+ + + + + + + + + {{ quantity }} + + + + + + +
+ + + +
+
+ {% endif %} +
+{% endmacro %} + + + +{% macro cart_item(oob=False) %} + +{% set p = item.product %} +{% set unit_price = p.special_price or p.regular_price %} +
+
+ {% if p.image %} + {{ p.title }} + {% else %} +
+ No image +
'market', 'product', p.slug + {% endif %} +
+ + {# Details #} +
+
+
+

+ {% set href=market_product_url(p.slug, market_place=item.market_place) %} + + {{ p.title }} + +

+ + {% if p.brand %} +

+ {{ p.brand }} +

+ {% endif %} + + {% if item.is_deleted %} +

+ + This item is no longer available or price has changed +

+ {% endif %} +
+ + {# Unit price #} +
+ {% if unit_price %} + {% set symbol = "£" if p.regular_price_currency == "GBP" else p.regular_price_currency %} +

+ {{ symbol }}{{ "%.2f"|format(unit_price) }} +

+ {% if p.special_price and p.special_price != p.regular_price %} +

+ {{ symbol }}{{ "%.2f"|format(p.regular_price) }} +

+ {% endif %} + {% else %} +

No price

+ {% endif %} +
+
+ +
+
+ Quantity + {% set qty_url = cart_quantity_url(item.product_id) if cart_quantity_url is defined else market_product_url(p.slug, 'cart', item.market_place) %} +
+ + + +
+ + {{ item.quantity }} + +
+ + + +
+ + {% if cart_delete_url is defined %} +
+ + +
+ {% endif %} +
+ +
+ {% if unit_price %} + {% set line_total = unit_price * item.quantity %} + {% set symbol = "£" if p.regular_price_currency == "GBP" else p.regular_price_currency %} +

+ Line total: + {{ symbol }}{{ "%.2f"|format(line_total) }} +

+ {% endif %} +
+
+
+
+ +{% endmacro %}