feat(dashboard): breadcrumbs that follow how you reached a page

Detail pages carry a breadcrumb bar built from a per-tab trail in
sessionStorage: following a link from a page on the trail extends it,
going back to one cuts it there, and opening a page directly shows its
natural parents. The property 3D view's back button goes to the previous
crumb, and the property page renames its crumb once its name loads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-27 03:43:21 -05:00
parent 370a03f0a3
commit 75e4fcd6cc
8 changed files with 89 additions and 14 deletions

View File

@@ -3,9 +3,7 @@
{% block title %}Account #{{ account.id }} - DarkflameServer{% endblock %}
{% block content %}
{% if can.accounts_view %}
<a href="/accounts" class="text-decoration-none mb-3 d-inline-block">&larr; Back to Accounts</a>
{% endif %}
<nav data-crumbs data-crumb-label="{{ account.name }}" aria-label="Breadcrumb"><ol class="breadcrumb mb-3">{% if can.accounts_view %}<li class="breadcrumb-item"><a href="/accounts">Accounts</a></li>{% endif %}<li class="breadcrumb-item active" aria-current="page">{{ account.name }}</li></ol></nav>
<div class="card mb-4" id="account" data-account-id="{{ account.id }}" data-gm-level="{{ account.gm_level }}" data-email="{{ account.email }}" data-account-name="{{ account.name }}" data-self="{% if is_self %}1{% endif %}" data-can-moderate="{% if manage.moderation %}1{% endif %}">
<div class="card-header d-flex justify-content-between align-items-center">

View File

@@ -3,7 +3,7 @@
{% block title %}Bug Report #{{ report.id }} - DarkflameServer{% endblock %}
{% block content %}
<a href="/bug_reports" class="text-decoration-none mb-3 d-inline-block">&larr; Back to Bug Reports</a>
<nav data-crumbs data-crumb-label="Bug Report #{{ report.id }}" aria-label="Breadcrumb"><ol class="breadcrumb mb-3"><li class="breadcrumb-item"><a href="/bug_reports">Bug Reports</a></li><li class="breadcrumb-item active" aria-current="page">Bug Report #{{ report.id }}</li></ol></nav>
<div class="card mb-4" id="report" data-report-id="{{ report.id }}">
<div class="card-header d-flex justify-content-between align-items-center">

View File

@@ -42,11 +42,7 @@
{% endblock %}
{% block content %}
{% if can.characters_view %}
<a href="/characters" class="text-decoration-none mb-3 d-inline-block">&larr; Back to Characters</a>
{% else %}
<a href="/" class="text-decoration-none mb-3 d-inline-block">&larr; Back to your characters</a>
{% endif %}
<nav data-crumbs data-crumb-label="{{ character.name }}" aria-label="Breadcrumb"><ol class="breadcrumb mb-3">{% if can.characters_view %}<li class="breadcrumb-item"><a href="/characters">Characters</a></li>{% else %}<li class="breadcrumb-item"><a href="/">Your characters</a></li>{% endif %}<li class="breadcrumb-item active" aria-current="page">{{ character.name }}</li></ol></nav>
<div class="row g-4">
<div class="col-lg-4">

View File

@@ -3,7 +3,7 @@
{% block title %}Play Key #{{ key.id }} - DarkflameServer{% endblock %}
{% block content %}
<a href="/play_keys" class="text-decoration-none mb-3 d-inline-block">&larr; Back to Play Keys</a>
<nav data-crumbs data-crumb-label="Play Key #{{ key.id }}" aria-label="Breadcrumb"><ol class="breadcrumb mb-3"><li class="breadcrumb-item"><a href="/play_keys">Play Keys</a></li><li class="breadcrumb-item active" aria-current="page">Play Key #{{ key.id }}</li></ol></nav>
<div class="row g-4">
<div class="col-lg-5">

View File

@@ -23,9 +23,10 @@
{% block content %}
{# Also the showcase's read-only view of approved public properties (Showcase.cpp passes showcase) #}
{% if not exists("showcase") %}<nav data-crumbs data-crumb-label="3D view" aria-label="Breadcrumb"><ol class="breadcrumb mb-2">{% if can.properties_view %}<li class="breadcrumb-item"><a href="/properties">Properties</a></li>{% endif %}<li class="breadcrumb-item"><a href="/properties/{{ property_id }}">Property {{ property_id }}</a></li><li class="breadcrumb-item active" aria-current="page">3D view</li></ol></nav>{% endif %}
<div id="property" data-property-id="{{ property_id }}" data-behavior-rules="{{ behavior_rules }}" data-showcase="{% if exists("showcase") %}1{% endif %}">
<div class="d-flex flex-wrap align-items-center gap-2 mb-2">
{% if exists("showcase") %}<a href="/showcase" class="btn btn-sm btn-outline-secondary">&larr; Showcase</a>{% else %}<a href="/properties/{{ property_id }}" class="btn btn-sm btn-outline-secondary">&larr; Property</a>{% endif %}
{% if exists("showcase") %}<a href="/showcase" class="btn btn-sm btn-outline-secondary">&larr; Showcase</a>{% else %}<a href="/properties/{{ property_id }}" class="btn btn-sm btn-outline-secondary" data-crumb-back>&larr; Property</a>{% endif %}
<div class="me-auto">
<h5 class="mb-0 d-inline" id="propName">Loading&hellip;</h5>
<span class="small text-body-secondary ms-2" id="propMeta"></span>

View File

@@ -3,9 +3,7 @@
{% block title %}Property - DarkflameServer{% endblock %}
{% block content %}
{% if can.properties_view %}
<a href="/properties" class="text-decoration-none mb-3 d-inline-block">&larr; Back to Properties</a>
{% endif %}
<nav data-crumbs data-crumb-label="Property {{ property_id }}" aria-label="Breadcrumb"><ol class="breadcrumb mb-3">{% if can.properties_view %}<li class="breadcrumb-item"><a href="/properties">Properties</a></li>{% endif %}<li class="breadcrumb-item active" aria-current="page">Property {{ property_id }}</li></ol></nav>
<div id="property" data-property-id="{{ property_id }}">
<div class="card mb-4">
@@ -189,6 +187,7 @@ function load() {
if (p.success === false) { setText('propName', p.error); return; }
loaded = p;
setText('propName', p.name || 'Unnamed property');
Crumbs.label(p.name || 'Unnamed property');
// This property's own events and statistics (each property is its own instance of the zone, told apart by the clone)
const economy = document.getElementById('propEconomy');
if (economy && p.clone_id) {