diff --git a/shared b/shared index 65c4989..322ae48 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 65c4989d08b85a821a0932e4376f2ab088f50d0c +Subproject commit 322ae481eeb47418245e669c3585f6d2195656da diff --git a/templates/_types/blog/_card.html b/templates/_types/blog/_card.html new file mode 100644 index 0000000..89ce8e7 --- /dev/null +++ b/templates/_types/blog/_card.html @@ -0,0 +1,80 @@ +{% import 'macros/stickers.html' as stick %} +
+ {# ❀️ like button - OUTSIDE the link, aligned with image top #} + {% if g.user %} +
+ {% set slug = post.slug %} + {% set liked = post.is_liked or False %} + {% set like_url = url_for('blog.post.like_toggle', slug=slug)|host %} + {% set item_type = 'post' %} + {% include "_types/browse/like/button.html" %} +
+ {% endif %} + + {% set _href=url_for('blog.post.post_detail', slug=post.slug)|host %} + +
+

+ {{ post.title }} +

+ + {% if post.status == "draft" %} +
+ Draft + {% if post.publish_requested %} + Publish requested + {% endif %} +
+ {% if post.updated_at %} +

+ Updated: {{ post.updated_at.strftime("%-d %b %Y at %H:%M") }} +

+ {% endif %} + {% elif post.published_at %} +

+ Published: {{ post.published_at.strftime("%-d %b %Y at %H:%M") }} +

+ {% endif %} + +
+ + {% if post.feature_image %} +
+ +
+ {% endif %} + {% if post.custom_excerpt %} +

+ {{ post.custom_excerpt }} +

+ {% else %} + {% if post.excerpt %} +

+ {{ post.excerpt }} +

+ {% endif %} + {% endif %} +
+ + {# Card decorations β€” via fragments #} + {% if card_widgets_html %} + {% set _card_html = card_widgets_html.get(post.id|string, "") %} + {% if _card_html %}{{ _card_html | safe }}{% endif %} + {% endif %} + + {% include '_types/blog/_card/at_bar.html' %} + +
diff --git a/templates/_types/blog/_card/at_bar.html b/templates/_types/blog/_card/at_bar.html new file mode 100644 index 0000000..f226d92 --- /dev/null +++ b/templates/_types/blog/_card/at_bar.html @@ -0,0 +1,19 @@ +
+ {% if post.tags %} +
+
in
+ +
+ {% endif %} +
+ {% if post.authors %} +
+
by
+ +
+ {% endif %} +
diff --git a/templates/_types/blog/_card/author.html b/templates/_types/blog/_card/author.html new file mode 100644 index 0000000..7ddddf7 --- /dev/null +++ b/templates/_types/blog/_card/author.html @@ -0,0 +1,21 @@ +{% macro author(author) %} + {% if author %} + {% if author.profile_image %} + {{ author.name }} + {% else %} +
+ {# optional fallback circle with first letter +
+ {{ author.name[:1] }} +
#} + {% endif %} + + + {{ author.name }} + + {% endif %} +{% endmacro %} \ No newline at end of file diff --git a/templates/_types/blog/_card/authors.html b/templates/_types/blog/_card/authors.html new file mode 100644 index 0000000..5b8911d --- /dev/null +++ b/templates/_types/blog/_card/authors.html @@ -0,0 +1,32 @@ +{# --- AUTHORS LIST STARTS HERE --- #} + {% if post.authors and post.authors|length %} + {% for a in post.authors %} + {% for author in authors if author.slug==a.slug %} +
  • + + {% if author.profile_image %} + {{ author.name }} + {% else %} + {# optional fallback circle with first letter #} +
    + {{ author.name[:1] }} +
    + {% endif %} + + + {{ author.name }} + +
    +
  • + {% endfor %} + {% endfor %} + {% endif %} + + {# --- AUTHOR LIST ENDS HERE --- #} \ No newline at end of file diff --git a/templates/_types/blog/_card/tag.html b/templates/_types/blog/_card/tag.html new file mode 100644 index 0000000..137cb0c --- /dev/null +++ b/templates/_types/blog/_card/tag.html @@ -0,0 +1,19 @@ +{% macro tag(tag) %} + {% if tag %} + {% if tag.feature_image %} + {{ tag.name }} + {% else %} +
    + {{ tag.name[:1] }} +
    + {% endif %} + + + {{ tag.name }} + + {% endif %} +{% endmacro %} \ No newline at end of file diff --git a/templates/_types/blog/_card/tag_group.html b/templates/_types/blog/_card/tag_group.html new file mode 100644 index 0000000..21c9974 --- /dev/null +++ b/templates/_types/blog/_card/tag_group.html @@ -0,0 +1,22 @@ +{% macro tag_group(group) %} + {% if group %} + {% if group.feature_image %} + {{ group.name }} + {% else %} +
    + {{ group.name[:1] }} +
    + {% endif %} + + + {{ group.name }} + + {% endif %} +{% endmacro %} diff --git a/templates/_types/blog/_card/tags.html b/templates/_types/blog/_card/tags.html new file mode 100644 index 0000000..2ea7ad1 --- /dev/null +++ b/templates/_types/blog/_card/tags.html @@ -0,0 +1,17 @@ +{% import '_types/blog/_card/tag.html' as dotag %} +{# --- TAG LIST STARTS HERE --- #} + {% if post.tags and post.tags|length %} + {% for t in post.tags %} + {% for tag in tags if tag.slug==t.slug %} +
  • + + {{dotag.tag(tag)}} + +
  • + {% endfor %} + {% endfor %} + {% endif %} + {# --- TAG LIST ENDS HERE --- #} \ No newline at end of file diff --git a/templates/_types/blog/_card_tile.html b/templates/_types/blog/_card_tile.html new file mode 100644 index 0000000..f03ca16 --- /dev/null +++ b/templates/_types/blog/_card_tile.html @@ -0,0 +1,59 @@ +
    + {% set _href=url_for('blog.post.post_detail', slug=post.slug)|host %} + + {% if post.feature_image %} +
    + +
    + {% endif %} + +
    +

    + {{ post.title }} +

    + + {% if post.status == "draft" %} +
    + Draft + {% if post.publish_requested %} + Publish requested + {% endif %} +
    + {% if post.updated_at %} +

    + Updated: {{ post.updated_at.strftime("%-d %b %Y at %H:%M") }} +

    + {% endif %} + {% elif post.published_at %} +

    + Published: {{ post.published_at.strftime("%-d %b %Y at %H:%M") }} +

    + {% endif %} + + {% if post.custom_excerpt %} +

    + {{ post.custom_excerpt }} +

    + {% elif post.excerpt %} +

    + {{ post.excerpt }} +

    + {% endif %} +
    +
    + + {% include '_types/blog/_card/at_bar.html' %} +
    diff --git a/templates/_types/blog/_cards.html b/templates/_types/blog/_cards.html new file mode 100644 index 0000000..82eee98 --- /dev/null +++ b/templates/_types/blog/_cards.html @@ -0,0 +1,111 @@ +{% for post in posts %} + {% if view == 'tile' %} + {% include "_types/blog/_card_tile.html" %} + {% else %} + {% include "_types/blog/_card.html" %} + {% endif %} +{% endfor %} +{% if page < total_pages|int %} + + + + + +{% else %} +
    End of results
    +{% endif %} + diff --git a/templates/_types/blog/_oob_elements.html b/templates/_types/blog/_oob_elements.html new file mode 100644 index 0000000..2aa02cb --- /dev/null +++ b/templates/_types/blog/_oob_elements.html @@ -0,0 +1,40 @@ +{% 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 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('root-header-child', 'blog-header-child', '_types/blog/header/_header.html')}} + + {% from '_types/root/header/_header.html' import header_row with context %} + {{ header_row(oob=True) }} +{% endblock %} + + + +{# Filter container - blog doesn't have child_summary but still needs this element #} +{% block filter %} + {% include "_types/blog/mobile/_filter/summary.html" %} +{% endblock %} + +{# Aside with filters #} +{% block aside %} + {% include "_types/blog/desktop/menu.html" %} +{% endblock %} + + +{% block mobile_menu %} + {% include '_types/root/_nav.html' %} + {% include '_types/root/_nav_panel.html' %} +{% endblock %} + + +{% block content %} + {% include '_types/blog/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/blog/admin/tag_groups/_edit_header.html b/templates/_types/blog/admin/tag_groups/_edit_header.html new file mode 100644 index 0000000..ade4ee9 --- /dev/null +++ b/templates/_types/blog/admin/tag_groups/_edit_header.html @@ -0,0 +1,9 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='tag-groups-edit-row', oob=oob) %} + {% from 'macros/admin_nav.html' import admin_nav_item %} + {{ admin_nav_item(url_for('blog.tag_groups_admin.edit', id=group.id), 'pencil', group.name, select_colours, aclass='') }} + {% call links.desktop_nav() %} + {% endcall %} + {% endcall %} +{% endmacro %} diff --git a/templates/_types/blog/admin/tag_groups/_edit_main_panel.html b/templates/_types/blog/admin/tag_groups/_edit_main_panel.html new file mode 100644 index 0000000..7d1fa96 --- /dev/null +++ b/templates/_types/blog/admin/tag_groups/_edit_main_panel.html @@ -0,0 +1,79 @@ +
    + + {# --- Edit group form --- #} +
    + + +
    +
    + + +
    +
    +
    + + +
    +
    + + +
    +
    +
    + + +
    +
    + + {# --- Tag checkboxes --- #} +
    + +
    + {% for tag in all_tags %} + + {% endfor %} +
    +
    + +
    + +
    +
    + + {# --- Delete form --- #} +
    + + +
    + +
    diff --git a/templates/_types/blog/admin/tag_groups/_edit_oob.html b/templates/_types/blog/admin/tag_groups/_edit_oob.html new file mode 100644 index 0000000..116bc7b --- /dev/null +++ b/templates/_types/blog/admin/tag_groups/_edit_oob.html @@ -0,0 +1,17 @@ +{% extends 'oob_elements.html' %} + +{% block oobs %} + {% from '_types/root/_n/macros.html' import oob_header with context %} + {{oob_header('tag-groups-header-child', 'tag-groups-edit-child', '_types/blog/admin/tag_groups/_edit_header.html')}} + {{oob_header('root-settings-header-child', 'tag-groups-header-child', '_types/blog/admin/tag_groups/_header.html')}} + + {% from '_types/root/settings/header/_header.html' import header_row with context %} + {{header_row(oob=True)}} +{% endblock %} + +{% block mobile_menu %} +{% endblock %} + +{% block content %} + {% include '_types/blog/admin/tag_groups/_edit_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/blog/admin/tag_groups/_header.html b/templates/_types/blog/admin/tag_groups/_header.html new file mode 100644 index 0000000..d9c3095 --- /dev/null +++ b/templates/_types/blog/admin/tag_groups/_header.html @@ -0,0 +1,9 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='tag-groups-row', oob=oob) %} + {% from 'macros/admin_nav.html' import admin_nav_item %} + {{ admin_nav_item(url_for('blog.tag_groups_admin.index'), 'tags', 'Tag Groups', select_colours, aclass='') }} + {% call links.desktop_nav() %} + {% endcall %} + {% endcall %} +{% endmacro %} diff --git a/templates/_types/blog/admin/tag_groups/_main_panel.html b/templates/_types/blog/admin/tag_groups/_main_panel.html new file mode 100644 index 0000000..1c8b8f4 --- /dev/null +++ b/templates/_types/blog/admin/tag_groups/_main_panel.html @@ -0,0 +1,73 @@ +
    + + {# --- Create new group form --- #} +
    + +

    New Group

    +
    + + + +
    + + +
    + + {# --- Existing groups list --- #} + {% if groups %} + + {% else %} +

    No tag groups yet.

    + {% endif %} + + {# --- Unassigned tags --- #} + {% if unassigned_tags %} +
    +

    Unassigned Tags ({{ unassigned_tags|length }})

    +
    + {% for tag in unassigned_tags %} + + {{ tag.name }} + + {% endfor %} +
    +
    + {% endif %} + +
    diff --git a/templates/_types/blog/admin/tag_groups/_oob_elements.html b/templates/_types/blog/admin/tag_groups/_oob_elements.html new file mode 100644 index 0000000..cb00363 --- /dev/null +++ b/templates/_types/blog/admin/tag_groups/_oob_elements.html @@ -0,0 +1,16 @@ +{% extends 'oob_elements.html' %} + +{% block oobs %} + {% from '_types/root/_n/macros.html' import oob_header with context %} + {{oob_header('root-settings-header-child', 'tag-groups-header-child', '_types/blog/admin/tag_groups/_header.html')}} + + {% from '_types/root/settings/header/_header.html' import header_row with context %} + {{header_row(oob=True)}} +{% endblock %} + +{% block mobile_menu %} +{% endblock %} + +{% block content %} + {% include '_types/blog/admin/tag_groups/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/blog/admin/tag_groups/edit.html b/templates/_types/blog/admin/tag_groups/edit.html new file mode 100644 index 0000000..5fefbc6 --- /dev/null +++ b/templates/_types/blog/admin/tag_groups/edit.html @@ -0,0 +1,13 @@ +{% extends '_types/blog/admin/tag_groups/index.html' %} + +{% block tag_groups_header_child %} + {% from '_types/root/_n/macros.html' import header with context %} + {% call header() %} + {% from '_types/blog/admin/tag_groups/_edit_header.html' import header_row with context %} + {{ header_row() }} + {% endcall %} +{% endblock %} + +{% block content %} + {% include '_types/blog/admin/tag_groups/_edit_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/blog/admin/tag_groups/index.html b/templates/_types/blog/admin/tag_groups/index.html new file mode 100644 index 0000000..680b051 --- /dev/null +++ b/templates/_types/blog/admin/tag_groups/index.html @@ -0,0 +1,20 @@ +{% extends '_types/root/settings/index.html' %} + +{% block root_settings_header_child %} + {% from '_types/root/_n/macros.html' import header with context %} + {% call header() %} + {% from '_types/blog/admin/tag_groups/_header.html' import header_row with context %} + {{ header_row() }} +
    + {% block tag_groups_header_child %} + {% endblock %} +
    + {% endcall %} +{% endblock %} + +{% block content %} + {% include '_types/blog/admin/tag_groups/_main_panel.html' %} +{% endblock %} + +{% block _main_mobile_menu %} +{% endblock %} diff --git a/templates/_types/blog/desktop/menu.html b/templates/_types/blog/desktop/menu.html new file mode 100644 index 0000000..57dba58 --- /dev/null +++ b/templates/_types/blog/desktop/menu.html @@ -0,0 +1,19 @@ +{% import '_types/browse/desktop/_filter/search.html' as s %} +{{ s.search(current_local_href, search, search_count, hx_select) }} +{% include '_types/blog/_action_buttons.html' %} +
    + {% include '_types/blog/desktop/menu/tag_groups.html' %} + {% include '_types/blog/desktop/menu/authors.html' %} +
    + +
    + +
    + + \ No newline at end of file diff --git a/templates/_types/blog/desktop/menu/authors.html b/templates/_types/blog/desktop/menu/authors.html new file mode 100644 index 0000000..de939e0 --- /dev/null +++ b/templates/_types/blog/desktop/menu/authors.html @@ -0,0 +1,62 @@ + {% import '_types/blog/_card/author.html' as doauthor %} + + {# Author filter bar #} + + diff --git a/templates/_types/blog/desktop/menu/tag_groups.html b/templates/_types/blog/desktop/menu/tag_groups.html new file mode 100644 index 0000000..e23a879 --- /dev/null +++ b/templates/_types/blog/desktop/menu/tag_groups.html @@ -0,0 +1,70 @@ + {# Tag group filter bar #} + diff --git a/templates/_types/blog/desktop/menu/tags.html b/templates/_types/blog/desktop/menu/tags.html new file mode 100644 index 0000000..c20b5bc --- /dev/null +++ b/templates/_types/blog/desktop/menu/tags.html @@ -0,0 +1,59 @@ + {% import '_types/blog/_card/tag.html' as dotag %} + + {# Tag filter bar #} + + diff --git a/templates/_types/blog/header/_header.html b/templates/_types/blog/header/_header.html new file mode 100644 index 0000000..67325b9 --- /dev/null +++ b/templates/_types/blog/header/_header.html @@ -0,0 +1,7 @@ + +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='blog-row', oob=oob) %} +
    + {% endcall %} +{% endmacro %} \ No newline at end of file diff --git a/templates/_types/blog/index.html b/templates/_types/blog/index.html new file mode 100644 index 0000000..5978020 --- /dev/null +++ b/templates/_types/blog/index.html @@ -0,0 +1,37 @@ +{% extends '_types/root/_index.html' %} + +{% block meta %} + {{ super() }} + +{% endblock %} + +{% block root_header_child %} + {% from '_types/root/_n/macros.html' import index_row with context %} + {% call index_row('root-blog-header', '_types/blog/header/_header.html') %} + {% block root_blog_header %} + {% endblock %} + {% endcall %} +{% endblock %} + + +{% block aside %} + {% include "_types/blog/desktop/menu.html" %} +{% endblock %} + +{% block filter %} + {% include "_types/blog/mobile/_filter/summary.html" %} +{% endblock %} + +{% block content %} + {% include '_types/blog/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/blog/mobile/_filter/_hamburger.html b/templates/_types/blog/mobile/_filter/_hamburger.html new file mode 100644 index 0000000..10e0b9c --- /dev/null +++ b/templates/_types/blog/mobile/_filter/_hamburger.html @@ -0,0 +1,13 @@ +
    + + + + + + + + +
    diff --git a/templates/_types/blog/mobile/_filter/summary.html b/templates/_types/blog/mobile/_filter/summary.html new file mode 100644 index 0000000..4ed013b --- /dev/null +++ b/templates/_types/blog/mobile/_filter/summary.html @@ -0,0 +1,14 @@ +{% import 'macros/layout.html' as layout %} + +{% call layout.details('/filter', 'md:hidden') %} + {% call layout.filter_summary("filter-summary-mobile", current_local_href, search, search_count, hx_select) %} + {% include '_types/blog/mobile/_filter/summary/tag_groups.html' %} + {% include '_types/blog/mobile/_filter/summary/authors.html' %} + {% endcall %} + {% include '_types/blog/_action_buttons.html' %} +
    + {% include '_types/blog/desktop/menu/tag_groups.html' %} + {% include '_types/blog/desktop/menu/authors.html' %} +
    +{% endcall %} + \ No newline at end of file diff --git a/templates/_types/blog/mobile/_filter/summary/authors.html b/templates/_types/blog/mobile/_filter/summary/authors.html new file mode 100644 index 0000000..32796d9 --- /dev/null +++ b/templates/_types/blog/mobile/_filter/summary/authors.html @@ -0,0 +1,31 @@ +{% if selected_authors and selected_authors|length %} + +{% endif %} \ No newline at end of file diff --git a/templates/_types/blog/mobile/_filter/summary/tag_groups.html b/templates/_types/blog/mobile/_filter/summary/tag_groups.html new file mode 100644 index 0000000..7bf142e --- /dev/null +++ b/templates/_types/blog/mobile/_filter/summary/tag_groups.html @@ -0,0 +1,33 @@ +{% if selected_groups and selected_groups|length %} + +{% endif %} diff --git a/templates/_types/blog/mobile/_filter/summary/tags.html b/templates/_types/blog/mobile/_filter/summary/tags.html new file mode 100644 index 0000000..df6169d --- /dev/null +++ b/templates/_types/blog/mobile/_filter/summary/tags.html @@ -0,0 +1,31 @@ +{% if selected_tags and selected_tags|length %} + +{% endif %} \ No newline at end of file diff --git a/templates/_types/blog/not_found.html b/templates/_types/blog/not_found.html new file mode 100644 index 0000000..f539822 --- /dev/null +++ b/templates/_types/blog/not_found.html @@ -0,0 +1,22 @@ +{% extends '_types/root/_index.html' %} + +{% block content %} +
    +
    πŸ“
    +

    Post Not Found

    +

    + The post "{{ slug }}" could not be found. +

    + + ← Back to Blog + +
    +{% endblock %} diff --git a/templates/_types/blog_drafts/_main_panel.html b/templates/_types/blog_drafts/_main_panel.html new file mode 100644 index 0000000..8cb0b7a --- /dev/null +++ b/templates/_types/blog_drafts/_main_panel.html @@ -0,0 +1,55 @@ +
    + +
    +

    Drafts

    + {% set new_href = url_for('blog.new_post')|host %} + + New Post + +
    + + {% if drafts %} +
    + {% for draft in drafts %} + {% set edit_href = url_for('blog.post.admin.edit', slug=draft.slug)|host %} + +
    +
    +

    + {{ draft.title or "Untitled" }} +

    + {% if draft.excerpt %} +

    + {{ draft.excerpt }} +

    + {% endif %} + {% if draft.updated_at %} +

    + Updated: {{ draft.updated_at.strftime("%-d %b %Y at %H:%M") }} +

    + {% endif %} +
    + + Draft + +
    +
    + {% endfor %} +
    + {% else %} +

    No drafts yet.

    + {% endif %} + +
    diff --git a/templates/_types/blog_drafts/_oob_elements.html b/templates/_types/blog_drafts/_oob_elements.html new file mode 100644 index 0000000..8d9790b --- /dev/null +++ b/templates/_types/blog_drafts/_oob_elements.html @@ -0,0 +1,12 @@ +{% extends 'oob_elements.html' %} + +{% from '_types/root/_oob_menu.html' import mobile_menu with context %} + +{% block oobs %} + {% from '_types/blog/header/_header.html' import header_row with context %} + {{ header_row(oob=True) }} +{% endblock %} + +{% block content %} + {% include '_types/blog_drafts/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/blog_drafts/index.html b/templates/_types/blog_drafts/index.html new file mode 100644 index 0000000..6ce38f1 --- /dev/null +++ b/templates/_types/blog_drafts/index.html @@ -0,0 +1,11 @@ +{% extends '_types/root/_index.html' %} + +{% block root_header_child %} + {% from '_types/root/_n/macros.html' import index_row with context %} + {% call index_row('root-blog-header', '_types/blog/header/_header.html') %} + {% endcall %} +{% endblock %} + +{% block content %} + {% include '_types/blog_drafts/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/blog_new/_main_panel.html b/templates/_types/blog_new/_main_panel.html new file mode 100644 index 0000000..6c3d264 --- /dev/null +++ b/templates/_types/blog_new/_main_panel.html @@ -0,0 +1,259 @@ +{# ── Error banner ── #} +{% if save_error %} +
    + Save failed: {{ save_error }} +
    +{% endif %} + +
    + + + + + + {# ── Feature image ── #} +
    + {# Empty state: add link #} +
    + +
    + + {# Filled state: image preview + controls #} + + + {# Upload spinner overlay #} + + + {# Hidden file input #} + +
    + + {# ── Title ── #} + + + {# ── Excerpt ── #} + + + {# ── Editor mount point ── #} +
    + + {# ── Status + Save footer ── #} +
    + + + +
    +
    + +{# ── Koenig editor assets ── #} + + + + diff --git a/templates/_types/blog_new/_oob_elements.html b/templates/_types/blog_new/_oob_elements.html new file mode 100644 index 0000000..61e78f5 --- /dev/null +++ b/templates/_types/blog_new/_oob_elements.html @@ -0,0 +1,12 @@ +{% extends 'oob_elements.html' %} + +{% from '_types/root/_oob_menu.html' import mobile_menu with context %} + +{% block oobs %} + {% from '_types/blog/header/_header.html' import header_row with context %} + {{ header_row(oob=True) }} +{% endblock %} + +{% block content %} + {% include '_types/blog_new/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/blog_new/index.html b/templates/_types/blog_new/index.html new file mode 100644 index 0000000..3c802d4 --- /dev/null +++ b/templates/_types/blog_new/index.html @@ -0,0 +1,11 @@ +{% extends '_types/root/_index.html' %} + +{% block root_header_child %} + {% from '_types/root/_n/macros.html' import index_row with context %} + {% call index_row('root-blog-header', '_types/blog/header/_header.html') %} + {% endcall %} +{% endblock %} + +{% block content %} + {% include '_types/blog_new/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/browse/like/button.html b/templates/_types/browse/like/button.html new file mode 100644 index 0000000..426bdc1 --- /dev/null +++ b/templates/_types/browse/like/button.html @@ -0,0 +1,20 @@ + diff --git a/templates/_types/home/_oob_elements.html b/templates/_types/home/_oob_elements.html new file mode 100644 index 0000000..03a4f17 --- /dev/null +++ b/templates/_types/home/_oob_elements.html @@ -0,0 +1,19 @@ +{% extends 'oob_elements.html' %} + +{% 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/header/_header.html' import header_row with context %} + {{ header_row(oob=True) }} +{% endblock %} + +{% block content %} +
    +
    + {% if post.html %} + {{post.html|safe}} + {% endif %} +
    +
    +{% endblock %} diff --git a/templates/_types/home/index.html b/templates/_types/home/index.html new file mode 100644 index 0000000..e5df191 --- /dev/null +++ b/templates/_types/home/index.html @@ -0,0 +1,14 @@ +{% extends '_types/root/_index.html' %} +{% block meta %} + {% include '_types/post/_meta.html' %} +{% endblock %} + +{% block content %} +
    +
    + {% if post.html %} + {{post.html|safe}} + {% endif %} +
    +
    +{% endblock %} diff --git a/templates/_types/menu_items/_form.html b/templates/_types/menu_items/_form.html new file mode 100644 index 0000000..8eed1c0 --- /dev/null +++ b/templates/_types/menu_items/_form.html @@ -0,0 +1,125 @@ + + + diff --git a/templates/_types/menu_items/_list.html b/templates/_types/menu_items/_list.html new file mode 100644 index 0000000..3892f07 --- /dev/null +++ b/templates/_types/menu_items/_list.html @@ -0,0 +1,68 @@ +
    + {% if menu_items %} +
    + {% for item in menu_items %} +
    + {# Drag handle #} +
    + +
    + + {# Page image #} + {% if item.feature_image %} + {{ item.label }} + {% else %} +
    + {% endif %} + + {# Page title #} +
    +
    {{ item.label }}
    +
    {{ item.slug }}
    +
    + + {# Sort order #} +
    + Order: {{ item.sort_order }} +
    + + {# Actions #} +
    + + +
    +
    + {% endfor %} +
    + {% else %} +
    + +

    No menu items yet. Add one to get started!

    +
    + {% endif %} +
    diff --git a/templates/_types/menu_items/_main_panel.html b/templates/_types/menu_items/_main_panel.html new file mode 100644 index 0000000..bc502dd --- /dev/null +++ b/templates/_types/menu_items/_main_panel.html @@ -0,0 +1,20 @@ +
    +
    + +
    + + {# Form container #} + + + {# Menu items list #} + +
    diff --git a/templates/_types/menu_items/_nav_oob.html b/templates/_types/menu_items/_nav_oob.html new file mode 100644 index 0000000..e25189a --- /dev/null +++ b/templates/_types/menu_items/_nav_oob.html @@ -0,0 +1,31 @@ +{% set _app_slugs = {'cart': cart_url('/')} %} +{% set _first_seg = request.path.strip('/').split('/')[0] %} + diff --git a/templates/_types/menu_items/_oob_elements.html b/templates/_types/menu_items/_oob_elements.html new file mode 100644 index 0000000..c242593 --- /dev/null +++ b/templates/_types/menu_items/_oob_elements.html @@ -0,0 +1,23 @@ +{% extends 'oob_elements.html' %} + +{# OOB elements for HTMX navigation - all elements that need updating #} + +{# Import shared OOB macros #} + +{% block oobs %} + {% from '_types/root/_n/macros.html' import oob_header with context %} + {{oob_header('root-settings-header-child', 'menu_items-header-child', '_types/menu_items/header/_header.html')}} + + {% from '_types/root/settings/header/_header.html' import header_row with context %} + {{header_row(oob=True)}} + +{% endblock %} + +{% block mobile_menu %} +{#% include '_types/root/settings/_nav.html' %#} +{% endblock %} + +{% block content %} + {% include '_types/menu_items/_main_panel.html' %} +{% endblock %} + diff --git a/templates/_types/menu_items/_page_search_results.html b/templates/_types/menu_items/_page_search_results.html new file mode 100644 index 0000000..df36d0d --- /dev/null +++ b/templates/_types/menu_items/_page_search_results.html @@ -0,0 +1,44 @@ +{% if pages %} +
    + {% for post in pages %} +
    + + {# Page image #} + {% if post.feature_image %} + {{ post.title }} + {% else %} +
    + {% endif %} + + {# Page info #} +
    +
    {{ post.title }}
    +
    {{ post.slug }}
    +
    +
    + {% endfor %} + + {# Infinite scroll sentinel #} + {% if has_more %} +
    + Loading more... +
    + {% endif %} +
    +{% elif query %} +
    + No pages found matching "{{ query }}" +
    +{% endif %} diff --git a/templates/_types/menu_items/header/_header.html b/templates/_types/menu_items/header/_header.html new file mode 100644 index 0000000..55a18d6 --- /dev/null +++ b/templates/_types/menu_items/header/_header.html @@ -0,0 +1,9 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='menu_items-row', oob=oob) %} + {% from 'macros/admin_nav.html' import admin_nav_item %} + {{ admin_nav_item(url_for('menu_items.list_menu_items'), 'bars', 'Menu Items', select_colours, aclass='') }} + {% call links.desktop_nav() %} + {% endcall %} + {% endcall %} +{% endmacro %} diff --git a/templates/_types/menu_items/index.html b/templates/_types/menu_items/index.html new file mode 100644 index 0000000..5bcf7da --- /dev/null +++ b/templates/_types/menu_items/index.html @@ -0,0 +1,20 @@ +{% extends '_types/root/settings/index.html' %} + +{% block root_settings_header_child %} + {% from '_types/root/_n/macros.html' import header with context %} + {% call header() %} + {% from '_types/menu_items/header/_header.html' import header_row with context %} + {{ header_row() }} + + {% endcall %} +{% endblock %} + +{% block content %} + {% include '_types/menu_items/_main_panel.html' %} +{% endblock %} + +{% block _main_mobile_menu %} +{% endblock %} diff --git a/templates/_types/post/_entry_container.html b/templates/_types/post/_entry_container.html new file mode 100644 index 0000000..3c3965a --- /dev/null +++ b/templates/_types/post/_entry_container.html @@ -0,0 +1,24 @@ +
    +
    + {% include '_types/post/_entry_items.html' with context %} +
    +
    + + diff --git a/templates/_types/post/_entry_items.html b/templates/_types/post/_entry_items.html new file mode 100644 index 0000000..d221e85 --- /dev/null +++ b/templates/_types/post/_entry_items.html @@ -0,0 +1,38 @@ +{# Get entries from either direct variable or associated_entries dict #} +{% set entry_list = entries if entries is defined else (associated_entries.entries if associated_entries is defined else []) %} +{% set current_page = page if page is defined else (associated_entries.page if associated_entries is defined else 1) %} +{% set has_more_entries = has_more if has_more is defined else (associated_entries.has_more if associated_entries is defined else False) %} + +{% for entry in entry_list %} + {% set _entry_path = '/' + post.slug + '/calendars/' + entry.calendar_slug + '/' + entry.start_at.year|string + '/' + entry.start_at.month|string + '/' + entry.start_at.day|string + '/entries/' + entry.id|string + '/' %} + + {% if post.feature_image %} + {{ post.title }} + {% else %} +
    + {% endif %} +
    +
    {{ entry.name }}
    +
    + {{ entry.start_at.strftime('%b %d, %Y at %H:%M') }} + {% if entry.end_at %} – {{ entry.end_at.strftime('%H:%M') }}{% endif %} +
    +
    +
    +{% endfor %} + +{# Load more entries one at a time until container is full #} +{% if has_more_entries %} +
    +
    +{% endif %} diff --git a/templates/_types/post/_main_panel.html b/templates/_types/post/_main_panel.html new file mode 100644 index 0000000..52a2c3a --- /dev/null +++ b/templates/_types/post/_main_panel.html @@ -0,0 +1,65 @@ +{# Main panel fragment for HTMX navigation - post/page article content #} +
    + {# Draft indicator + edit link (shown for both posts and pages) #} + {% if post.status == "draft" %} +
    + Draft + {% if post.publish_requested %} + Publish requested + {% endif %} + {% set is_admin = (g.get("rights") or {}).get("admin") %} + {% if is_admin or (g.user and post.user_id == g.user.id) %} + {% set edit_href = url_for('blog.post.admin.edit', slug=post.slug)|host %} + + Edit + + {% endif %} +
    + {% endif %} + + {% if not post.is_page %} + {# ── Blog post chrome: like button, excerpt, tags/authors ── #} + {% if g.user %} +
    + {% set slug = post.slug %} + {% set liked = post.is_liked or False %} + {% set like_url = url_for('blog.post.like_toggle', slug=slug)|host %} + {% set item_type = 'post' %} + {% include "_types/browse/like/button.html" %} +
    + {% endif %} + + {% if post.custom_excerpt %} +
    + {{post.custom_excerpt|safe}} +
    + {% endif %} + + {% endif %} + + {% if post.feature_image %} +
    + +
    + {% endif %} +
    + {% if post.html %} + {{post.html|safe}} + {% endif %} +
    +
    +
    diff --git a/templates/_types/post/_oob_elements.html b/templates/_types/post/_oob_elements.html new file mode 100644 index 0000000..d8bda2c --- /dev/null +++ b/templates/_types/post/_oob_elements.html @@ -0,0 +1,36 @@ +{% 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 %} + + + +{% block oobs %} + {% from '_types/root/header/_header.html' import header_row with context %} + {{ header_row(oob=True) }} +{% endblock %} + +{% from '_types/root/_n/macros.html' import header with context %} +{% call header(id='root-header-child', oob=True) %} + {% call header() %} + {% from '_types/post/header/_header.html' import header_row with context %} + {{header_row()}} +
    + +
    + {% endcall %} +{% endcall %} + + +{# Mobile menu #} + +{% block mobile_menu %} + {% include '_types/post/_nav.html' %} +{% endblock %} + +{% block content %} + {% include '_types/post/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/post/admin/_associated_entries.html b/templates/_types/post/admin/_associated_entries.html new file mode 100644 index 0000000..d9fe853 --- /dev/null +++ b/templates/_types/post/admin/_associated_entries.html @@ -0,0 +1,50 @@ +
    +

    Associated Entries

    + {% if associated_entry_ids %} +
    + {% for calendar in all_calendars %} + {% for entry in calendar.entries %} + {% if entry.id in associated_entry_ids and entry.deleted_at is none %} + + {% endif %} + {% endfor %} + {% endfor %} +
    + {% else %} +
    No entries associated yet. Browse calendars below to add entries.
    + {% endif %} +
    diff --git a/templates/_types/post/admin/_calendar_view.html b/templates/_types/post/admin/_calendar_view.html new file mode 100644 index 0000000..80ae33f --- /dev/null +++ b/templates/_types/post/admin/_calendar_view.html @@ -0,0 +1,88 @@ +
    + {# Month/year navigation #} +
    + +
    + + {# Calendar grid #} +
    + + +
    + {% for week in weeks %} + {% for day in week %} +
    +
    {{ day.date.day }}
    + + {# Entries for this day #} +
    + {% for e in month_entries %} + {% if e.start_at.date() == day.date %} + {% if e.id in associated_entry_ids %} + {# Associated entry - show with delete button #} +
    + {{ e.name }} + +
    + {% else %} + {# Non-associated entry - clickable to add #} + + {% endif %} + {% endif %} + {% endfor %} +
    +
    + {% endfor %} + {% endfor %} +
    +
    +
    diff --git a/templates/_types/post/admin/_main_panel.html b/templates/_types/post/admin/_main_panel.html new file mode 100644 index 0000000..58d5238 --- /dev/null +++ b/templates/_types/post/admin/_main_panel.html @@ -0,0 +1,7 @@ +{# Main panel fragment for HTMX navigation - post admin #} +
    +
    +
    diff --git a/templates/_types/post/admin/_nav_entries.html b/templates/_types/post/admin/_nav_entries.html new file mode 100644 index 0000000..47290d4 --- /dev/null +++ b/templates/_types/post/admin/_nav_entries.html @@ -0,0 +1,50 @@ + + {# Left scroll arrow - desktop only #} + + + {# Widget-driven nav items container #} +
    +
    + {% for wdata in container_nav_widgets %} + {% with ctx=wdata.ctx %} + {% include wdata.widget.template with context %} + {% endwith %} + {% endfor %} +
    +
    + + + + {# Right scroll arrow - desktop only #} + diff --git a/templates/_types/post/admin/_nav_entries_oob.html b/templates/_types/post/admin/_nav_entries_oob.html new file mode 100644 index 0000000..eecc3d5 --- /dev/null +++ b/templates/_types/post/admin/_nav_entries_oob.html @@ -0,0 +1,80 @@ +{# OOB swap for nav entries and calendars when toggling associations or editing calendars #} +{% import 'macros/links.html' as links %} + +{# Associated Entries and Calendars - vertical on mobile, horizontal with arrows on desktop #} +{% if (associated_entries and associated_entries.entries) or calendars %} +
    + {# Left scroll arrow - desktop only #} + + +
    +
    + {# Calendar entries #} + {% if associated_entries and associated_entries.entries %} + {% for entry in associated_entries.entries %} + {% set _entry_path = '/' + post.slug + '/calendars/' + entry.calendar_slug + '/' + entry.start_at.year|string + '/' + entry.start_at.month|string + '/' + entry.start_at.day|string + '/entries/' + entry.id|string + '/' %} + +
    +
    +
    {{ entry.name }}
    +
    + {{ entry.start_at.strftime('%b %d, %Y at %H:%M') }} + {% if entry.end_at %} – {{ entry.end_at.strftime('%H:%M') }}{% endif %} +
    +
    +
    + {% endfor %} + {% endif %} + {# Calendar links #} + {% if calendars %} + {% for calendar in calendars %} + {% set local_href=events_url('/' + post.slug + '/calendars/' + calendar.slug + '/') %} + + +
    {{calendar.name}}
    +
    + {% endfor %} + {% endif %} +
    +
    + + + + {# Right scroll arrow - desktop only #} + +
    +{% else %} + {# Empty placeholder to remove nav items when all are disassociated/deleted #} +
    +{% endif %} diff --git a/templates/_types/post/admin/header/_header.html b/templates/_types/post/admin/header/_header.html new file mode 100644 index 0000000..2708e4f --- /dev/null +++ b/templates/_types/post/admin/header/_header.html @@ -0,0 +1,13 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='post-admin-row', oob=oob) %} + {% call links.link( + url_for('blog.post.admin.admin', slug=post.slug), + hx_select_search) %} + {{ links.admin() }} + {% endcall %} + {% call links.desktop_nav() %} + {% include '_types/post/admin/_nav.html' %} + {% endcall %} + {% endcall %} +{% endmacro %} \ No newline at end of file diff --git a/templates/_types/post/index.html b/templates/_types/post/index.html new file mode 100644 index 0000000..56ed99c --- /dev/null +++ b/templates/_types/post/index.html @@ -0,0 +1,25 @@ +{% extends '_types/root/_index.html' %} +{% import 'macros/layout.html' as layout %} +{% block meta %} + {% include '_types/post/_meta.html' %} +{% endblock %} + +{% block root_header_child %} + {% from '_types/root/_n/macros.html' import index_row with context %} + {% call index_row('post-header-child', '_types/post/header/_header.html') %} + {% block post_header_child %} + {% endblock %} + {% endcall %} +{% endblock %} + +{% block _main_mobile_menu %} + {% include '_types/post/_nav.html' %} +{% endblock %} + + +{% block aside %} +{% endblock %} + +{% block content %} + {% include '_types/post/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/post_data/_main_panel.html b/templates/_types/post_data/_main_panel.html new file mode 100644 index 0000000..83dcc32 --- /dev/null +++ b/templates/_types/post_data/_main_panel.html @@ -0,0 +1,137 @@ +{% macro render_scalar_table(obj) -%} +
    + + + + + + + + + {% for col in obj.__mapper__.columns %} + {% set key = col.key %} + {% set val = obj|attr(key) %} + {% if key != "_sa_instance_state" %} + + + + + {% endif %} + {% endfor %} + +
    FieldValue
    {{ key }} + {% if val is none %} + β€” + {% elif val.__class__.__name__ in ["datetime", "date"] and val.isoformat is defined %} +
    {{ val.isoformat() }}
    + {% elif val is string %} +
    {{ val }}
    + {% else %} +
    {{ val }}
    + {% endif %} +
    +
    +{%- endmacro %} + +{% macro render_model(obj, depth=0, max_depth=2) -%} + {% if obj is none %} + β€” + {% else %} +
    + {{ render_scalar_table(obj) }} + +
    + {% for rel in obj.__mapper__.relationships %} + {% set rel_name = rel.key %} + {% set loaded = rel.key in obj.__dict__ %} + {% if loaded %} + {% set value = obj|attr(rel_name) %} + {% else %} + {% set value = none %} + {% endif %} + +
    +
    + Relationship: {{ rel_name }} + + {{ 'many' if rel.uselist else 'one' }} β†’ {{ rel.mapper.class_.__name__ }} + {% if not loaded %} β€’ not loaded{% endif %} + +
    + +
    + {% if value is none %} + β€” + + {% elif rel.uselist %} + {% set items = value or [] %} +
    {{ items|length }} item{{ '' if items|length == 1 else 's' }}
    + + {% if items %} +
    + + + + + + + + + {% for it in items %} + + + + + {% endfor %} + +
    #Summary
    {{ loop.index }} + {% set ident = [] %} + {% for k in ['id','ghost_id','uuid','slug','name','title'] if k in it.__mapper__.c %} + {% set v = (it|attr(k))|default('', true) %} + {% do ident.append(k ~ '=' ~ v) %} + {% endfor %} +
    {{ (ident|join(' β€’ ')) or it|string }}
    + + {% if depth < max_depth %} +
    + {{ render_model(it, depth+1, max_depth) }} +
    + {% else %} +
    …max depth reached…
    + {% endif %} +
    +
    + {% endif %} + + {% else %} + {% set child = value %} + {% set ident = [] %} + {% for k in ['id','ghost_id','uuid','slug','name','title'] if k in child.__mapper__.c %} + {% set v = (child|attr(k))|default('', true) %} + {% do ident.append(k ~ '=' ~ v) %} + {% endfor %} +
    {{ (ident|join(' β€’ ')) or child|string }}
    + + {% if depth < max_depth %} +
    + {{ render_model(child, depth+1, max_depth) }} +
    + {% else %} +
    …max depth reached…
    + {% endif %} + {% endif %} +
    +
    + {% endfor %} +
    +
    + {% endif %} +{%- endmacro %} + +
    +
    + Model: Post β€’ Table: {{ original_post.__tablename__ }} +
    + {{ render_model(original_post, 0, 2) }} +
    + diff --git a/templates/_types/post_data/_nav.html b/templates/_types/post_data/_nav.html new file mode 100644 index 0000000..f5c504d --- /dev/null +++ b/templates/_types/post_data/_nav.html @@ -0,0 +1,2 @@ +{% from 'macros/admin_nav.html' import placeholder_nav %} +{{ placeholder_nav() }} diff --git a/templates/_types/post_data/_oob_elements.html b/templates/_types/post_data/_oob_elements.html new file mode 100644 index 0000000..32fd0c7 --- /dev/null +++ b/templates/_types/post_data/_oob_elements.html @@ -0,0 +1,28 @@ +{% extends 'oob_elements.html' %} + +{# OOB elements for HTMX navigation - all elements that need updating #} + +{# Import shared OOB macros #} +{% 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-admin-header-child', 'post_data-header-child', '_types/post_data/header/_header.html')}} + + {% from '_types/post/admin/header/_header.html' import header_row with context %} + {{ header_row(oob=True) }} +{% endblock %} + + +{% block mobile_menu %} + {% include '_types/post_data/_nav.html' %} +{% endblock %} + + +{% block content %} + {% include "_types/post_data/_main_panel.html" %} +{% endblock %} + + diff --git a/templates/_types/post_data/header/_header.html b/templates/_types/post_data/header/_header.html new file mode 100644 index 0000000..27eaf6f --- /dev/null +++ b/templates/_types/post_data/header/_header.html @@ -0,0 +1,15 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='post_data-row', oob=oob) %} + + +
    data
    +
    + {% call links.desktop_nav() %} + {#% include '_types/post_data/_nav.html' %#} + {% endcall %} + {% endcall %} +{% endmacro %} + + + diff --git a/templates/_types/post_data/index.html b/templates/_types/post_data/index.html new file mode 100644 index 0000000..1df67b8 --- /dev/null +++ b/templates/_types/post_data/index.html @@ -0,0 +1,24 @@ +{% extends '_types/post/admin/index.html' %} + +{% block ___app_title %} + {% import 'macros/links.html' as links %} + {% call links.menu_row() %} + {% call links.link(url_for('blog.post.admin.data', slug=post.slug), hx_select_search) %} + +
    + data +
    + {% endcall %} + {% call links.desktop_nav() %} + {% include '_types/post_data/_nav.html' %} + {% endcall %} + {% endcall %} +{% endblock %} + +{% block _main_mobile_menu %} + {% include '_types/post_data/_nav.html' %} +{% endblock %} + +{% block content %} + {% include '_types/post_data/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/post_edit/_main_panel.html b/templates/_types/post_edit/_main_panel.html new file mode 100644 index 0000000..05d9251 --- /dev/null +++ b/templates/_types/post_edit/_main_panel.html @@ -0,0 +1,352 @@ +{# ── Error banner ── #} +{% if save_error %} +
    + Save failed: {{ save_error }} +
    +{% endif %} + +
    + + + + + + + {# ── Feature image ── #} +
    + {# Empty state: add link #} +
    + +
    + + {# Filled state: image preview + controls #} +
    + + {# Delete button (top-right, visible on hover) #} + + + {# Caption input #} + +
    + + {# Upload spinner overlay #} + + + {# Hidden file input #} + +
    + + {# ── Title ── #} + + + {# ── Excerpt ── #} + + + {# ── Editor mount point ── #} +
    + + {# ── Initial Lexical JSON from Ghost ── #} + + + {# ── Status + Publish mode + Save footer ── #} + {% set already_emailed = ghost_post and ghost_post.email and ghost_post.email.status %} +
    + + + {# Publish mode β€” only relevant when publishing #} + + + {# Newsletter picker β€” only when email is involved #} + + + + + {% if save_success %} + Saved. + {% endif %} + {% if request.args.get('publish_requested') %} + Publish requested β€” an admin will review. + {% endif %} + {% if post and post.publish_requested %} + Publish requested + {% endif %} + {% if already_emailed %} + + Emailed{% if ghost_post.newsletter %} to {{ ghost_post.newsletter.name }}{% endif %} + + {% endif %} +
    + + {# ── Publish-mode show/hide logic ── #} + +
    + +{# ── Koenig editor assets ── #} + + + + diff --git a/templates/_types/post_edit/_nav.html b/templates/_types/post_edit/_nav.html new file mode 100644 index 0000000..0b1d08a --- /dev/null +++ b/templates/_types/post_edit/_nav.html @@ -0,0 +1,5 @@ +{% import 'macros/links.html' as links %} +{% call links.link(url_for('blog.post.admin.settings', slug=post.slug), hx_select_search, select_colours, True, aclass=styles.nav_button) %} + + settings +{% endcall %} diff --git a/templates/_types/post_edit/_oob_elements.html b/templates/_types/post_edit/_oob_elements.html new file mode 100644 index 0000000..694096c --- /dev/null +++ b/templates/_types/post_edit/_oob_elements.html @@ -0,0 +1,19 @@ +{% extends 'oob_elements.html' %} + +{% 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-admin-header-child', 'post_edit-header-child', '_types/post_edit/header/_header.html')}} + + {% from '_types/post/admin/header/_header.html' import header_row with context %} + {{ header_row(oob=True) }} +{% endblock %} + +{% block mobile_menu %} + {% include '_types/post_edit/_nav.html' %} +{% endblock %} + +{% block content %} + {% include '_types/post_edit/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/post_edit/header/_header.html b/templates/_types/post_edit/header/_header.html new file mode 100644 index 0000000..60e07e7 --- /dev/null +++ b/templates/_types/post_edit/header/_header.html @@ -0,0 +1,14 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='post_edit-row', oob=oob) %} + {% call links.link(url_for('blog.post.admin.edit', slug=post.slug), hx_select_search) %} + +
    + edit +
    + {% endcall %} + {% call links.desktop_nav() %} + {% include '_types/post_edit/_nav.html' %} + {% endcall %} + {% endcall %} +{% endmacro %} diff --git a/templates/_types/post_edit/index.html b/templates/_types/post_edit/index.html new file mode 100644 index 0000000..b5c7212 --- /dev/null +++ b/templates/_types/post_edit/index.html @@ -0,0 +1,17 @@ +{% extends '_types/post/admin/index.html' %} + +{% block post_admin_header_child %} + {% from '_types/root/_n/macros.html' import index_row with context %} + {% call index_row('post-admin-header-child', '_types/post_edit/header/_header.html') %} + {% block post_edit_header_child %} + {% endblock %} + {% endcall %} +{% endblock %} + +{% block _main_mobile_menu %} + {% include '_types/post_edit/_nav.html' %} +{% endblock %} + +{% block content %} + {% include '_types/post_edit/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/post_entries/_main_panel.html b/templates/_types/post_entries/_main_panel.html new file mode 100644 index 0000000..342041e --- /dev/null +++ b/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/templates/_types/post_entries/_nav.html b/templates/_types/post_entries/_nav.html new file mode 100644 index 0000000..f5c504d --- /dev/null +++ b/templates/_types/post_entries/_nav.html @@ -0,0 +1,2 @@ +{% from 'macros/admin_nav.html' import placeholder_nav %} +{{ placeholder_nav() }} diff --git a/templates/_types/post_entries/_oob_elements.html b/templates/_types/post_entries/_oob_elements.html new file mode 100644 index 0000000..3ef5559 --- /dev/null +++ b/templates/_types/post_entries/_oob_elements.html @@ -0,0 +1,28 @@ +{% extends 'oob_elements.html' %} + +{# OOB elements for HTMX navigation - all elements that need updating #} + +{# Import shared OOB macros #} +{% 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-admin-header-child', 'post_entries-header-child', '_types/post_entries/header/_header.html')}} + + {% from '_types/post/admin/header/_header.html' import header_row with context %} + {{ header_row(oob=True) }} +{% endblock %} + + +{% block mobile_menu %} + {% include '_types/post_entries/_nav.html' %} +{% endblock %} + + +{% block content %} + {% include "_types/post_entries/_main_panel.html" %} +{% endblock %} + + diff --git a/templates/_types/post_entries/header/_header.html b/templates/_types/post_entries/header/_header.html new file mode 100644 index 0000000..019c000 --- /dev/null +++ b/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/templates/_types/post_entries/index.html b/templates/_types/post_entries/index.html new file mode 100644 index 0000000..382d297 --- /dev/null +++ b/templates/_types/post_entries/index.html @@ -0,0 +1,19 @@ +{% extends '_types/post/admin/index.html' %} + + + +{% block post_admin_header_child %} + {% from '_types/root/_n/macros.html' import index_row with context %} + {% call index_row('post-admin-header-child', '_types/post_entries/header/_header.html') %} + {% block post_entries_header_child %} + {% endblock %} + {% endcall %} +{% endblock %} + +{% block _main_mobile_menu %} + {% include '_types/post_entries/_nav.html' %} +{% endblock %} + +{% block content %} + {% include '_types/post_entries/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/post_settings/_main_panel.html b/templates/_types/post_settings/_main_panel.html new file mode 100644 index 0000000..038fab1 --- /dev/null +++ b/templates/_types/post_settings/_main_panel.html @@ -0,0 +1,198 @@ +{# ── Post/Page Settings Form ── #} +{% set gp = ghost_post or {} %} +{% set _is_page = post.is_page if post else False %} + +{% macro field_label(text, field_for=None) %} + +{% endmacro %} + +{% macro text_input(name, value='', placeholder='', type='text', maxlength=None) %} + +{% endmacro %} + +{% macro textarea_input(name, value='', placeholder='', rows=3, maxlength=None) %} + +{% endmacro %} + +{% macro checkbox_input(name, checked=False, label='') %} + +{% endmacro %} + +{% macro section(title, open=False) %} +
    + + {{ title }} + +
    + {{ caller() }} +
    +
    +{% endmacro %} + +
    + + + +
    + + {# ── General ── #} + {% call section('General', open=True) %} +
    + {{ field_label('Slug', 'settings-slug') }} + {{ text_input('slug', gp.slug or '', 'page-slug' if _is_page else 'post-slug') }} +
    +
    + {{ field_label('Published at', 'settings-published_at') }} + +
    +
    + {{ checkbox_input('featured', gp.featured, 'Featured page' if _is_page else 'Featured post') }} +
    +
    + {{ field_label('Visibility', 'settings-visibility') }} + +
    +
    + {{ checkbox_input('email_only', gp.email_only, 'Email only') }} +
    + {% endcall %} + + {# ── Tags ── #} + {% call section('Tags') %} +
    + {{ field_label('Tags (comma-separated)', 'settings-tags') }} + {% set tag_names = gp.tags|map(attribute='name')|list|join(', ') if gp.tags else '' %} + {{ text_input('tags', tag_names, 'news, updates, featured') }} +

    Unknown tags will be created automatically.

    +
    + {% endcall %} + + {# ── Feature Image ── #} + {% call section('Feature Image') %} +
    + {{ field_label('Alt text', 'settings-feature_image_alt') }} + {{ text_input('feature_image_alt', gp.feature_image_alt or '', 'Describe the feature image') }} +
    + {% endcall %} + + {# ── SEO / Meta ── #} + {% call section('SEO / Meta') %} +
    + {{ field_label('Meta title', 'settings-meta_title') }} + {{ text_input('meta_title', gp.meta_title or '', 'SEO title', maxlength=300) }} +

    Recommended: 70 characters. Max: 300.

    +
    +
    + {{ field_label('Meta description', 'settings-meta_description') }} + {{ textarea_input('meta_description', gp.meta_description or '', 'SEO description', rows=2, maxlength=500) }} +

    Recommended: 156 characters.

    +
    +
    + {{ field_label('Canonical URL', 'settings-canonical_url') }} + {{ text_input('canonical_url', gp.canonical_url or '', 'https://example.com/original-post', type='url') }} +
    + {% endcall %} + + {# ── Facebook / OpenGraph ── #} + {% call section('Facebook / OpenGraph') %} +
    + {{ field_label('OG title', 'settings-og_title') }} + {{ text_input('og_title', gp.og_title or '') }} +
    +
    + {{ field_label('OG description', 'settings-og_description') }} + {{ textarea_input('og_description', gp.og_description or '', rows=2) }} +
    +
    + {{ field_label('OG image URL', 'settings-og_image') }} + {{ text_input('og_image', gp.og_image or '', 'https://...', type='url') }} +
    + {% endcall %} + + {# ── X / Twitter ── #} + {% call section('X / Twitter') %} +
    + {{ field_label('Twitter title', 'settings-twitter_title') }} + {{ text_input('twitter_title', gp.twitter_title or '') }} +
    +
    + {{ field_label('Twitter description', 'settings-twitter_description') }} + {{ textarea_input('twitter_description', gp.twitter_description or '', rows=2) }} +
    +
    + {{ field_label('Twitter image URL', 'settings-twitter_image') }} + {{ text_input('twitter_image', gp.twitter_image or '', 'https://...', type='url') }} +
    + {% endcall %} + + {# ── Advanced ── #} + {% call section('Advanced') %} +
    + {{ field_label('Custom template', 'settings-custom_template') }} + {{ text_input('custom_template', gp.custom_template or '', 'custom-page.hbs' if _is_page else 'custom-post.hbs') }} +
    + {% endcall %} + +
    + + {# ── Save footer ── #} +
    + + + {% if save_success %} + Saved. + {% endif %} +
    +
    diff --git a/templates/_types/post_settings/_nav.html b/templates/_types/post_settings/_nav.html new file mode 100644 index 0000000..a08d80a --- /dev/null +++ b/templates/_types/post_settings/_nav.html @@ -0,0 +1,5 @@ +{% import 'macros/links.html' as links %} +{% call links.link(url_for('blog.post.admin.edit', slug=post.slug), hx_select_search, select_colours, True, aclass=styles.nav_button) %} + + edit +{% endcall %} diff --git a/templates/_types/post_settings/_oob_elements.html b/templates/_types/post_settings/_oob_elements.html new file mode 100644 index 0000000..d2d6beb --- /dev/null +++ b/templates/_types/post_settings/_oob_elements.html @@ -0,0 +1,19 @@ +{% extends 'oob_elements.html' %} + +{% 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-admin-header-child', 'post_settings-header-child', '_types/post_settings/header/_header.html')}} + + {% from '_types/post/admin/header/_header.html' import header_row with context %} + {{ header_row(oob=True) }} +{% endblock %} + +{% block mobile_menu %} + {% include '_types/post_settings/_nav.html' %} +{% endblock %} + +{% block content %} + {% include '_types/post_settings/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/post_settings/header/_header.html b/templates/_types/post_settings/header/_header.html new file mode 100644 index 0000000..ba187fe --- /dev/null +++ b/templates/_types/post_settings/header/_header.html @@ -0,0 +1,14 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='post_settings-row', oob=oob) %} + {% call links.link(url_for('blog.post.admin.settings', slug=post.slug), hx_select_search) %} + +
    + settings +
    + {% endcall %} + {% call links.desktop_nav() %} + {% include '_types/post_settings/_nav.html' %} + {% endcall %} + {% endcall %} +{% endmacro %} diff --git a/templates/_types/post_settings/index.html b/templates/_types/post_settings/index.html new file mode 100644 index 0000000..59835f4 --- /dev/null +++ b/templates/_types/post_settings/index.html @@ -0,0 +1,17 @@ +{% extends '_types/post/admin/index.html' %} + +{% block post_admin_header_child %} + {% from '_types/root/_n/macros.html' import index_row with context %} + {% call index_row('post-admin-header-child', '_types/post_settings/header/_header.html') %} + {% block post_settings_header_child %} + {% endblock %} + {% endcall %} +{% endblock %} + +{% block _main_mobile_menu %} + {% include '_types/post_settings/_nav.html' %} +{% endblock %} + +{% block content %} + {% include '_types/post_settings/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/root/header/_header.html b/templates/_types/root/header/_header.html index 6724edb..7792cd5 100644 --- a/templates/_types/root/header/_header.html +++ b/templates/_types/root/header/_header.html @@ -11,9 +11,6 @@ {# Cart mini β€” fetched from cart app as fragment #} {% if cart_mini_html %} {{ cart_mini_html | safe }} - {% else %} - {% from '_types/cart/_mini.html' import mini with context %} - {{mini()}} {% endif %} {# Site title #} @@ -26,18 +23,10 @@ @@ -48,12 +37,6 @@
    {% if auth_menu_html %} {{ auth_menu_html | safe }} - {% else %} - {% if g.user %} - {% include '_types/root/mobile/_full_user.html' %} - {% else %} - {% include '_types/root/mobile/_sign_in.html' %} - {% endif %} {% endif %}
    {% endmacro %} diff --git a/templates/_types/root/settings/_main_panel.html b/templates/_types/root/settings/_main_panel.html new file mode 100644 index 0000000..9f4c9a8 --- /dev/null +++ b/templates/_types/root/settings/_main_panel.html @@ -0,0 +1,2 @@ +
    +
    diff --git a/templates/_types/root/settings/_nav.html b/templates/_types/root/settings/_nav.html new file mode 100644 index 0000000..f9d4420 --- /dev/null +++ b/templates/_types/root/settings/_nav.html @@ -0,0 +1,5 @@ +{% from 'macros/admin_nav.html' import admin_nav_item %} +{{ admin_nav_item(url_for('menu_items.list_menu_items'), 'bars', 'Menu Items', select_colours) }} +{{ admin_nav_item(url_for('snippets.list_snippets'), 'puzzle-piece', 'Snippets', select_colours) }} +{{ admin_nav_item(url_for('blog.tag_groups_admin.index'), 'tags', 'Tag Groups', select_colours) }} +{{ admin_nav_item(url_for('settings.cache'), 'refresh', 'Cache', select_colours) }} diff --git a/templates/_types/root/settings/_oob_elements.html b/templates/_types/root/settings/_oob_elements.html new file mode 100644 index 0000000..fbe1bf3 --- /dev/null +++ b/templates/_types/root/settings/_oob_elements.html @@ -0,0 +1,26 @@ +{% 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 with context %} + +{% block oobs %} + + {% from '_types/root/_n/macros.html' import oob_header with context %} + {{oob_header('root-header-child', 'root-settings-header-child', '_types/root/settings/header/_header.html')}} + + {% from '_types/root/header/_header.html' import header_row with context %} + {{ header_row(oob=True) }} +{% endblock %} + + +{% block mobile_menu %} +{% include '_types/root/settings/_nav.html' %} +{% endblock %} + + +{% block content %} + {% include '_types/root/settings/_main_panel.html' %} +{% endblock %} + diff --git a/templates/_types/root/settings/cache/_header.html b/templates/_types/root/settings/cache/_header.html new file mode 100644 index 0000000..64f8535 --- /dev/null +++ b/templates/_types/root/settings/cache/_header.html @@ -0,0 +1,9 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='cache-row', oob=oob) %} + {% from 'macros/admin_nav.html' import admin_nav_item %} + {{ admin_nav_item(url_for('settings.cache'), 'refresh', 'Cache', select_colours, aclass='') }} + {% call links.desktop_nav() %} + {% endcall %} + {% endcall %} +{% endmacro %} diff --git a/templates/_types/root/settings/cache/_main_panel.html b/templates/_types/root/settings/cache/_main_panel.html new file mode 100644 index 0000000..854012d --- /dev/null +++ b/templates/_types/root/settings/cache/_main_panel.html @@ -0,0 +1,14 @@ +
    +
    +
    + + +
    +
    +
    +
    diff --git a/templates/_types/root/settings/cache/_oob_elements.html b/templates/_types/root/settings/cache/_oob_elements.html new file mode 100644 index 0000000..5989bf7 --- /dev/null +++ b/templates/_types/root/settings/cache/_oob_elements.html @@ -0,0 +1,16 @@ +{% extends 'oob_elements.html' %} + +{% block oobs %} + {% from '_types/root/_n/macros.html' import oob_header with context %} + {{oob_header('root-settings-header-child', 'cache-header-child', '_types/root/settings/cache/_header.html')}} + + {% from '_types/root/settings/header/_header.html' import header_row with context %} + {{header_row(oob=True)}} +{% endblock %} + +{% block mobile_menu %} +{% endblock %} + +{% block content %} + {% include '_types/root/settings/cache/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/root/settings/cache/index.html b/templates/_types/root/settings/cache/index.html new file mode 100644 index 0000000..05706f8 --- /dev/null +++ b/templates/_types/root/settings/cache/index.html @@ -0,0 +1,20 @@ +{% extends '_types/root/settings/index.html' %} + +{% block root_settings_header_child %} + {% from '_types/root/_n/macros.html' import header with context %} + {% call header() %} + {% from '_types/root/settings/cache/_header.html' import header_row with context %} + {{ header_row() }} +
    + {% block cache_header_child %} + {% endblock %} +
    + {% endcall %} +{% endblock %} + +{% block content %} + {% include '_types/root/settings/cache/_main_panel.html' %} +{% endblock %} + +{% block _main_mobile_menu %} +{% endblock %} diff --git a/templates/_types/root/settings/header/_header.html b/templates/_types/root/settings/header/_header.html new file mode 100644 index 0000000..69e7c72 --- /dev/null +++ b/templates/_types/root/settings/header/_header.html @@ -0,0 +1,11 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='root-settings-row', oob=oob) %} + {% call links.link(url_for('settings.home'), hx_select_search) %} + {{ links.admin() }} + {% endcall %} + {% call links.desktop_nav() %} + {% include '_types/root/settings/_nav.html' %} + {% endcall %} + {% endcall %} +{% endmacro %} \ No newline at end of file diff --git a/templates/_types/root/settings/index.html b/templates/_types/root/settings/index.html new file mode 100644 index 0000000..1773f3d --- /dev/null +++ b/templates/_types/root/settings/index.html @@ -0,0 +1,18 @@ +{% extends '_types/root/_index.html' %} +{% import 'macros/layout.html' as layout %} + +{% block root_header_child %} + {% from '_types/root/_n/macros.html' import index_row with context %} + {% call index_row('root-settings-header-child', '_types/root/settings/header/_header.html') %} + {% block root_settings_header_child %} + {% endblock %} + {% endcall %} +{% endblock %} + +{% block _main_mobile_menu %} + {% include '_types/root/settings/_nav.html' %} +{% endblock %} + +{% block content %} + {% include '_types/root/settings/_main_panel.html' %} +{% endblock %} \ No newline at end of file diff --git a/templates/_types/snippets/_list.html b/templates/_types/snippets/_list.html new file mode 100644 index 0000000..2b982ca --- /dev/null +++ b/templates/_types/snippets/_list.html @@ -0,0 +1,73 @@ +
    + {% if snippets %} +
    + {% for s in snippets %} +
    + {# Name #} +
    +
    {{ s.name }}
    +
    + {% if s.user_id == g.user.id %} + You + {% else %} + User #{{ s.user_id }} + {% endif %} +
    +
    + + {# Visibility badge #} + {% set badge_colours = { + 'private': 'bg-stone-200 text-stone-700', + 'shared': 'bg-blue-100 text-blue-700', + 'admin': 'bg-amber-100 text-amber-700', + } %} + + {{ s.visibility }} + + + {# Admin: inline visibility select #} + {% if is_admin %} + + {% endif %} + + {# Delete button #} + {% if s.user_id == g.user.id or is_admin %} + + {% endif %} +
    + {% endfor %} +
    + {% else %} +
    + +

    No snippets yet. Create one from the blog editor.

    +
    + {% endif %} +
    diff --git a/templates/_types/snippets/_main_panel.html b/templates/_types/snippets/_main_panel.html new file mode 100644 index 0000000..73b50b7 --- /dev/null +++ b/templates/_types/snippets/_main_panel.html @@ -0,0 +1,9 @@ +
    +
    +

    Snippets

    +
    + +
    + {% include '_types/snippets/_list.html' %} +
    +
    diff --git a/templates/_types/snippets/_oob_elements.html b/templates/_types/snippets/_oob_elements.html new file mode 100644 index 0000000..a1377cf --- /dev/null +++ b/templates/_types/snippets/_oob_elements.html @@ -0,0 +1,18 @@ +{% extends 'oob_elements.html' %} + +{# OOB elements for HTMX navigation - all elements that need updating #} + +{% block oobs %} + {% from '_types/root/_n/macros.html' import oob_header with context %} + {{oob_header('root-settings-header-child', 'snippets-header-child', '_types/snippets/header/_header.html')}} + + {% from '_types/root/settings/header/_header.html' import header_row with context %} + {{header_row(oob=True)}} +{% endblock %} + +{% block mobile_menu %} +{% endblock %} + +{% block content %} + {% include '_types/snippets/_main_panel.html' %} +{% endblock %} diff --git a/templates/_types/snippets/header/_header.html b/templates/_types/snippets/header/_header.html new file mode 100644 index 0000000..0882518 --- /dev/null +++ b/templates/_types/snippets/header/_header.html @@ -0,0 +1,9 @@ +{% import 'macros/links.html' as links %} +{% macro header_row(oob=False) %} + {% call links.menu_row(id='snippets-row', oob=oob) %} + {% from 'macros/admin_nav.html' import admin_nav_item %} + {{ admin_nav_item(url_for('snippets.list_snippets'), 'puzzle-piece', 'Snippets', select_colours, aclass='') }} + {% call links.desktop_nav() %} + {% endcall %} + {% endcall %} +{% endmacro %} diff --git a/templates/_types/snippets/index.html b/templates/_types/snippets/index.html new file mode 100644 index 0000000..90f0106 --- /dev/null +++ b/templates/_types/snippets/index.html @@ -0,0 +1,20 @@ +{% extends '_types/root/settings/index.html' %} + +{% block root_settings_header_child %} + {% from '_types/root/_n/macros.html' import header with context %} + {% call header() %} + {% from '_types/snippets/header/_header.html' import header_row with context %} + {{ header_row() }} +
    + {% block snippets_header_child %} + {% endblock %} +
    + {% endcall %} +{% endblock %} + +{% block content %} + {% include '_types/snippets/_main_panel.html' %} +{% endblock %} + +{% block _main_mobile_menu %} +{% endblock %} diff --git a/templates/macros/admin_nav.html b/templates/macros/admin_nav.html new file mode 100644 index 0000000..738a319 --- /dev/null +++ b/templates/macros/admin_nav.html @@ -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) %} + + {{ label }} + {% endcall %} +{% endmacro %} + +{% macro placeholder_nav() %} +{# Placeholder for admin sections without specific nav items #} + +{% endmacro %} diff --git a/templates/macros/scrolling_menu.html b/templates/macros/scrolling_menu.html new file mode 100644 index 0000000..d1a823a --- /dev/null +++ b/templates/macros/scrolling_menu.html @@ -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 #} + + + {# Scrollable container #} +
    +
    + {% for item in items %} +
    + {{ caller(item) }} +
    + {% endfor %} +
    +
    + + + + {# Right scroll arrow - desktop only #} + + {% endif %} +{% endmacro %} diff --git a/templates/macros/stickers.html b/templates/macros/stickers.html new file mode 100644 index 0000000..2be5b9f --- /dev/null +++ b/templates/macros/stickers.html @@ -0,0 +1,24 @@ +{% macro sticker(src, title, enabled, size=40, found=false) -%} + + + + {{ title|capitalize }} + + + + + +{%- endmacro -%} +