diff --git a/app/ui/templates/components/breadcrumbs_v2.html b/app/ui/templates/components/breadcrumbs_v2.html new file mode 100644 index 0000000..9d819a8 --- /dev/null +++ b/app/ui/templates/components/breadcrumbs_v2.html @@ -0,0 +1,38 @@ +{# + UI/UX V2 Phase 1A — Breadcrumb component (INERT UNTIL INCLUDED) + + Expected optional context: + breadcrumbs_v2: [{"label": "Clients", "url": "/clients"}, ...] + breadcrumbs_aria_label_v2: default "Breadcrumb" + + The last item is rendered as the current page even if a URL is supplied. +#} +{% set _crumbs = breadcrumbs_v2|default([], true) %} +{% set _crumb_label = breadcrumbs_aria_label_v2|default('Breadcrumb', true) %} + +{% if _crumbs %} + +{% endif %} diff --git a/app/ui/templates/components/context_bar_v2.html b/app/ui/templates/components/context_bar_v2.html new file mode 100644 index 0000000..355d4c4 --- /dev/null +++ b/app/ui/templates/components/context_bar_v2.html @@ -0,0 +1,16 @@ +{# + UI/UX V2 Phase 1A — Optional page context bar (INERT UNTIL INCLUDED) + + Combines breadcrumbs and page header without touching the current layout. + A later phase can include this wrapper on selected pages after route-level + breadcrumb/back/action context has been supplied explicitly. +#} +{% set _has_breadcrumbs = breadcrumbs_v2|default([], true) %} +{% set _has_page_header = page_title_v2|default('', true) or page_description_v2|default('', true) or back_url_v2|default('', true) or page_actions_v2|default([], true) %} + +{% if _has_breadcrumbs or _has_page_header %} +
+ {% include "ui/templates/components/breadcrumbs_v2.html" %} + {% include "ui/templates/components/page_header_v2.html" %} +
+{% endif %} diff --git a/app/ui/templates/components/mobile_navigation_v2.html b/app/ui/templates/components/mobile_navigation_v2.html new file mode 100644 index 0000000..c1ec0a4 --- /dev/null +++ b/app/ui/templates/components/mobile_navigation_v2.html @@ -0,0 +1,86 @@ +{# + UI/UX V2 Phase 1A — Mobile navigation component (INERT UNTIL INCLUDED) + + Uses native
, so it requires no JavaScript and cannot interfere with + the current application shell. It consumes the same navigation_items_v2 data + as navigation_v2.html. +#} +{% set _mobile_items = navigation_items_v2|default([], true) %} +{% set _mobile_label = navigation_aria_label_v2|default('Mobile navigation', true) %} + +{% if _mobile_items %} +
+
+ + + + + Menu + + {{ _mobile_label }} + + + +
+
+{% endif %} diff --git a/app/ui/templates/components/navigation_v2.html b/app/ui/templates/components/navigation_v2.html new file mode 100644 index 0000000..d9f818d --- /dev/null +++ b/app/ui/templates/components/navigation_v2.html @@ -0,0 +1,80 @@ +{# + UI/UX V2 Phase 1A — Desktop navigation component (INERT UNTIL INCLUDED) + + Expected optional context: + navigation_items_v2: list of mappings with: + label (required), url, active, visible, badge, children + navigation_aria_label_v2: accessible label (default: "Primary navigation") + + The component is deliberately data-driven and contains no role or permission + assumptions. A later role pilot will build navigation_items_v2 from the + existing permission helpers without changing current access rules. +#} +{% set _nav_items = navigation_items_v2|default([], true) %} +{% set _nav_label = navigation_aria_label_v2|default('Primary navigation', true) %} + +{% if _nav_items %} + +{% endif %} diff --git a/app/ui/templates/components/page_header_v2.html b/app/ui/templates/components/page_header_v2.html new file mode 100644 index 0000000..9326b51 --- /dev/null +++ b/app/ui/templates/components/page_header_v2.html @@ -0,0 +1,62 @@ +{# + UI/UX V2 Phase 1A — Page header component (INERT UNTIL INCLUDED) + + Expected optional context: + page_title_v2 (string) + page_description_v2 (string) + back_url_v2 (string) + back_label_v2 (default "Back") + page_actions_v2: list of mappings: + label, url, variant(primary|secondary|danger), visible, external + + This component never discovers, moves, hides or rewrites existing page buttons. +#} +{% set _title = page_title_v2|default('', true) %} +{% set _description = page_description_v2|default('', true) %} +{% set _back_url = back_url_v2|default('', true) %} +{% set _back_label = back_label_v2|default('Back', true) %} +{% set _actions = page_actions_v2|default([], true) %} + +{% if _title or _description or _back_url or _actions %} +
+ {% if _back_url %} +
+ + + {{ _back_label }} + +
+ {% endif %} + +
+
+ {% if _title %} +

{{ _title }}

+ {% endif %} + {% if _description %} +

{{ _description }}

+ {% endif %} +
+ + {% if _actions %} +
+ {% for action in _actions %} + {% if action.get('visible', true) %} + {% set _variant = action.get('variant', 'secondary') %} + {% set _classes = 'bg-brand-600 text-white hover:bg-brand-700 focus-visible:ring-brand-500' if _variant == 'primary' else ('bg-red-600 text-white hover:bg-red-700 focus-visible:ring-red-500' if _variant == 'danger' else 'border border-slate-300 bg-white text-slate-700 hover:bg-slate-50 hover:text-slate-950 focus-visible:ring-brand-500') %} + + {{ action.get('label', '') }} + + {% endif %} + {% endfor %} +
+ {% endif %} +
+
+{% endif %}