Warning, file /swf-monitor/src/templates/base.html was not indexed
or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 {% load static tz %}
0002 <!DOCTYPE html>
0003 <html lang="en">
0004 <head>
0005 <meta charset="UTF-8">
0006 <meta name="viewport" content="width=device-width, initial-scale=1.0">
0007 <title>{% block title %}SWF Monitor{% endblock %}</title>
0008 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
0009 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
0010 <link rel="stylesheet" href="{% static 'css/style.css' %}">
0011 <link rel="stylesheet" href="{% static 'css/state-colors.css' %}">
0012 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css">
0013 <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
0014 <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js"></script>
0015 <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-json.min.js"></script>
0016 <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
0017 <style>
0018 .btn.btn-dark-green {
0019 background-color: #166534; border-color: #166534; color: #fff;
0020 }
0021 .btn.btn-dark-green:hover, .btn.btn-dark-green:focus {
0022 background-color: #14532d; border-color: #14532d; color: #fff;
0023 }
0024 .btn.btn-dark-green:disabled, .btn.btn-dark-green.disabled {
0025 background-color: #6c757d; border-color: #6c757d; color: #e9ecef;
0026 }
0027 #js-error-overlay {
0028 position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
0029 z-index: 100000; max-width: 720px; width: calc(100vw - 2rem);
0030 background: #c9302c; color: #fff; padding: 1.25rem 1.5rem;
0031 border: 3px solid #7a1a18; border-radius: .5rem;
0032 box-shadow: 0 10px 30px rgba(0,0,0,.4);
0033 font-family: -apple-system, "Segoe UI", Roboto, sans-serif;
0034 display: none;
0035 }
0036 #js-error-overlay h3 { margin: 0 0 .5rem; font-size: 1.1rem; }
0037 #js-error-overlay pre {
0038 background: rgba(0,0,0,.25); color: #fff; padding: .5rem .75rem;
0039 border-radius: .25rem; margin: .5rem 0 0; font-size: .8rem;
0040 white-space: pre-wrap; word-break: break-word; max-height: 40vh; overflow: auto;
0041 }
0042 #js-error-overlay .close-btn {
0043 position: absolute; top: .5rem; right: .75rem;
0044 background: transparent; border: none; color: #fff; font-size: 1.5rem;
0045 cursor: pointer; line-height: 1;
0046 }
0047 nav {
0048 display: flex;
0049 align-items: center;
0050 gap: 1em;
0051 }
0052 .nav-spacer {
0053 flex: 1 1 auto;
0054 }
0055 .nav-auth {
0056 display: flex;
0057 gap: 1em;
0058 }
0059 /* Dropdown Button */
0060 .dropbtn {
0061 background-color: inherit;
0062 color: #ADD8E6; /* Light Blue */
0063 text-decoration: none;
0064 padding: 0;
0065 font-size: inherit;
0066 border: none;
0067 cursor: pointer;
0068 }
0069
0070 /* The container <div> - needed to position the dropdown content */
0071 .dropdown {
0072 position: relative;
0073 display: inline-block;
0074 margin-right: 1em;
0075 }
0076
0077 /* Dropdown Content (Hidden by Default) */
0078 .dropdown-content {
0079 display: none;
0080 position: absolute;
0081 background-color: #f9f9f9;
0082 min-width: 160px;
0083 box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
0084 z-index: 1;
0085 }
0086
0087 /* Links inside the dropdown */
0088 .dropdown-content a {
0089 color: black;
0090 padding: 12px 16px;
0091 text-decoration: none;
0092 display: block;
0093 }
0094
0095 /* Change color of dropdown links on hover */
0096 .dropdown-content a:hover {background-color: #f1f1f1}
0097
0098 /* Show the dropdown menu on hover */
0099 .dropdown:hover .dropdown-content {
0100 display: block;
0101 }
0102 /* Nav mode flipper */
0103 .nav-mode { display: none; }
0104 .nav-mode.active { display: contents; }
0105 #mode-label { border-right: 1px solid rgba(255,255,255,.2); padding-right: 12px; margin-right: 4px; }
0106 /* One-click copy icon for IDs — see swf_fmt.copy_btn template tag */
0107 .copy-btn {
0108 background: none; border: none; color: #6c757d; cursor: pointer;
0109 padding: 0 .25rem; font-size: .85em; line-height: 1; vertical-align: baseline;
0110 }
0111 .copy-btn:hover { color: #0d6efd; }
0112 .copy-btn.copied { color: #198754; }
0113 </style>
0114 </head>
0115 <body>
0116 <div id="js-error-overlay" role="alert">
0117 <button class="close-btn" onclick="document.getElementById('js-error-overlay').style.display='none';">×</button>
0118 <h3>JavaScript error</h3>
0119 <div id="js-error-msg"></div>
0120 <pre id="js-error-detail"></pre>
0121 </div>
0122 <script>
0123 (function() {
0124 function show(title, detail) {
0125 var box = document.getElementById('js-error-overlay');
0126 var msg = document.getElementById('js-error-msg');
0127 var det = document.getElementById('js-error-detail');
0128 if (!box || !msg || !det) return;
0129 msg.textContent = title || 'Error';
0130 det.textContent = detail || '';
0131 box.style.display = 'block';
0132 }
0133 window.addEventListener('error', function(ev) {
0134 var err = ev.error;
0135 var loc = (ev.filename || '') + (ev.lineno ? ':' + ev.lineno + ':' + (ev.colno || 0) : '');
0136 var stack = err && err.stack ? err.stack : '';
0137 show(ev.message || 'Script error', (loc ? loc + '\n\n' : '') + stack);
0138 });
0139 window.addEventListener('unhandledrejection', function(ev) {
0140 var r = ev.reason;
0141 var msg = (r && r.message) ? r.message : String(r);
0142 var stack = (r && r.stack) ? r.stack : '';
0143 show('Unhandled promise rejection: ' + msg, stack);
0144 });
0145 })();
0146 // One-click copy-to-clipboard handler for .copy-btn buttons rendered by
0147 // the copy_btn template tag (swf_fmt). Swaps the icon to a checkmark for
0148 // 1s on success so the user sees the click landed.
0149 document.addEventListener('click', function(ev) {
0150 var btn = ev.target.closest('.copy-btn');
0151 if (!btn) return;
0152 ev.preventDefault();
0153 var text = btn.getAttribute('data-copy') || '';
0154 if (!text || !navigator.clipboard) return;
0155 navigator.clipboard.writeText(text).then(function() {
0156 var icon = btn.querySelector('i');
0157 if (!icon) return;
0158 var prev = icon.className;
0159 icon.className = 'bi bi-clipboard-check';
0160 btn.classList.add('copied');
0161 setTimeout(function() {
0162 icon.className = prev;
0163 btn.classList.remove('copied');
0164 }, 1000);
0165 });
0166 });
0167 // Near-zero-latency tooltips via Bootstrap 5 for elements with title=.
0168 // Scoped to state-filled cells and the copy button so we don't spin up
0169 // a tooltip for every cell on every page. show delay 50ms, hide 0ms.
0170 document.addEventListener('DOMContentLoaded', function() {
0171 if (!window.bootstrap || !bootstrap.Tooltip) return;
0172 function init(root) {
0173 root.querySelectorAll(
0174 'td[title]:not([data-bs-tooltip-init]), .copy-btn[title]:not([data-bs-tooltip-init])'
0175 ).forEach(function(el) {
0176 if (!el.getAttribute('title')) return;
0177 el.setAttribute('data-bs-tooltip-init', '1');
0178 new bootstrap.Tooltip(el, {delay: {show: 50, hide: 0}});
0179 });
0180 }
0181 init(document);
0182 // DataTables redraws replace rows; re-init after each draw.
0183 if (window.jQuery) {
0184 jQuery(document).on('draw.dt', function(_, settings) {
0185 init(settings.nTable || document);
0186 });
0187 }
0188 });
0189 </script>
0190 <nav>
0191 <div class="dropdown" id="mode-flipper">
0192 <button class="dropbtn" id="mode-label" style="font-weight:700;"></button>
0193 <div class="dropdown-content">
0194 <a href="{% url 'monitor_app:prod_hub' %}" id="flip-production" onclick="setNavMode('production');">ePIC Production</a>
0195 <a href="{% if is_tunnel %}https://pandaserver02.sdcc.bnl.gov/swf-monitor/testbed/{% else %}{% url 'monitor_app:testbed_hub' %}{% endif %}" id="flip-testbed" onclick="setNavMode('testbed');">ePIC Testbed</a>
0196 </div>
0197 </div>
0198
0199 {# ── Testbed mode ── #}
0200 <span class="nav-mode nav-testbed">
0201 <div class="dropdown">
0202 <button class="dropbtn">Workflows</button>
0203 <div class="dropdown-content">
0204 <a href="{% url 'monitor_app:workflows_home' %}">Views</a>
0205 <a href="{% url 'monitor_app:workflow_definitions_list' %}">Workflow definitions</a>
0206 <a href="{% url 'monitor_app:workflow_executions_list' %}">Workflow executions</a>
0207 <a href="{% url 'monitor_app:namespaces_list' %}">Namespaces</a>
0208 </div>
0209 </div>
0210 <div class="dropdown">
0211 <button class="dropbtn">Files</button>
0212 <div class="dropdown-content">
0213 <a href="{% url 'monitor_app:stf_files_list' %}">STF Files</a>
0214 <a href="{% url 'monitor_app:fastmon_files_list' %}">STF Samples</a>
0215 <a href="{% url 'monitor_app:tf_slices_list' %}">TF Slices</a>
0216 </div>
0217 </div>
0218 <a href="{% url 'monitor_app:workflow_agents_list' %}">Agents</a>
0219 <a href="{% url 'monitor_app:subscribers_list' %}">Subscribers</a>
0220 <a href="{% url 'monitor_app:workflow_messages' %}">Messages</a>
0221 <a href="{% url 'monitor_app:log_summary' %}">Logs</a>
0222 <a href="{% url 'monitor_app:database_tables_list' %}">Database</a>
0223 <a href="{% url 'monitor_app:persistent_state' %}">State</a>
0224 <div class="dropdown">
0225 <button class="dropbtn">PanDA/Rucio</button>
0226 <div class="dropdown-content">
0227 <a href="{% url 'monitor_app:panda_hub' %}">PanDA/Rucio Hub</a>
0228 <a href="{% url 'monitor_app:panda_activity' %}">Activity</a>
0229 <a href="{% url 'monitor_app:panda_jobs_list' %}">Jobs</a>
0230 <a href="{% url 'monitor_app:panda_tasks_list' %}">Tasks</a>
0231 <a href="{% url 'monitor_app:panda_errors_list' %}">Errors</a>
0232 <a href="{% url 'monitor_app:panda_diagnostics_list' %}">Diagnostics</a>
0233 <a href="{% url 'monitor_app:epic_queues_list' %}">EIC PanDA Queues</a>
0234 <a href="{% url 'monitor_app:panda_database_tables_list' %}">PanDA Database</a>
0235 <a href="{% url 'monitor_app:idds_database_tables_list' %}">iDDS Database</a>
0236 <a href="{% url 'monitor_app:rucio_endpoints_list' %}">Rucio Endpoints</a>
0237 </div>
0238 </div>
0239 </span>
0240
0241 {# ── Production mode ── #}
0242 <span class="nav-mode nav-production">
0243 <a href="{% url 'monitor_app:panda_activity' %}">Activity</a>
0244 <a href="{% url 'monitor_app:panda_jobs_list' %}">Jobs</a>
0245 <div class="dropdown">
0246 <button class="dropbtn">Tasks</button>
0247 <div class="dropdown-content">
0248 <a href="{% url 'monitor_app:panda_tasks_list' %}">PanDA tasks</a>
0249 <a href="{% url 'pcs:pcs_catalog' %}">Production Task Catalog</a>
0250 </div>
0251 </div>
0252 <a href="{% url 'monitor_app:panda_errors_list' %}">Errors</a>
0253 <a href="{% url 'monitor_app:panda_diagnostics_list' %}">Diagnostics</a>
0254 <a href="{% url 'monitor_app:epic_queues_list' %}">Queues</a>
0255 <div class="dropdown">
0256 <button class="dropbtn">PCS</button>
0257 <div class="dropdown-content">
0258 <a href="{% url 'pcs:pcs_hub' %}">PCS Hub</a>
0259 <a href="{% url 'pcs:tag_compose' tag_type='p' %}">Physics Tags</a>
0260 <a href="{% url 'pcs:tag_compose' tag_type='e' %}">EvGen Tags</a>
0261 <a href="{% url 'pcs:tag_compose' tag_type='s' %}">Simu Tags</a>
0262 <a href="{% url 'pcs:tag_compose' tag_type='r' %}">Reco Tags</a>
0263 <a href="{% url 'pcs:datasets_compose' %}">Datasets</a>
0264 <a href="{% url 'pcs:prod_configs_compose' %}">Prod Configs</a>
0265 <a href="{% url 'pcs:prod_task_compose' %}?tab=tasks">Prod Tasks</a>
0266 </div>
0267 </div>
0268 </span>
0269 <span class="nav-spacer"></span>
0270 <a href="{% url 'monitor_app:about' %}">About</a>
0271 <div class="nav-auth">
0272 {% if user.is_authenticated %}
0273 {% if user.is_staff %}
0274 <a href="{% url 'admin:index' %}">Admin</a>
0275 {% endif %}
0276 <a href="{% url 'monitor_app:account' %}">Account</a>
0277 <form method="post" action="{% url 'logout' %}" style="display:inline;margin:0;">{% csrf_token %}<button type="submit" style="background:none;border:none;color:inherit;cursor:pointer;padding:0;font:inherit;">Logout</button></form>
0278 {% else %}
0279 <a href="{% url 'login' %}">Login</a>
0280 {% endif %}
0281 </div>
0282 </nav>
0283 {% timezone "America/New_York" %}
0284 <div class="swf-time-banner" style="display:flex;justify-content:flex-end;gap:1.25em;padding:.25em 1em;font-size:.8em;color:#666;background:#f8f9fa;border-bottom:1px solid #e5e7eb;">
0285 <span>Times in {% now "T" %}</span>
0286 <span>Built at {% now "Ymd H:i:s" %}</span>
0287 </div>
0288 {% endtimezone %}
0289 <main>
0290 {% if messages %}
0291 {% for message in messages %}
0292 <div class="alert alert-{{ message.tags|default:'info' }} alert-dismissible fade show w-100 mb-0 rounded-0" role="alert"
0293 style="background-color: #e9ecef; border: none; border-bottom: 1px solid #dee2e6; padding: 20px;">
0294 {% if 'safe' in message.tags %}
0295 {{ message|safe }}
0296 {% else %}
0297 {{ message }}
0298 {% endif %}
0299 <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
0300 </div>
0301 {% endfor %}
0302 {% endif %}
0303 {% block content %}{% endblock %}
0304 </main>
0305 <script>
0306 function setNavMode(mode) {
0307 localStorage.setItem('navMode', mode);
0308 applyNavMode(mode);
0309 }
0310 function applyNavMode(mode) {
0311 document.querySelectorAll('.nav-mode').forEach(el => el.classList.remove('active'));
0312 const target = document.querySelector('.nav-' + mode);
0313 if (target) target.classList.add('active');
0314 const label = document.getElementById('mode-label');
0315 if (label) label.textContent = mode === 'testbed' ? 'ePIC Testbed' : 'ePIC Production';
0316 const ft = document.getElementById('flip-testbed');
0317 const fp = document.getElementById('flip-production');
0318 if (ft) ft.style.fontWeight = mode === 'testbed' ? '700' : 'normal';
0319 if (fp) fp.style.fontWeight = mode === 'production' ? '700' : 'normal';
0320 }
0321 (function() {
0322 var mode = localStorage.getItem('navMode') || 'production';
0323 applyNavMode(mode);
0324 })();
0325 </script>
0326 </body>
0327 </html>