Files
GIA/core/templates/partials/ai-workspace-mitigation-panel.html

661 lines
35 KiB
HTML

<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>
<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>
<span class="tag is-light">{{ plan.creation_mode|title }}</span>
</div>
{% if notice_message %}
<div class="notification is-{{ notice_level|default:'info' }} is-light" style="padding: 0.5rem 0.65rem; margin-bottom: 0.55rem;">
{{ notice_message }}
</div>
{% endif %}
<div class="tabs is-small is-toggle is-toggle-rounded" style="margin-bottom: 0.55rem;">
<ul>
<li id="mitigation-tab-btn-{{ person.id }}-plan_board" class="is-active">
<a onclick="giaMitigationShowTab('{{ person.id }}', 'plan_board'); return false;">Rules &amp; Games</a>
</li>
<li id="mitigation-tab-btn-{{ person.id }}-corrections">
<a onclick="giaMitigationShowTab('{{ person.id }}', 'corrections'); return false;">Corrections</a>
</li>
<li id="mitigation-tab-btn-{{ person.id }}-engage">
<a onclick="giaMitigationShowTab('{{ person.id }}', 'engage'); return false;">Engage</a>
</li>
<li id="mitigation-tab-btn-{{ person.id }}-fundamentals">
<a onclick="giaMitigationShowTab('{{ person.id }}', 'fundamentals'); return false;">Fundamentals</a>
</li>
<li id="mitigation-tab-btn-{{ person.id }}-auto">
<a onclick="giaMitigationShowTab('{{ person.id }}', 'auto'); return false;">Auto</a>
</li>
<li id="mitigation-tab-btn-{{ person.id }}-ask_ai">
<a onclick="giaMitigationShowTab('{{ person.id }}', 'ask_ai'); return false;">Ask AI</a>
</li>
</ul>
</div>
<div id="mitigation-tab-{{ person.id }}-plan_board" class="mitigation-tab-pane">
<div class="is-flex is-justify-content-space-between is-align-items-center" style="gap: 0.5rem; margin-bottom: 0.45rem; flex-wrap: wrap;">
<p class="is-size-7">Two lanes by type: rules on the left, games on the right.</p>
<div class="buttons are-small" style="margin: 0;">
<button
class="button is-light"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_artifact_create' type='widget' person_id=person.id plan_id=plan.id kind='rule' %}"
hx-vals='{"active_tab":"plan_board"}'
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">
<span class="icon is-small"><i class="fa-solid fa-plus"></i></span>
<span>Rule</span>
</button>
<button
class="button is-light"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_artifact_create' type='widget' person_id=person.id plan_id=plan.id kind='game' %}"
hx-vals='{"active_tab":"plan_board"}'
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">
<span class="icon is-small"><i class="fa-solid fa-plus"></i></span>
<span>Game</span>
</button>
</div>
</div>
<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 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
type="button"
class="button is-small is-danger is-light"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_artifact_delete_all' type='widget' person_id=person.id plan_id=plan.id kind='rule' %}"
hx-vals='{"active_tab":"plan_board"}'
hx-confirm="Delete all rules?"
hx-target="#mitigation-shell-{{ person.id }}"
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>
<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>
<input type="hidden" name="enabled" value="1">
<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>
</form>
</article>
{% empty %}
<article class="box" style="padding: 0.65rem; border: 1px dashed rgba(0, 0, 0, 0.2); box-shadow: none;">
<p class="is-size-7 has-text-grey">No rules yet.</p>
</article>
{% endfor %}
</article>
</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 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
type="button"
class="button is-small is-danger is-light"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_artifact_delete_all' type='widget' person_id=person.id plan_id=plan.id kind='game' %}"
hx-vals='{"active_tab":"plan_board"}'
hx-confirm="Delete all games?"
hx-target="#mitigation-shell-{{ person.id }}"
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>
<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>
<input type="hidden" name="enabled" value="1">
<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>
</form>
</article>
{% empty %}
<article class="box" style="padding: 0.65rem; border: 1px dashed rgba(0, 0, 0, 0.2); box-shadow: none;">
<p class="is-size-7 has-text-grey">No games yet.</p>
</article>
{% endfor %}
</article>
</div>
</div>
</div>
<div id="mitigation-tab-{{ person.id }}-corrections" class="mitigation-tab-pane" style="display: none;">
<div class="is-flex is-justify-content-space-between is-align-items-center" style="gap: 0.5rem; margin-bottom: 0.45rem; flex-wrap: wrap;">
<p class="is-size-7">Corrections capture situation-specific clarification points.</p>
<div class="buttons are-small" style="margin: 0;">
<button
class="button is-small is-light"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_artifact_create' type='widget' person_id=person.id plan_id=plan.id kind='correction' %}"
hx-vals='{"active_tab":"corrections"}'
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">
<span class="icon is-small"><i class="fa-solid fa-plus"></i></span>
<span>Correction</span>
</button>
<button
type="button"
class="button is-small is-danger is-light"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_artifact_delete_all' type='widget' person_id=person.id plan_id=plan.id kind='correction' %}"
hx-vals='{"active_tab":"corrections"}'
hx-confirm="Delete all corrections?"
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">Delete All</button>
</div>
</div>
{% if corrections %}
{% for correction in corrections %}
<article class="box" style="padding: 0.55rem; margin-bottom: 0.5rem; border: 1px solid rgba(0, 0, 0, 0.12); box-shadow: none;">
<span class="tag is-light is-small" style="margin-bottom: 0.3rem;">Correction</span>
<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='correction' artifact_id=correction.id %}"
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">
<div class="columns is-multiline" style="margin: 0 -0.3rem;">
<div class="column is-12" style="padding: 0.3rem;">
<input class="input is-small" type="text" name="title" value="{{ correction.title }}">
</div>
<div class="column is-12" style="padding: 0.3rem;">
<label class="label is-small" style="margin-bottom: 0.2rem;">Message Context</label>
<textarea class="textarea is-small" rows="2" name="source_phrase">{{ correction.source_phrase }}</textarea>
</div>
<div class="column is-12" style="padding: 0.3rem;">
<label class="label is-small" style="margin-bottom: 0.2rem;">Insight</label>
<textarea class="textarea is-small" rows="2" name="body">{{ correction.clarification }}</textarea>
</div>
</div>
<input type="hidden" name="enabled" value="1">
<input type="hidden" name="active_tab" value="{{ active_tab|default:'corrections' }}">
<div class="buttons are-small" style="margin: 0;">
<button class="button is-small is-link is-light">Save Correction</button>
<button
type="button"
class="button is-small 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='correction' artifact_id=correction.id %}"
hx-vals='{"active_tab":"corrections"}'
hx-confirm="Delete this correction?"
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">Delete</button>
</div>
</form>
</article>
{% endfor %}
{% else %}
<article class="box" style="padding: 0.65rem; border: 1px dashed rgba(0, 0, 0, 0.2); box-shadow: none;">
<p class="is-size-7 has-text-grey">No corrections yet.</p>
</article>
{% endif %}
</div>
<div id="mitigation-tab-{{ person.id }}-engage" class="mitigation-tab-pane" style="display: none;">
<article class="box" style="padding: 0.65rem; border: 1px solid rgba(0, 0, 0, 0.12); box-shadow: none; margin-bottom: 0.55rem;">
<p class="is-size-7" style="margin-bottom: 0.45rem;">
Build a share-ready message from a rule, game, or correction. Voice framing now lives here.
</p>
<p class="is-size-7" style="margin-bottom: 0;"><strong>Send:</strong> {{ send_state.text }}</p>
</article>
<form
id="engage-form-{{ person.id }}"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_engage_share' type='widget' person_id=person.id plan_id=plan.id %}"
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">
<input type="hidden" name="active_tab" value="{{ active_tab|default:'engage' }}">
<input type="hidden" id="engage-action-input-{{ person.id }}" name="action" value="preview">
<input type="hidden" id="engage-force-send-{{ person.id }}" name="force_send" value="0">
<div class="columns is-multiline" style="margin: 0 -0.3rem;">
<div class="column is-12-mobile is-6-tablet" style="padding: 0.3rem;">
<label class="label is-small" style="margin-bottom: 0.25rem;">Source</label>
<div class="select is-small is-fullwidth">
<select name="source_ref" required onchange="giaEngageAutoPreview('{{ person.id }}');">
{% if engage_options %}
{% for option in engage_options %}
<option value="{{ option.value }}" {% if option.value == engage_form.source_ref %}selected{% endif %}>{{ option.label }}</option>
{% endfor %}
{% else %}
{% for rule in rules %}
<option value="rule:{{ rule.id }}">Rule: {{ rule.title }}</option>
{% endfor %}
{% for game in games %}
<option value="game:{{ game.id }}">Game: {{ game.title }}</option>
{% endfor %}
{% for correction in corrections %}
<option value="correction:{{ correction.id }}">Correction: {{ correction.title }}</option>
{% endfor %}
{% endif %}
</select>
</div>
</div>
<div class="column is-12-mobile is-6-tablet" style="padding: 0.3rem;">
<label class="label is-small" style="margin-bottom: 0.25rem;">Framing</label>
<input type="hidden" id="engage-framing-input-{{ person.id }}" name="framing" value="{{ engage_form.framing|default:'dont_change' }}">
<div id="engage-framing-tabs-{{ person.id }}" class="tabs is-small is-toggle is-toggle-rounded" style="margin-bottom: 0;">
<ul>
<li class="{% if engage_form.framing == 'dont_change' or engage_form.framing == 'neutral' or engage_form.framing == 'named' or not engage_form.framing %}is-active{% endif %}">
<a onclick="giaEngageSelect('{{ person.id }}', 'framing', 'dont_change', this); return false;">Don't Change</a>
</li>
<li class="{% if engage_form.framing == 'shared' %}is-active{% endif %}">
<a onclick="giaEngageSelect('{{ person.id }}', 'framing', 'shared', this); return false;">Shared (We/Us)</a>
</li>
</ul>
</div>
</div>
<div class="column is-12-mobile is-6-tablet" style="padding: 0.3rem;">
<label class="label is-small" style="margin-bottom: 0.25rem;">Share With</label>
<input type="hidden" id="engage-share-input-{{ person.id }}" name="share_target" value="{{ engage_form.share_target|default:'self' }}">
<div id="engage-share-tabs-{{ person.id }}" class="tabs is-small is-toggle is-toggle-rounded" style="margin-bottom: 0;">
<ul>
<li class="{% if engage_form.share_target == 'self' or not engage_form.share_target %}is-active{% endif %}">
<a onclick="giaEngageSelect('{{ person.id }}', 'share', 'self', this); return false;">Me</a>
</li>
<li class="{% if engage_form.share_target == 'other' %}is-active{% endif %}">
<a onclick="giaEngageSelect('{{ person.id }}', 'share', 'other', this); return false;">Other Party</a>
</li>
<li class="{% if engage_form.share_target == 'both' %}is-active{% endif %}">
<a onclick="giaEngageSelect('{{ person.id }}', 'share', 'both', this); return false;">Both Parties</a>
</li>
</ul>
</div>
</div>
<div class="column is-12" style="padding: 0.3rem;">
<label class="label is-small" style="margin-bottom: 0.25rem;">Context (optional)</label>
<textarea class="textarea is-small" rows="2" name="context_note" placeholder="One additional note for this share.">{{ engage_form.context_note }}</textarea>
</div>
</div>
<div class="buttons are-small" style="margin-top: 0.15rem;">
<button id="engage-send-btn-{{ person.id }}" type="submit" class="button is-link is-light" onclick="giaEngageSetAction('{{ person.id }}', 'send');" {% if not send_state.can_send %}disabled{% endif %}>
<span class="icon is-small"><i class="fa-solid fa-paper-plane"></i></span>
<span>Send</span>
</button>
<button type="submit" class="button is-info is-light" onclick="giaEngageSetAction('{{ person.id }}', 'queue');">
<span class="icon is-small"><i class="fa-solid fa-inbox-in"></i></span>
<span>Add To Queue</span>
</button>
</div>
</form>
{% if engage_preview %}
<article class="box {% if engage_preview_flash %}engage-preview-flash{% endif %}" style="margin-top: 0.6rem; padding: 0.65rem; border: 1px solid rgba(0, 0, 0, 0.14); box-shadow: none;">
<p class="is-size-7 has-text-weight-semibold" style="margin-bottom: 0.35rem;">Preview</p>
<pre style="margin: 0; white-space: pre-wrap; font-size: 0.78rem; line-height: 1.36;">{{ engage_preview }}</pre>
</article>
{% else %}
<article class="box" style="margin-top: 0.6rem; padding: 0.65rem; border: 1px dashed rgba(0, 0, 0, 0.2); box-shadow: none;">
<p class="is-size-7 has-text-grey">No preview yet.</p>
</article>
{% endif %}
</div>
<div id="mitigation-tab-{{ person.id }}-fundamentals" class="mitigation-tab-pane" style="display: none;">
<div class="columns is-multiline" style="margin: 0 -0.35rem;">
<div class="column is-12-mobile is-5-tablet" style="padding: 0.35rem;">
<article class="box" style="padding: 0.65rem; border: 1px solid rgba(0, 0, 0, 0.12); box-shadow: none; height: 100%;">
<p class="is-size-7 has-text-weight-semibold" style="margin-bottom: 0.4rem;">Current Fundamentals</p>
{% if plan.fundamental_items %}
<div class="content" style="margin-bottom: 0;">
<ul style="margin-top: 0;">
{% for item in plan.fundamental_items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
</div>
{% else %}
<p class="is-size-7 has-text-grey">No fundamentals yet.</p>
{% endif %}
</article>
</div>
<div class="column is-12-mobile is-7-tablet" style="padding: 0.35rem;">
<article class="box" style="padding: 0.65rem; 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_fundamentals_save' type='widget' person_id=person.id plan_id=plan.id %}"
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">
<input type="hidden" name="active_tab" value="{{ active_tab|default:'fundamentals' }}">
<div class="field" style="margin-bottom: 0.45rem;">
<label class="label is-small">Edit Fundamentals (one per line)</label>
<textarea class="textarea is-small" rows="10" name="fundamentals_text">{{ fundamentals_text }}</textarea>
</div>
<button class="button is-small is-link is-light">Save Fundamentals</button>
</form>
</article>
</div>
</div>
</div>
<div id="mitigation-tab-{{ person.id }}-auto" class="mitigation-tab-pane" style="display: none;">
<article class="box" style="padding: 0.65rem; border: 1px solid rgba(0, 0, 0, 0.12); box-shadow: none; margin-bottom: 0.55rem;">
<p class="is-size-7" style="margin-bottom: 0.35rem;">
Auto checks read recent message rows and can write linked mitigation objects for this workspace conversation.
</p>
<p class="is-size-7" style="margin-bottom: 0;">
Last run: {% if auto_settings.last_run_at %}{{ auto_settings.last_run_at }}{% else %}Never{% endif %}
</p>
{% if auto_settings.last_result_summary %}
<p class="is-size-7" style="margin-top: 0.35rem; margin-bottom: 0;">{{ auto_settings.last_result_summary }}</p>
{% endif %}
</article>
<form
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_auto' type='widget' person_id=person.id plan_id=plan.id %}"
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">
<input type="hidden" name="active_tab" value="auto">
<div class="columns is-multiline" style="margin: 0 -0.3rem;">
<div class="column is-12-mobile is-6-tablet" style="padding: 0.3rem;">
<label class="checkbox is-size-7"><input type="checkbox" name="enabled" value="1" {% if auto_settings.enabled %}checked{% endif %}> Enable auto checks for this Conversation</label>
</div>
<div class="column is-12-mobile is-6-tablet" style="padding: 0.3rem;">
<label class="checkbox is-size-7"><input type="checkbox" name="auto_pattern_recognition" value="1" {% if auto_settings.auto_pattern_recognition %}checked{% endif %}> Detect pattern signals from Message rows</label>
</div>
<div class="column is-12-mobile is-6-tablet" style="padding: 0.3rem;">
<label class="checkbox is-size-7"><input type="checkbox" name="auto_create_mitigation" value="1" {% if auto_settings.auto_create_mitigation %}checked{% endif %}> Create a Plan when the Conversation has none</label>
</div>
<div class="column is-12-mobile is-6-tablet" style="padding: 0.3rem;">
<label class="checkbox is-size-7"><input type="checkbox" name="auto_create_corrections" value="1" {% if auto_settings.auto_create_corrections %}checked{% endif %}> Create Correction rows linked to the Plan</label>
</div>
<div class="column is-12-mobile is-6-tablet" style="padding: 0.3rem;">
<label class="checkbox is-size-7"><input type="checkbox" name="auto_notify_enabled" value="1" {% if auto_settings.auto_notify_enabled %}checked{% endif %}> Notify when auto writes new Correction rows</label>
</div>
<div class="column is-12-mobile is-6-tablet" style="padding: 0.3rem;">
<label class="label is-small" style="margin-bottom: 0.25rem;">Message rows per check</label>
<input class="input is-small" type="number" min="10" max="200" name="sample_message_window" value="{{ auto_settings.sample_message_window }}">
</div>
<div class="column is-12-mobile is-6-tablet" style="padding: 0.3rem;">
<label class="label is-small" style="margin-bottom: 0.25rem;">Cooldown seconds between checks</label>
<input class="input is-small" type="number" min="0" max="86400" name="check_cooldown_seconds" value="{{ auto_settings.check_cooldown_seconds }}">
</div>
<div class="column is-12-mobile is-6-tablet" style="padding: 0.3rem;">
<label class="label is-small" style="margin-bottom: 0.25rem;">NTFY topic override for auto</label>
<input class="input is-small" type="text" name="ntfy_topic_override" value="{{ auto_settings.ntfy_topic_override|default:'' }}" placeholder="Optional topic override">
</div>
<div class="column is-12" style="padding: 0.3rem;">
<label class="label is-small" style="margin-bottom: 0.25rem;">NTFY URL override for auto</label>
<input class="input is-small" type="text" name="ntfy_url_override" value="{{ auto_settings.ntfy_url_override|default:'' }}" placeholder="Optional NTFY URL override">
</div>
<div class="column is-12" style="padding: 0.3rem;">
<p class="is-size-7 has-text-grey">If overrides are empty, notifications fall back to Notification Settings topic/url.</p>
</div>
</div>
<div class="buttons are-small" style="margin-top: 0.2rem;">
<button class="button is-link is-light" name="action" value="save">Save Auto Controls</button>
<button class="button is-primary is-light" name="action" value="run_now">Run Check Now</button>
</div>
</form>
</div>
<div id="mitigation-tab-{{ person.id }}-ask_ai" class="mitigation-tab-pane" style="display: none;">
<form
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_export' type='widget' person_id=person.id plan_id=plan.id %}"
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML"
style="margin-bottom: 0.55rem;">
<input type="hidden" name="active_tab" value="{{ active_tab|default:'ask_ai' }}">
<div class="field is-grouped is-grouped-multiline is-align-items-flex-end" style="margin-bottom: 0; gap: 0.35rem;">
<div class="control">
<label class="label is-small" style="margin-bottom: 0.25rem;">Bundle</label>
<div class="select is-small">
<select name="artifact_type">
<option value="rulebook">Rulebook</option>
<option value="rules">Rules</option>
<option value="games">Games</option>
<option value="corrections">Corrections</option>
</select>
</div>
</div>
<div class="control">
<label class="label is-small" style="margin-bottom: 0.25rem;">Format</label>
<div class="select is-small">
<select name="export_format">
<option value="markdown">Markdown</option>
<option value="json">JSON</option>
<option value="text">Text</option>
</select>
</div>
</div>
<div class="control">
<button class="button is-small is-link is-light" style="margin-top: 1.35rem;">
<span class="icon is-small"><i class="fa-solid fa-file-export"></i></span>
<span>Export</span>
</button>
</div>
</div>
</form>
{% if latest_export %}
<article class="box" style="padding: 0.55rem; margin-bottom: 0.6rem; border: 1px dashed rgba(0, 0, 0, 0.25); box-shadow: none;">
<p class="is-size-7 has-text-weight-semibold" style="margin-bottom: 0.3rem;">
Last Export: {{ latest_export.artifact_type|title }} ({{ latest_export.export_format|upper }})
</p>
<pre style="max-height: 14rem; overflow: auto; margin: 0; white-space: pre-wrap; font-size: 0.72rem; line-height: 1.28;">{{ latest_export.payload }}</pre>
</article>
{% endif %}
<article class="box" style="padding: 0.65rem; border: 1px solid rgba(0, 0, 0, 0.12); box-shadow: none;">
<p class="is-size-7 has-text-weight-semibold" style="margin-bottom: 0.4rem;">Ask AI</p>
<div style="max-height: 12rem; overflow-y: auto; margin-bottom: 0.55rem; padding-right: 0.2rem;">
{% for message in mitigation_messages %}
<div style="margin-bottom: 0.45rem;">
<span class="tag is-light is-small">{{ message.role }}</span>
<div style="margin-top: 0.15rem; white-space: pre-wrap;">{{ message.text }}</div>
</div>
{% empty %}
<p class="is-size-7 has-text-grey">No messages yet.</p>
{% endfor %}
</div>
<form
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'ai_workspace_mitigation_chat' type='widget' person_id=person.id plan_id=plan.id %}"
hx-target="#mitigation-shell-{{ person.id }}"
hx-swap="outerHTML">
<input type="hidden" name="active_tab" value="{{ active_tab|default:'ask_ai' }}">
<div class="field" style="margin-bottom: 0.5rem;">
<div class="control">
<textarea name="message" class="textarea is-small" rows="2" placeholder="Refine the plan or request a new lens..."></textarea>
</div>
</div>
<button class="button is-small is-primary is-light">
<span class="icon is-small"><i class="fa-solid fa-comments"></i></span>
<span>Ask AI</span>
</button>
</form>
</article>
</div>
</div>
<style>
@keyframes engagePreviewPulse {
0% { background-color: rgba(255, 255, 255, 1); }
45% { background-color: rgba(236, 246, 255, 1); }
100% { background-color: rgba(255, 255, 255, 1); }
}
#mitigation-shell-{{ person.id }} .engage-preview-flash {
animation: engagePreviewPulse 850ms ease-in-out 1;
}
</style>
<script>
(function() {
const personId = "{{ person.id }}";
const canSend = "{{ send_state.can_send|yesno:'1,0' }}" === "1";
function resizeEditableTextareas(root) {
if (!root) return;
root.querySelectorAll('textarea[data-editable="1"]').forEach(function(area) {
area.style.height = "auto";
area.style.height = Math.max(area.scrollHeight, 72) + "px";
});
}
window.giaEngageSyncSendOverride = function(pid) {
if (pid !== personId) return;
const forceInput = document.getElementById("engage-force-send-" + pid);
const sendBtn = document.getElementById("engage-send-btn-" + pid);
const force =
!!(window.giaWorkspaceState
&& window.giaWorkspaceState[pid]
&& window.giaWorkspaceState[pid].forceSend);
if (forceInput) {
forceInput.value = force ? "1" : "0";
}
if (sendBtn) {
sendBtn.disabled = !canSend && !force;
}
};
function setActiveTabHiddenFields(tabName) {
const root = document.getElementById("mitigation-shell-" + personId);
if (!root) return;
root.querySelectorAll('input[name="active_tab"]').forEach(function(input) {
input.value = tabName;
});
resizeEditableTextareas(root);
}
window.giaMitigationShowTab = function(pid, tabName) {
if (pid !== personId) return;
["plan_board", "corrections", "engage", "fundamentals", "auto", "ask_ai"].forEach(function(name) {
const pane = document.getElementById("mitigation-tab-" + personId + "-" + name);
const tab = document.getElementById("mitigation-tab-btn-" + personId + "-" + name);
if (!pane || !tab) return;
const active = name === tabName;
pane.style.display = active ? "block" : "none";
tab.classList.toggle("is-active", active);
});
setActiveTabHiddenFields(tabName);
};
window.giaMitigationToggleEdit = function(button) {
const form = button.closest("form");
if (!form) return;
const editing = button.dataset.editState === "edit";
const fields = form.querySelectorAll('[data-editable="1"]');
if (!editing) {
fields.forEach(function(field) {
field.removeAttribute("readonly");
});
button.dataset.editState = "edit";
button.textContent = "Save";
button.classList.remove("is-light");
resizeEditableTextareas(form);
} else {
form.requestSubmit();
}
};
window.giaEngageSetAction = function(pid, action) {
if (pid !== personId) return;
const actionInput = document.getElementById("engage-action-input-" + pid);
if (actionInput) {
actionInput.value = action;
}
if (action === "send") {
window.giaEngageSyncSendOverride(pid);
}
};
window.giaEngageAutoPreview = function(pid) {
if (pid !== personId) return;
const form = document.getElementById("engage-form-" + pid);
if (!form) return;
window.giaEngageSetAction(pid, "preview");
form.requestSubmit();
};
window.giaEngageSelect = function(pid, kind, value, node) {
if (pid !== personId) return;
let inputId = "";
if (kind === "share") {
inputId = "engage-share-input-" + pid;
} else if (kind === "framing") {
inputId = "engage-framing-input-" + pid;
}
const input = inputId ? document.getElementById(inputId) : null;
if (input) {
input.value = value;
}
const li = node && node.closest ? node.closest("li") : null;
if (!li) return;
const ul = li.parentElement;
if (!ul) return;
Array.from(ul.children).forEach(function(child) {
child.classList.remove("is-active");
});
li.classList.add("is-active");
window.giaEngageAutoPreview(pid);
};
window.giaMitigationShowTab(personId, "{{ active_tab|default:'plan_board' }}");
resizeEditableTextareas(document.getElementById("mitigation-shell-" + personId));
window.giaEngageSyncSendOverride(personId);
})();
</script>