Skip to content

Commit 55ccb17

Browse files
committed
feature: add share link for error group.
1 parent e8a988c commit 55ccb17

1 file changed

Lines changed: 89 additions & 8 deletions

File tree

cdip_admin/integrations/templates/integrations/inbound_integration_errors.html

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,19 +119,33 @@ <h5 class="mt-4 mb-2">
119119
<span class="badge badge-secondary ml-1">{{ type_count }}</span>
120120
</h5>
121121
{% for error_msg, configs in err_groups %}
122-
<div class="card mb-2">
122+
<div class="card mb-2 error-group-card"
123+
data-type-name="{{ type_name|escapejs }}"
124+
data-error-msg="{{ error_msg|escapejs }}">
123125
<div class="card-header d-flex justify-content-between align-items-center">
124126
<div class="d-flex align-items-center">
125127
<span class="badge badge-warning mr-2">{{ configs|length }}</span>
126128
<code class="text-truncate" style="max-width: 700px;" title="{{ error_msg }}">{{ error_msg }}</code>
127129
</div>
128-
<button class="btn btn-sm btn-outline-secondary collapse-toggle"
129-
type="button"
130-
data-toggle="collapse"
131-
data-target="#group-{{ forloop.parentloop.counter }}-{{ forloop.counter }}"
132-
aria-expanded="true">
133-
<span class="caret-icon">&#9660;</span>
134-
</button>
130+
<div class="d-flex align-items-center" style="gap:.4rem;">
131+
<button type="button"
132+
class="btn btn-link btn-sm p-0 js-copy-group-link"
133+
title="Copy link to this group">
134+
<i class="fas fa-link fa-xs text-muted"></i>
135+
</button>
136+
<button type="button"
137+
class="btn btn-link btn-sm p-0 js-email-group"
138+
title="Share via email">
139+
<i class="fas fa-envelope fa-xs text-muted"></i>
140+
</button>
141+
<button class="btn btn-sm btn-outline-secondary collapse-toggle"
142+
type="button"
143+
data-toggle="collapse"
144+
data-target="#group-{{ forloop.parentloop.counter }}-{{ forloop.counter }}"
145+
aria-expanded="true">
146+
<span class="caret-icon">&#9660;</span>
147+
</button>
148+
</div>
135149
</div>
136150
<div class="collapse show" id="group-{{ forloop.parentloop.counter }}-{{ forloop.counter }}">
137151
<div class="card-body p-0">
@@ -191,6 +205,15 @@ <h5 class="mt-4 mb-2">
191205

192206
<script>
193207
(function() {
208+
function hashStr(s) {
209+
var h = 0;
210+
for (var i = 0; i < s.length; i++) h = (Math.imul(31, h) + s.charCodeAt(i)) | 0;
211+
return (h >>> 0).toString(36);
212+
}
213+
function cardHash(card) {
214+
return hashStr(card.getAttribute('data-type-name') + '||' + card.getAttribute('data-error-msg'));
215+
}
216+
194217
var overlay = document.getElementById('slide-panel-overlay');
195218
var panel = document.getElementById('slide-panel');
196219
var panelBody = document.getElementById('slide-panel-body');
@@ -264,6 +287,21 @@ <h5 class="mt-4 mb-2">
264287
}
265288

266289
document.addEventListener('click', function(e) {
290+
var groupCopyBtn = e.target.closest('.js-copy-group-link');
291+
if (groupCopyBtn) {
292+
e.stopPropagation();
293+
var card = groupCopyBtn.closest('.error-group-card');
294+
var fragment = 'eg-' + cardHash(card);
295+
var url = window.location.origin + window.location.pathname
296+
+ window.location.search + '#' + fragment;
297+
navigator.clipboard.writeText(url);
298+
showCopied(groupCopyBtn);
299+
var icon = groupCopyBtn.querySelector('i');
300+
var prev = icon.className;
301+
icon.className = 'fas fa-check fa-xs text-success';
302+
setTimeout(function() { icon.className = prev; }, 1500);
303+
return;
304+
}
267305
var copyBtn = e.target.closest('.js-copy-link');
268306
if (copyBtn) {
269307
e.stopPropagation();
@@ -284,6 +322,49 @@ <h5 class="mt-4 mb-2">
284322
htmx.ajax('GET', '{% url "inbound_integration_configuration_list" %}' + '/' + editId + '/edit', {target: '#slide-panel-body', swap: 'innerHTML'});
285323
});
286324

325+
document.addEventListener('click', function(e) {
326+
var emailBtn = e.target.closest('.js-email-group');
327+
if (!emailBtn) return;
328+
e.stopPropagation();
329+
var card = emailBtn.closest('.error-group-card');
330+
var errorMsg = card.getAttribute('data-error-msg');
331+
var typeName = card.getAttribute('data-type-name');
332+
var rows = card.querySelectorAll('tbody tr');
333+
var configLines = [];
334+
rows.forEach(function(row) {
335+
var cells = row.querySelectorAll('td');
336+
var name = cells[2] ? cells[2].textContent.trim() : '';
337+
var org = cells[3] ? cells[3].textContent.trim() : '';
338+
if (name) configLines.push('• ' + name + ' (' + org + ')');
339+
});
340+
var url = window.location.origin + window.location.pathname
341+
+ window.location.search + '#eg-' + cardHash(card);
342+
var subject = 'Integration errors: ' + typeName + ' — '
343+
+ (errorMsg.length > 80 ? errorMsg.slice(0, 80) + '…' : errorMsg);
344+
var body = 'Error:\n' + errorMsg
345+
+ '\n\nAffected configurations (' + configLines.length + '):\n'
346+
+ configLines.join('\n')
347+
+ '\n\nDirect link:\n' + url;
348+
window.location.href = 'mailto:?subject=' + encodeURIComponent(subject)
349+
+ '&body=' + encodeURIComponent(body);
350+
});
351+
352+
// Deep-link: scroll to and highlight error group card from URL fragment
353+
var fragMatch = location.hash.match(/^#(eg-[a-z0-9]+)$/);
354+
if (fragMatch) {
355+
var targetHash = fragMatch[1].slice(3); // strip 'eg-'
356+
var target = null;
357+
document.querySelectorAll('.error-group-card').forEach(function(c) {
358+
if (cardHash(c) === targetHash) target = c;
359+
});
360+
if (target) {
361+
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
362+
target.style.transition = 'box-shadow .3s';
363+
target.style.boxShadow = '0 0 0 3px #ffc107';
364+
setTimeout(function() { target.style.boxShadow = ''; }, 2000);
365+
}
366+
}
367+
287368
// Deep-link: open edit panel from URL hash
288369
var hashMatch = location.hash.match(/^#edit=(.+)$/);
289370
if (hashMatch) {

0 commit comments

Comments
 (0)