Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-28 07:24:56

0001 {% extends 'base.html' %}
0002 {% load static %}
0003 {% load swf_fmt %}
0004 
0005 {% block title %}Edit team {{ team_name }} — Alarms{% endblock %}
0006 
0007 {% block content %}
0008 <link rel="stylesheet" href="{% static 'css/state-colors.css' %}">
0009 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/codemirror.min.css">
0010 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/theme/material-darker.min.css">
0011 <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/codemirror.min.js"></script>
0012 
0013 <style>
0014   .CodeMirror { height: auto !important; border: 1px solid #333; font-family: "SF Mono", Monaco, Menlo, monospace; font-size: 13px; }
0015   .CodeMirror-scroll { min-height: 260px; max-height: 60vh; }
0016   .autosave-indicator { font-size: 12px; color: #888; margin-left: 1em; }
0017   .autosave-indicator.saving { color: #daa520; }
0018   .autosave-indicator.saved  { color: #4CAF50; }
0019   .autosave-indicator.error  { color: #dc3545; }
0020   .version-row td { padding: 4px 8px; font-size: 12.5px; border-top: 1px solid #2a2a2a; }
0021   .version-row .vnum { color: #daa520; white-space: nowrap; }
0022   .version-row .vts { color: #8a8a8a; white-space: nowrap; }
0023   .version-row .vwho { color: #8a8a8a; white-space: nowrap; }
0024   .local-backup-row td { background: #2a2200 !important; color: #ff9800; }
0025 </style>
0026 
0027 <div class="container mt-3" style="max-width: 1000px;">
0028     <p><a href="{% url 'monitor_app:alarms_dashboard' %}">&larr; All alarms</a></p>
0029     {% if create_mode %}
0030       <h2 class="mb-3">New team</h2>
0031     {% else %}
0032       <h2 class="mb-3">Team <code>{{ team_name }}</code></h2>
0033     {% endif %}
0034 
0035     <div class="d-flex align-items-center mb-2">
0036         <button id="save-btn" class="btn btn-sm btn-secondary" disabled>Save</button>
0037         <span id="autosave-indicator" class="autosave-indicator"></span>
0038     </div>
0039 
0040     {% if create_mode %}
0041     <h3 style="font-size:1em;" class="mt-3">Name</h3>
0042     <p class="text-muted" style="font-size:0.85em;">
0043       Short slug used as <code>@&lt;name&gt;</code> in alarm recipient
0044       lists. No leading <code>@</code> — it's added automatically.
0045     </p>
0046     <input type="text" id="name-input" class="form-control"
0047            maxlength="255" placeholder="e.g. eic-ops">
0048     {% endif %}
0049 
0050     <h3 style="font-size:1em;" class="mt-3">Title</h3>
0051     <input type="text" id="title-input" class="form-control" value="{{ title }}"
0052            maxlength="255" placeholder="Short human-readable title">
0053 
0054     <h3 style="font-size:1em;" class="mt-3">Members (emails, space- or comma-separated)</h3>
0055     <p class="text-muted" style="font-size:0.85em;">
0056       One per line or several per line. Blanks and commas are fine.
0057     </p>
0058     <div id="content-editor">
0059         <textarea id="editor">{{ content }}</textarea>
0060     </div>
0061 
0062     <div id="version-history" class="mt-4"></div>
0063 </div>
0064 
0065 {% if create_mode %}
0066 <script>
0067 (function() {
0068     var CREATE_URL = "{% url 'monitor_app:team_create' %}";
0069     var nameInput = document.getElementById('name-input');
0070     var titleInput = document.getElementById('title-input');
0071     var cmContent = CodeMirror.fromTextArea(document.getElementById('editor'), {
0072         mode: null, theme: 'material-darker', lineNumbers: true,
0073         lineWrapping: true, tabSize: 2,
0074     });
0075     var indicator = document.getElementById('autosave-indicator');
0076     var saveBtn = document.getElementById('save-btn');
0077     indicator.textContent = 'Enter a name and save to create.';
0078 
0079     function updateSaveState() {
0080         var dirty = nameInput.value.trim().length > 0;
0081         saveBtn.disabled = !dirty;
0082         saveBtn.className = 'btn btn-sm ' + (dirty ? 'btn-primary' : 'btn-secondary');
0083     }
0084     nameInput.addEventListener('input', updateSaveState);
0085     updateSaveState();
0086 
0087     saveBtn.addEventListener('click', function() {
0088         var nm = nameInput.value.trim();
0089         if (!nm) { indicator.textContent = 'name required'; return; }
0090         indicator.className = 'autosave-indicator saving';
0091         indicator.textContent = 'Creating…';
0092         fetch(CREATE_URL, {
0093             method: 'POST',
0094             headers: {'Content-Type': 'application/json'},
0095             body: JSON.stringify({
0096                 name: nm,
0097                 title: titleInput.value,
0098                 content: cmContent.getValue(),
0099             }),
0100         }).then(function(r) { return r.json().then(function(j){ return {ok: r.ok, j: j}; }); })
0101           .then(function(o) {
0102             if (!o.ok) {
0103                 indicator.className = 'autosave-indicator error';
0104                 indicator.textContent = 'Error: ' + (o.j.error || 'create failed');
0105                 return;
0106             }
0107             window.location = o.j.edit_url;
0108           })
0109           .catch(function(e) {
0110             indicator.className = 'autosave-indicator error';
0111             indicator.textContent = 'Error: ' + e.message;
0112           });
0113     });
0114 })();
0115 </script>
0116 {% else %}
0117 <script>
0118 (function() {
0119     var AT_NAME    = "{{ team_name|escapejs }}";
0120     var SAVE_URL   = "{% url 'monitor_app:team_save' at_name=team_name %}";
0121     var VERSION_URL = "{% url 'monitor_app:team_version' at_name=team_name version_num=0 %}".slice(0, -2);
0122     var LOCAL_KEY  = 'swf-teams-autosave-' + AT_NAME;
0123     var AUTOSAVE_MS = 10000;
0124 
0125     var cmContent = CodeMirror.fromTextArea(document.getElementById('editor'), {
0126         mode: null, theme: 'material-darker', lineNumbers: true,
0127         lineWrapping: true, tabSize: 2,
0128         extraKeys: {
0129             'Cmd-S': function() { save(false); },
0130             'Ctrl-S': function() { save(false); },
0131         },
0132     });
0133     var titleInput = document.getElementById('title-input');
0134     var lastTitle = titleInput.value;
0135     var lastContent = cmContent.getValue();
0136 
0137     var indicator = document.getElementById('autosave-indicator');
0138     function setIndicator(cls, text) {
0139         indicator.className = 'autosave-indicator ' + cls;
0140         indicator.textContent = text;
0141     }
0142 
0143     var saveBtn = document.getElementById('save-btn');
0144     function isDirty() {
0145         return titleInput.value !== lastTitle || cmContent.getValue() !== lastContent;
0146     }
0147     function updateSaveState() {
0148         if (isDirty()) {
0149             saveBtn.classList.remove('btn-secondary');
0150             saveBtn.classList.add('btn-primary');
0151             saveBtn.disabled = false;
0152         } else {
0153             saveBtn.classList.remove('btn-primary');
0154             saveBtn.classList.add('btn-secondary');
0155             saveBtn.disabled = true;
0156         }
0157     }
0158 
0159     function save(isAutosave) {
0160         var titleVal = titleInput.value;
0161         var contentVal = cmContent.getValue();
0162         if (isAutosave && titleVal === lastTitle && contentVal === lastContent) {
0163             return Promise.resolve();
0164         }
0165         setIndicator('saving', isAutosave ? 'Autosaving…' : 'Saving…');
0166         return fetch(SAVE_URL, {
0167             method: 'POST',
0168             headers: { 'Content-Type': 'application/json' },
0169             body: JSON.stringify({
0170                 title: titleVal, content: contentVal, autosave: !!isAutosave,
0171             }),
0172         }).then(function(r) {
0173             if (!r.ok) return r.json().then(function(j) { throw new Error(j.error || ('HTTP ' + r.status)); });
0174             return r.json();
0175         }).then(function(j) {
0176             lastTitle = titleVal;
0177             lastContent = contentVal;
0178             try { localStorage.removeItem(LOCAL_KEY); } catch (e) {}
0179             setIndicator('saved', (isAutosave ? 'Autosaved' : 'Saved') +
0180                 ' — v' + (j.version_num || 0) +
0181                 ' at ' + new Date((j.modified || 0) * 1000).toLocaleTimeString());
0182             updateSaveState();
0183         }).catch(function(e) {
0184             setIndicator('error', 'Save failed: ' + e.message);
0185         });
0186     }
0187 
0188     document.getElementById('save-btn').addEventListener('click', function() { save(false); });
0189     setInterval(function() { save(true); }, AUTOSAVE_MS);
0190 
0191     function backup() {
0192         try {
0193             localStorage.setItem(LOCAL_KEY, JSON.stringify({
0194                 title: titleInput.value,
0195                 content: cmContent.getValue(),
0196                 ts: Date.now(),
0197             }));
0198         } catch (e) {}
0199     }
0200     function onChange() { backup(); updateSaveState(); }
0201     cmContent.on('change', onChange);
0202     titleInput.addEventListener('input', onChange);
0203     updateSaveState();
0204 
0205     window.addEventListener('beforeunload', function() {
0206         if (titleInput.value === lastTitle && cmContent.getValue() === lastContent) return;
0207         try {
0208             var blob = new Blob([JSON.stringify({
0209                 title: titleInput.value, content: cmContent.getValue(), autosave: true,
0210             })], { type: 'application/json' });
0211             navigator.sendBeacon(SAVE_URL, blob);
0212         } catch (e) {}
0213     });
0214 
0215     // Version history (static from page render).
0216     var versions = JSON.parse('{{ versions_json|escapejs }}');
0217     function fmtTs(t) { return t ? new Date(t * 1000).toISOString().replace('T', ' ').slice(0, 19) : ''; }
0218     function esc(s) { return String(s||'').replace(/[&<>"']/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c];}); }
0219 
0220     var root = document.getElementById('version-history');
0221     var hasLocal = false;
0222     try { hasLocal = !!localStorage.getItem(LOCAL_KEY); } catch (e) {}
0223 
0224     var html = '<h3 style="font-size:1em;">Version history <span class="text-muted">(' + versions.length + ')</span></h3>';
0225     html += '<table class="table table-sm"><tbody>';
0226     if (hasLocal) {
0227         try {
0228             var lo = JSON.parse(localStorage.getItem(LOCAL_KEY));
0229             html += '<tr class="local-backup-row"><td class="vnum">local</td>' +
0230                     '<td class="vts">' + new Date(lo.ts).toISOString().replace('T',' ').slice(0,19) + '</td>' +
0231                     '<td class="vwho">autosave (browser)</td>' +
0232                     '<td>' + esc((lo.content || '').slice(0,120)) + '</td>' +
0233                     '<td><button class="btn btn-sm btn-outline-warning" onclick="swfTeamRestoreLocal()">Restore</button></td></tr>';
0234         } catch (e) {}
0235     }
0236     for (var i = 0; i < versions.length; i++) {
0237         var v = versions[i];
0238         html += '<tr class="version-row"><td class="vnum">v' + v.version_num + '</td>' +
0239                 '<td class="vts">' + fmtTs(v.timestamp) + '</td>' +
0240                 '<td class="vwho">' + esc(v.changed_by || '') + '</td>' +
0241                 '<td>' + esc((v.preview || '').slice(0,120)) + '</td>' +
0242                 '<td><button class="btn btn-sm btn-outline-secondary" onclick="swfTeamRestoreVersion(' + v.version_num + ')">Load</button></td></tr>';
0243     }
0244     html += '</tbody></table>';
0245     root.innerHTML = html;
0246 
0247     window.swfTeamRestoreVersion = function(n) {
0248         if (!confirm('Load v' + n + ' into the editor? Unsaved changes will be lost.')) return;
0249         fetch(VERSION_URL + n + '/')
0250             .then(function(r) { return r.json(); })
0251             .then(function(j) {
0252                 titleInput.value = j.title || '';
0253                 cmContent.setValue(j.content || '');
0254                 setIndicator('saved', 'v' + n + ' loaded — edit and save to commit.');
0255                 updateSaveState();
0256             })
0257             .catch(function(e) { setIndicator('error', 'Restore failed: ' + e.message); });
0258     };
0259     window.swfTeamRestoreLocal = function() {
0260         try {
0261             var lo = JSON.parse(localStorage.getItem(LOCAL_KEY));
0262             if (!lo) return;
0263             if (!confirm('Restore from local browser backup? Unsaved server state will be overwritten on save.')) return;
0264             titleInput.value = lo.title || '';
0265             cmContent.setValue(lo.content || '');
0266             setIndicator('saved', 'Local backup restored — save to commit.');
0267             updateSaveState();
0268         } catch (e) { setIndicator('error', 'Restore failed: ' + e.message); }
0269     };
0270 })();
0271 </script>
0272 {% endif %}
0273 {% endblock %}