agnes-the-ai-analyst/app/web/templates/base.html
ZdenekSrotyr 9e948abc9c
release(0.54.18): Curated Memory restructure + per-user Dismiss + bundled adversarial-review fixes (#316/#320/#322) (#324)
* feat(web): Curated Memory restructure + per-user Dismiss + filter-state utility

Squashed from cvrysanek/zsrotyr's 4-commit PR branch + rebased onto
current main + CHANGELOG bullets spliced into [Unreleased] (preserves
existing #316/#320/#322 entries that landed on main since the branch
was authored).

Routes + access:
- /corporate-memory now user-facing (get_current_user), in primary
  nav next to "Data Packages" — same gate as /api/memory/*.
- /admin/corporate-memory is the new admin review queue location
  (was /corporate-memory/admin); reached via Admin dropdown. Template
  renamed: corporate_memory_admin.html → admin_corporate_memory.html.

Visual chrome:
- Both pages migrate to shared _page_hero.html blue hero band.

Per-user Dismiss (new feature, schema v46):
- knowledge_item_user_dismissed(user_id, item_id, dismissed_at) + index.
- POST /api/memory/{id}/dismiss + DELETE (idempotent).
- Mandatory items can never be dismissed — enforced at 2 layers.
- GET /api/memory: hide_dismissed=false default + dismissed_by_me flag.
- GET /api/memory/bundle: always excludes dismissed for the caller.
- UI: Dismiss/Undismiss button per item (hidden for mandatory),
  gray-out + line-through for dismissed rows, Hide-dismissed toggle.

Admin edit modal:
- Category as <select> + "Add new category…" reveal.
- Audience as <select> with (unset)/all/group:<name> from RBAC.
- Tags: full tag-input widget (pills, ×-remove, Backspace pop,
  Enter/comma to add, ↑/↓ typeahead from EXISTING_TAGS).

Bulk-edit modal pickers (closes #128):
- Move-to-category / Add-tag: <select> + add-new.
- Set-audience: <select> (no more typo-able 'gourp:eng').
- Remove-tag: closed-set picker.

FilterState utility:
- app/web/static/js/filter-state.js — save/load/clear/bindInputs
  for per-page localStorage filter state. Adopted on /corporate-memory.

E2E verified live on a real VM through the API + browser flow.

* release: 0.54.18 — Curated Memory restructure + 4 adversarial-review fixes

Bundles together:
- #316  fix(store): surface review failures + harden publish gate
        (BREAKING fail-CLOSED guardrail, override v2+ promote, restore guard,
        retry/rescan staged-bundle, banner widening, LLM truncation retry)
- #320  fix(store): C2 bundle export RBAC + H2 per-entity write lock +
        H3 update_status compare-and-swap with bg_verdict_skipped audit
- #322  fix(store): M1 prompt sentinel filename escape + M2 atomic
        promote_to_version helper + L1 admin forensic download per-version
- #324  Curated Memory restructure + per-user Dismiss + FilterState utility

Bump from 0.54.17 → 0.54.18 (patch — pre-1.0 policy: every cycle is patch).
2026-05-15 18:51:05 +02:00

47 lines
1.7 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Data Analyst Portal{% endblock %}</title>
<link rel="stylesheet" href="{{ static_url('style-custom.css') }}">
{# app.js loaded by _app_header.html itself so pages that include
the header directly without extending base.html (e.g. admin_tables)
still get the nav-dropdown wiring. #}
{% block head_extra %}{% endblock %}
{% include '_theme.html' %}
</head>
<body {% block body_attrs %}{% endblock %}>
{% include '_app_header.html' %}
{# `layout` block: pages opt out of the narrow .container wrap by overriding
this entire block (e.g. dashboard.html renders its own full-width <main
class="main">). Pages that want the standard chrome leave it alone and
fill `content` instead. #}
{% block layout %}
<div class="container">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="flash-messages">
{% for category, message in messages %}
<div class="flash flash-{{ category }}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>&copy; {{ now().year if now is defined else 2024 }} {{ config.INSTANCE_COPYRIGHT or 'AI Data Analyst' }}</p>
</footer>
</div>
{% endblock %}
{% include "_version_badge.html" %}
{% block scripts %}{% endblock %}
</body>
</html>