{% extends "base.html" %} {% from "macros/_stack_card.html" import card %} {% block title %}Data Packages — {{ config.INSTANCE_NAME or 'AI Data Analyst' }}{% endblock %} {% block content %} {# Hero — gradient banner mirroring marketplace.html .mp-hero so /catalog and /marketplace read as one product. Eyebrow + h1 + sub layered on the same blue gradient, plus an embedded search row identical to the one on /marketplace. The hero search is wired to the same JS that drives the per-card filter below, so typing here also narrows the grid. #}
Data

What data are you looking for?

Browse curated data packages your team has approved. Add the ones you need to your stack — required packages are added automatically.

{# v51 lifecycle status filter — show / hide cards by per-package ``data-status``. Default opt-in: Prod + POC checked (the analyst- ready buckets). Coming-soon stays unchecked so the catalog isn't polluted with placeholders. Empty selection = no filter (show all). #}
Show: {% if user.is_admin %} {% endif %}
{# Tab strip + right-of-tabs actions row (marketplace .mp-tabs-row parity). Section order locked to match /marketplace exactly: hero → tabs-row (+ admin actions) → curator-block → filter pills → grid → empty state. The pre-v50 ordering (curator-block before tabs) read as a content banner squatting above the navigation; marketplace's order makes the curator block feel like a trust stamp on the active tab, not a header. Icons mirror marketplace's tab icons: layout-grid for Browse; rectangle-stack for My Stack. #}
{# v53 Recipes tab — query templates analysts copy + adapt. Sibling concept to Data Packages but not stack-subscribable. Count hydrates on first visit; starts empty (--) until then. #}
{% if user.is_admin %}
+ New Data Package admin-only
{% endif %}
{# Curator-style info block — mirrors marketplace .mp-curator-block. Sits BELOW the tab strip per the unified section order — same position marketplace.html uses (marketplace.html:497). #} {# Filter chips — All / Required / Available / In stack + per-source-type chips. #}
{% if source_type_chips %} Source: {% for st in source_type_chips %} {% endfor %} {% endif %}
{# Browse view — every package the user has any grant on. #}
Each Data Package is curated by your admin.
Packages bundle related tables on a single topic (Sales, Finance, …) so analysts can pull a coherent dataset with one click. Required packages are added to your stack automatically.
{% if entries %}
{% for entry in entries %} {{ card(entry) }} {% endfor %}
{% else %} {# Empty state — friendly + actionable. Admins get a concrete CTA (open /admin/tables with the N-registered-tables hint so they know there IS data to package). Non-admins get a different copy because the admin path is gated. #}
📦

No Data Packages yet

{% if user.is_admin %} {% if total_registered_tables and total_registered_tables > 0 %}

You have {{ total_registered_tables }} table{{ 's' if total_registered_tables != 1 else '' }} registered in /admin/tables but no Data Packages grouping them.

Create your first package by editing any table and assigning it to a new package — analysts opt-in to the package as a whole.

{% else %}

No tables registered yet either. Register your first source table, then bundle related tables into a Data Package so analysts can opt-in to them as a group.

{% endif %} {% else %}

Ask your admin to create a Data Package or grant your group access to an existing one, then refresh this page.

{% endif %}
{% endif %}
{# My Stack view — required + subscribed. Grid + empty-state are both rendered so the optimistic add/remove JS can swap visibility without having to inject DOM that wasn't in the initial render. #} {# v53 Recipes view — populated client-side on first tab open from /api/recipes so the page render isn't blocked by a second DB query when most users land on Browse. Card-grid uses the same .stack-grid class for visual parity, but recipe cards are simpler (no Add-to-stack, no Required/in-stack badges — recipes aren't subscribable). #} {% if user.is_admin %} {# v53 Create Recipe modal — opened from the "+ New Recipe" admin button in the tab strip. Single-step (no inline RBAC matrix yet — recipes are visible to any authenticated user). #} {# v53 Edit Recipe modal — pre-fills from GET /api/admin/recipes/{id}. Slug is locked (it's the stable URL identifier referenced by /catalog/r/ + any pinned links). Delete lives on the card footer, not in the modal, so admins can drop a recipe without opening the editor. #} {% endif %} {# Toast for ephemeral feedback (Add/Remove success/failure). #}
{% endblock %}