{% import "site_shell/_macros.html" as ui %} {# Fragment-style base for /projects/* pages. Pages that extend this template render as a content fragment injected into the main slot by render_shell_document(content_html=...) -- see projects/routes.py::_wrap_projects_handler_in_shell. The shell owns the surrounding chrome (header, sidebar, layout, theme); this fragment contributes only the page-specific body, the per-project sub-navigation column, any extra CSS, and any extra JS. The legacy standalone chrome (own //, the navy-gradient app header, the fixed global sidebar, the logout form, and the Vue / bootstrap-vue script stack) is intentionally gone -- the site shell provides all of it. What remains of the old sidebar is the per-project sub-navigation, kept as an in-content left column: once a project is selected it gives the 15 project-scoped destinations a stable home without competing with the shell's global sidebar. Child templates still declare {% raw %}{% block title %}{% endraw %} blocks; they are intentionally never rendered here -- the shell document's comes from the wrapper's page-title table. Colors reference the --pyc-* design tokens (with light-palette fallbacks) from /static/styles/pyc-tokens.css, which the shell document links, so this section follows light / dark automatically. Every selector is scoped under .projects-fragment so nothing leaks into the shell chrome. #} <section class="pyc-admin-page-fragment projects-fragment"> {#- The child template's title block feeds the shell document's <title>; the shell wrapper reads and removes this element. #} <meta name="pyc-page-title" content="{% block title %}{% endblock %}"> {% block extra_css %}{% endblock %} {% if flashes %} <div class="pyc-flashes" role="status" aria-live="polite"> {% for category, message in flashes %} <div class="alert alert-{{ category }}">{{ message }}</div> {% endfor %} </div> {% endif %} <div class="pyc-subnav-layout"> {% if project is defined and project %} <nav class="pyc-subnav" aria-label="Project navigation"> <ul> {#- The same two names and addresses as the sidebar's Projects group (PRJ-27). #} <li><a href="{{ url_for('projects.dashboard') }}" {% if request_endpoint == 'projects.dashboard' %}class="active" aria-current="page"{% endif %}> {{ ui.icon("dashboard") }} Projects Dashboard </a></li> <li><a href="{{ url_for('projects.list_root') }}" {% if request_endpoint in ('projects.project_list', 'projects.project_create') %}class="active" aria-current="page"{% endif %}> {{ ui.icon("folder") }} All Projects </a></li> <li class="pyc-subnav__section">{{ project.name }}</li> <li><a href="{{ url_for('projects.project_detail', project_id=project.id) }}" {% if request_endpoint in ('projects.project_detail', 'projects.project_edit') %}class="active" aria-current="page"{% endif %}> {{ ui.icon("info") }} Overview </a></li> <li><a href="{{ url_for('projects.member_list', project_id=project.id) }}" {% if request_endpoint.startswith('projects.member_') %}class="active" aria-current="page"{% endif %}> {{ ui.icon("people") }} Members </a></li> <li><a href="{{ url_for('projects.repository_list', project_id=project.id) }}" {% if request_endpoint.startswith('projects.repository_') %}class="active" aria-current="page"{% endif %}> {{ ui.icon("source") }} Repositories </a></li> <li class="pyc-subnav__section">CI/CD</li> {% if ui_profile.is_enabled('build') or request_endpoint.startswith('projects.build_') %} <li><a href="{{ url_for('projects.project_workspace_link', project_id=project.id, destination='builds') }}" {% if request_endpoint.startswith('projects.build_') %}class="active" aria-current="page"{% endif %}> {{ ui.icon("build") }} Builds </a></li> {% endif %} {#- Runs, Pipelines and the Test catalog are where this project's work is defined and listed; they open the site-wide pages filtered to the project. The project model's own Infrastructure, Tests, Pipelines, Channels, Schedules and Calendar sections cannot hold data (nothing creates it), so they are not listed here; their addresses still open, with a Preview notice (PRJ-08). #} {% if ui_profile.is_enabled('build') or ui_profile.is_enabled('testing') or ui_profile.is_enabled('rpa') %} <li><a href="{{ url_for('projects.project_workspace_link', project_id=project.id, destination='runs') }}"> {{ ui.icon("play_arrow") }} Runs </a></li> {% endif %} {% if ui_profile.is_enabled('build') %} <li><a href="{{ url_for('projects.project_workspace_link', project_id=project.id, destination='pipelines') }}"> {{ ui.icon("account_tree") }} Pipelines </a></li> {% endif %} {% if ui_profile.is_enabled('testing') %} <li><a href="{{ url_for('projects.project_workspace_link', project_id=project.id, destination='test-catalog') }}"> {{ ui.icon("science") }} Test catalog </a></li> {% endif %} <li class="pyc-subnav__section">Releases</li> <li><a href="{{ url_for('projects.release_list', project_id=project.id) }}" {% if request_endpoint in ('projects.release_list', 'projects.release_detail') %}class="active" aria-current="page"{% endif %}> {{ ui.icon("new_releases") }} Releases </a></li> <li><a href="{{ url_for('projects.release_template_list', project_id=project.id) }}" {% if request_endpoint.startswith('projects.release_template_') %}class="active" aria-current="page"{% endif %}> {{ ui.icon("layers") }} Release Templates </a></li> <li class="pyc-subnav__section">Automation</li> {% if ui_profile.is_enabled('rpa') or request_endpoint.startswith('projects.rpa_') %} <li><a href="{{ url_for('projects.rpa_list', project_id=project.id) }}" {% if request_endpoint.startswith('projects.rpa_') %}class="active" aria-current="page"{% endif %}> {{ ui.icon("assignment") }} RPA </a></li> {% endif %} <li><a href="{{ url_for('projects.watch_list', project_id=project.id) }}" {% if request_endpoint.startswith('projects.watch_') %}class="active" aria-current="page"{% endif %}> {{ ui.icon("visibility") }} Watches </a></li> </ul> </nav> {% endif %} <div class="pyc-subnav-layout__content"> {% set _hidden_surface = none %} {% if request_endpoint.startswith('projects.build_') and not ui_profile.is_enabled('build') %}{% set _hidden_surface = 'Build Automation' %} {% elif request_endpoint.startswith('projects.infra_') and not ui_profile.is_enabled('infra') %}{% set _hidden_surface = 'Infrastructure' %} {% elif request_endpoint.startswith('projects.test_') and not ui_profile.is_enabled('testing') %}{% set _hidden_surface = 'Testing Automation' %} {% elif request_endpoint.startswith('projects.rpa_') and not ui_profile.is_enabled('rpa') %}{% set _hidden_surface = 'Robotic Process Automation' %} {% endif %} {% if _hidden_surface %} <div class="pyc-hidden-surface-notice pyc-hidden-surface-notice--inline" role="status"> This area belongs to the {{ _hidden_surface }} surface, which is hidden from your navigation. <a href="{{ url_for('config.account_preferences') }}">Change your preferences</a> </div> {% endif %} {#- This project's own build list is retired: nothing in this version picks up a build made here (PRJ-07). #} {% if request_endpoint.startswith('projects.build_') and project is defined and project %} {% call ui.preview_notice() %} This is the project’s older build list. A build started here is not run — nothing in this version picks it up, so it waits. Start and follow builds on the <a href="{{ url_for('projects.project_workspace_link', project_id=project.id, destination='builds') }}">Builds page</a>, which shows this project’s builds. {% endcall %} {% endif %} {% block content %}{% endblock %} </div> </div> {% block extra_js %}{% endblock %} </section>