Renovate mitigation panels and messages

This commit is contained in:
2026-02-15 21:02:40 +00:00
parent 252c626c91
commit 6f64e91694
12 changed files with 1518 additions and 236 deletions

View File

@@ -1,13 +1,13 @@
<div id="mitigation-shell-{{ person.id }}" style="margin-top: 0.7rem;">
<div class="is-flex is-justify-content-space-between is-align-items-start" style="gap: 0.5rem; margin-bottom: 0.5rem;">
<div>
<div class="is-flex is-justify-content-space-between is-align-items-start mitigation-header" style="gap: 0.5rem; margin-bottom: 0.5rem;">
<div class="mitigation-header-main">
<p class="is-size-7 has-text-weight-semibold">Pattern Mitigation</p>
<h4 class="title is-6" style="margin-bottom: 0.2rem;">{{ plan.title|default:"Mitigation Plan" }}</h4>
{% if plan.objective %}
<p class="is-size-7">{{ plan.objective }}</p>
{% endif %}
</div>
<div class="is-flex is-flex-direction-column" style="gap: 0.35rem;">
<div class="is-flex is-flex-direction-column mitigation-header-meta" style="gap: 0.35rem;">
<span class="tag is-light">{{ plan.creation_mode|title }} / {{ plan.status|title }}</span>
<span class="tag is-light">Created {{ plan.created_at }}</span>
<span class="tag is-light">Updated {{ plan.updated_at }}</span>
@@ -92,7 +92,7 @@
<div class="columns is-multiline" style="margin: 0 -0.35rem;">
<div class="column is-12-mobile is-6-tablet" style="padding: 0.35rem;">
<article class="box" style="min-height: 14rem; border: 1px solid rgba(0, 0, 0, 0.15); box-shadow: none;">
<div style="min-height: 14rem;">
<div class="is-flex is-justify-content-space-between is-align-items-center" style="gap: 0.4rem; margin-bottom: 0.45rem;">
<p class="is-size-7 has-text-weight-bold" style="letter-spacing: 0.04em; margin: 0;">RULES</p>
<button
@@ -106,36 +106,51 @@
hx-swap="outerHTML">Delete All</button>
</div>
{% for rule in rules %}
<article class="box" style="padding: 0.55rem; margin-bottom: 0.45rem; border: 1px solid rgba(0, 0, 0, 0.12); box-shadow: none;">
<span class="tag is-light is-small" style="margin-bottom: 0.3rem;">Rule</span>
<span class="tag is-light is-small" style="margin-bottom: 0.3rem;">Created {{ rule.created_at }}</span>
<article class="box mitigation-artifact-card" style="padding: 0.45rem; margin-bottom: 0.35rem; border: 1px solid rgba(0, 0, 0, 0.12); box-shadow: none;">
<form
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_artifact_save' type='widget' person_id=person.id plan_id=plan.id kind='rule' artifact_id=rule.id %}"
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">
<div class="field" style="margin-bottom: 0.35rem;">
<input class="input is-small" type="text" name="title" value="{{ rule.title }}" data-editable="1" readonly>
</div>
<div class="field" style="margin-bottom: 0.35rem;">
<textarea class="textarea is-small" rows="3" name="body" data-editable="1" readonly>{{ rule.content }}</textarea>
</div>
<label class="checkbox is-size-7" style="margin-bottom: 0.35rem;">
<input type="checkbox" name="enabled" value="1" {% if rule.enabled %}checked{% endif %}>
Enabled
</label>
<input type="hidden" name="active_tab" value="{{ active_tab|default:'plan_board' }}">
<div class="buttons are-small" style="margin: 0;">
<button type="button" class="button is-link is-light" data-edit-state="view" onclick="giaMitigationToggleEdit(this); return false;">Edit</button>
<button
type="button"
class="button is-danger is-light"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_artifact_delete' type='widget' person_id=person.id plan_id=plan.id kind='rule' artifact_id=rule.id %}"
hx-vals='{"active_tab":"plan_board"}'
hx-confirm="Delete this rule?"
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">Delete</button>
<div class="mitigation-artifact-headline">
<p class="mitigation-artifact-title">{{ rule.title }}</p>
<div class="mitigation-artifact-actions">
<label class="checkbox is-size-7 mitigation-artifact-enabled">
<input type="checkbox" name="enabled" value="1" data-editable-toggle="1" disabled {% if rule.enabled %}checked{% endif %}>
On
</label>
<button
type="button"
class="button is-small is-link is-light is-rounded mitigation-edit-btn"
data-edit-state="view"
title="Edit rule"
onclick="giaMitigationToggleEdit(this); return false;">
<span class="icon is-small"><i class="fa-solid fa-pen"></i></span>
</button>
<button
type="button"
class="button is-small is-danger is-light is-rounded"
title="Delete rule"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_artifact_delete' type='widget' person_id=person.id plan_id=plan.id kind='rule' artifact_id=rule.id %}"
hx-vals='{"active_tab":"plan_board"}'
hx-confirm="Delete this rule?"
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">
<span class="icon is-small"><i class="fa-solid fa-trash"></i></span>
</button>
</div>
</div>
<p class="mitigation-artifact-meta">Created {{ rule.created_at }}</p>
<p class="mitigation-artifact-preview">{{ rule.content }}</p>
<div class="mitigation-edit-fields">
<div class="field" style="margin-bottom: 0.3rem;">
<input class="input is-small" type="text" name="title" value="{{ rule.title }}" data-editable="1" readonly>
</div>
<div class="field" style="margin-bottom: 0;">
<textarea class="textarea is-small" rows="2" name="body" data-editable="1" readonly>{{ rule.content }}</textarea>
</div>
</div>
</form>
</article>
@@ -144,11 +159,11 @@
<p class="is-size-7 has-text-grey">No rules yet.</p>
</article>
{% endfor %}
</article>
</div>
</div>
<div class="column is-12-mobile is-6-tablet" style="padding: 0.35rem;">
<article class="box" style="min-height: 14rem; border: 1px solid rgba(0, 0, 0, 0.15); box-shadow: none;">
<div style="min-height: 14rem;">
<div class="is-flex is-justify-content-space-between is-align-items-center" style="gap: 0.4rem; margin-bottom: 0.45rem;">
<p class="is-size-7 has-text-weight-bold" style="letter-spacing: 0.04em; margin: 0;">GAMES</p>
<button
@@ -162,36 +177,51 @@
hx-swap="outerHTML">Delete All</button>
</div>
{% for game in games %}
<article class="box" style="padding: 0.55rem; margin-bottom: 0.45rem; border: 1px solid rgba(0, 0, 0, 0.12); box-shadow: none;">
<span class="tag is-light is-small" style="margin-bottom: 0.3rem;">Game</span>
<span class="tag is-light is-small" style="margin-bottom: 0.3rem;">Created {{ game.created_at }}</span>
<article class="box mitigation-artifact-card" style="padding: 0.45rem; margin-bottom: 0.35rem; border: 1px solid rgba(0, 0, 0, 0.12); box-shadow: none;">
<form
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_artifact_save' type='widget' person_id=person.id plan_id=plan.id kind='game' artifact_id=game.id %}"
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">
<div class="field" style="margin-bottom: 0.35rem;">
<input class="input is-small" type="text" name="title" value="{{ game.title }}" data-editable="1" readonly>
</div>
<div class="field" style="margin-bottom: 0.35rem;">
<textarea class="textarea is-small" rows="3" name="body" data-editable="1" readonly>{{ game.instructions }}</textarea>
</div>
<label class="checkbox is-size-7" style="margin-bottom: 0.35rem;">
<input type="checkbox" name="enabled" value="1" {% if game.enabled %}checked{% endif %}>
Enabled
</label>
<input type="hidden" name="active_tab" value="{{ active_tab|default:'plan_board' }}">
<div class="buttons are-small" style="margin: 0;">
<button type="button" class="button is-link is-light" data-edit-state="view" onclick="giaMitigationToggleEdit(this); return false;">Edit</button>
<button
type="button"
class="button is-danger is-light"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_artifact_delete' type='widget' person_id=person.id plan_id=plan.id kind='game' artifact_id=game.id %}"
hx-vals='{"active_tab":"plan_board"}'
hx-confirm="Delete this game?"
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">Delete</button>
<div class="mitigation-artifact-headline">
<p class="mitigation-artifact-title">{{ game.title }}</p>
<div class="mitigation-artifact-actions">
<label class="checkbox is-size-7 mitigation-artifact-enabled">
<input type="checkbox" name="enabled" value="1" data-editable-toggle="1" disabled {% if game.enabled %}checked{% endif %}>
On
</label>
<button
type="button"
class="button is-small is-link is-light is-rounded mitigation-edit-btn"
data-edit-state="view"
title="Edit game"
onclick="giaMitigationToggleEdit(this); return false;">
<span class="icon is-small"><i class="fa-solid fa-pen"></i></span>
</button>
<button
type="button"
class="button is-small is-danger is-light is-rounded"
title="Delete game"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_artifact_delete' type='widget' person_id=person.id plan_id=plan.id kind='game' artifact_id=game.id %}"
hx-vals='{"active_tab":"plan_board"}'
hx-confirm="Delete this game?"
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">
<span class="icon is-small"><i class="fa-solid fa-trash"></i></span>
</button>
</div>
</div>
<p class="mitigation-artifact-meta">Created {{ game.created_at }}</p>
<p class="mitigation-artifact-preview">{{ game.instructions }}</p>
<div class="mitigation-edit-fields">
<div class="field" style="margin-bottom: 0.3rem;">
<input class="input is-small" type="text" name="title" value="{{ game.title }}" data-editable="1" readonly>
</div>
<div class="field" style="margin-bottom: 0;">
<textarea class="textarea is-small" rows="2" name="body" data-editable="1" readonly>{{ game.instructions }}</textarea>
</div>
</div>
</form>
</article>
@@ -200,7 +230,7 @@
<p class="is-size-7 has-text-grey">No games yet.</p>
</article>
{% endfor %}
</article>
</div>
</div>
</div>
</div>
@@ -615,6 +645,110 @@
45% { background-color: rgba(236, 246, 255, 1); }
100% { background-color: rgba(255, 255, 255, 1); }
}
#mitigation-shell-{{ person.id }} .mitigation-header {
flex-wrap: wrap;
}
#mitigation-shell-{{ person.id }} .mitigation-artifact-card {
border-radius: 8px;
}
#mitigation-shell-{{ person.id }} .mitigation-artifact-headline {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 0.35rem;
}
#mitigation-shell-{{ person.id }} .mitigation-artifact-title {
margin: 0;
font-size: 0.78rem;
font-weight: 600;
line-height: 1.2;
overflow-wrap: anywhere;
word-break: break-word;
}
#mitigation-shell-{{ person.id }} .mitigation-artifact-actions {
display: inline-flex;
align-items: center;
gap: 0.25rem;
flex-shrink: 0;
}
#mitigation-shell-{{ person.id }} .mitigation-artifact-enabled {
margin-right: 0.1rem;
color: #6d7583;
}
#mitigation-shell-{{ person.id }} .mitigation-artifact-meta {
margin: 0.18rem 0 0.2rem;
font-size: 0.67rem;
color: #7b8492;
}
#mitigation-shell-{{ person.id }} .mitigation-artifact-preview {
margin: 0;
font-size: 0.73rem;
color: #4c5665;
line-height: 1.3;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
#mitigation-shell-{{ person.id }} .mitigation-edit-fields {
display: none;
margin-top: 0.32rem;
}
#mitigation-shell-{{ person.id }} .mitigation-artifact-card.is-editing .mitigation-edit-fields {
display: block;
}
#mitigation-shell-{{ person.id }} .mitigation-artifact-card.is-editing .mitigation-artifact-preview {
display: none;
}
#mitigation-shell-{{ person.id }} .mitigation-header-main,
#mitigation-shell-{{ person.id }} .mitigation-header-meta {
min-width: 0;
flex: 1 1 16rem;
}
#mitigation-shell-{{ person.id }} .mitigation-header-meta .tag {
white-space: normal;
word-break: break-word;
overflow-wrap: anywhere;
height: auto;
line-height: 1.2;
max-width: 100%;
padding-top: 0.24em;
padding-bottom: 0.24em;
}
#mitigation-shell-{{ person.id }} .columns,
#mitigation-shell-{{ person.id }} .column {
min-width: 0;
}
#mitigation-shell-{{ person.id }} .input,
#mitigation-shell-{{ person.id }} .textarea,
#mitigation-shell-{{ person.id }} .select,
#mitigation-shell-{{ person.id }} .select select {
max-width: 100%;
}
#mitigation-shell-{{ person.id }} pre {
white-space: pre-wrap;
overflow-wrap: anywhere;
word-break: break-word;
}
@media (max-width: 768px) {
#mitigation-shell-{{ person.id }} .mitigation-header-main,
#mitigation-shell-{{ person.id }} .mitigation-header-meta {
flex-basis: 100%;
width: 100%;
}
#mitigation-shell-{{ person.id }} .field.is-grouped {
flex-wrap: wrap;
}
#mitigation-shell-{{ person.id }} .field.is-grouped > .control {
width: 100%;
}
#mitigation-shell-{{ person.id }} .buttons {
flex-wrap: wrap;
}
#mitigation-shell-{{ person.id }} .tabs ul {
flex-wrap: wrap;
}
}
#mitigation-shell-{{ person.id }} .engage-preview-flash {
animation: engagePreviewPulse 850ms ease-in-out 1;
}
@@ -675,15 +809,24 @@
window.giaMitigationToggleEdit = function(button) {
const form = button.closest("form");
if (!form) return;
const card = form.closest(".mitigation-artifact-card");
const editing = button.dataset.editState === "edit";
const fields = form.querySelectorAll('[data-editable="1"]');
const toggles = form.querySelectorAll('[data-editable-toggle="1"]');
if (!editing) {
fields.forEach(function(field) {
field.removeAttribute("readonly");
});
toggles.forEach(function(field) {
field.removeAttribute("disabled");
});
if (card) {
card.classList.add("is-editing");
}
button.dataset.editState = "edit";
button.textContent = "Save";
button.classList.remove("is-light");
button.title = "Save";
button.innerHTML = '<span class="icon is-small"><i class="fa-solid fa-check"></i></span>';
resizeEditableTextareas(form);
} else {
form.requestSubmit();