Files
GIA/core/templates/partials/ai-workspace-person-widget.html

751 lines
32 KiB
HTML

<div
class="ai-person-widget"
id="ai-person-widget-{{ person.id }}"
data-run-url-template="{% url 'ai_workspace_run' type='widget' person_id=person.id operation='summarise' %}"
data-send-url="{% url 'ai_workspace_send' type='widget' person_id=person.id %}"
data-queue-url="{% url 'ai_workspace_queue' type='widget' person_id=person.id %}"
data-limit="{{ limit }}"
data-can-send="{{ send_state.can_send|yesno:'1,0' }}">
<div style="margin-bottom: 0.75rem; padding: 0.5rem 0.25rem; border-bottom: 1px solid rgba(0, 0, 0, 0.12);">
<p class="is-size-7 has-text-weight-semibold">Selected Person</p>
<h3 class="title is-5" style="margin-bottom: 0.25rem;">{{ person.name }}</h3>
<div class="tags" style="margin-top: 0.35rem;">
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='platform' %}">Platform {{ workspace_conversation.platform_type|title }}</a>
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='thread' %}">Thread {{ workspace_conversation.platform_thread_id|default:"-" }}</a>
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='workspace_created' %}">Workspace Created {{ workspace_conversation.created_at|default:"-" }}</a>
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='stability_state' %}">Stability {{ workspace_conversation.stability_state|title }}</a>
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='stability_score' %}">Stability Score {{ workspace_conversation.stability_score|default:"-" }}</a>
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='stability_confidence' %}">Confidence {{ workspace_conversation.stability_confidence }}</a>
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='sample_messages' %}">Sample Msg {{ workspace_conversation.stability_sample_messages }}</a>
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='sample_days' %}">Sample Days {{ workspace_conversation.stability_sample_days }}</a>
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='stability_computed' %}">Stability Computed {{ workspace_conversation.stability_last_computed_at|default:"-" }}</a>
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='commitment_inbound' %}">Commit In {{ workspace_conversation.commitment_inbound_score|default:"-" }}</a>
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='commitment_outbound' %}">Commit Out {{ workspace_conversation.commitment_outbound_score|default:"-" }}</a>
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='commitment_confidence' %}">Commit Confidence {{ workspace_conversation.commitment_confidence }}</a>
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='commitment_computed' %}">Commitment Computed {{ workspace_conversation.commitment_last_computed_at|default:"-" }}</a>
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='last_event' %}">Last Event {{ workspace_conversation.last_event_ts|default:"-" }}</a>
<a class="tag is-light" href="{% url 'ai_workspace_insight_detail' type='page' person_id=person.id metric='last_ai_run' %}">Last AI Run {{ workspace_conversation.last_ai_run_at|default:"-" }}</a>
</div>
<div class="buttons are-small" style="margin-top: 0.35rem; margin-bottom: 0;">
<a class="button is-light" href="{% url 'ai_workspace_insight_graphs' type='page' person_id=person.id %}">
<span class="icon is-small"><i class="fa-solid fa-chart-line"></i></span>
<span>Insight Graphs</span>
</a>
<a class="button is-light" href="{% url 'ai_workspace_information' type='page' person_id=person.id %}">
<span class="icon is-small"><i class="fa-solid fa-circle-info"></i></span>
<span>Information</span>
</a>
<a class="button is-light" href="{% url 'ai_workspace_insight_help' type='page' person_id=person.id %}">
<span class="icon is-small"><i class="fa-solid fa-circle-question"></i></span>
<span>Scoring Help</span>
</a>
</div>
{% with participants=workspace_conversation.participants.all %}
{% if participants %}
<div style="margin-top: 0.35rem;">
<p class="is-size-7 has-text-weight-semibold" style="margin-bottom: 0.2rem;">Participants</p>
<div class="tags">
{% for participant in participants %}
<span class="tag is-light">{{ participant.name }}</span>
{% endfor %}
</div>
</div>
{% endif %}
{% endwith %}
{% if participant_feedback_display %}
<div style="margin-top: 0.25rem;">
<p class="is-size-7 has-text-weight-semibold" style="margin-bottom: 0.2rem;">Participant Feedback</p>
<div class="tags">
<span class="tag is-light {{ participant_feedback_display.state_class }}">
<span class="icon is-small"><i class="{{ participant_feedback_display.state_icon }}"></i></span>
<span>{{ participant_feedback_display.state_label }}</span>
</span>
<span class="tag is-light">Inbound {{ participant_feedback_display.inbound_messages|default:"-" }}</span>
<span class="tag is-light">Outbound {{ participant_feedback_display.outbound_messages|default:"-" }}</span>
<span class="tag is-light">Sample Msg {{ participant_feedback_display.sample_messages|default:"-" }}</span>
<span class="tag is-light">Sample Days {{ participant_feedback_display.sample_days|default:"-" }}</span>
{% if participant_feedback_display.updated_at_label %}
<span class="tag is-light">Updated {{ participant_feedback_display.updated_at_label }}</span>
{% endif %}
</div>
</div>
{% endif %}
{% if compose_page_url %}
<div class="buttons are-small" style="margin-top: 0.45rem; margin-bottom: 0;">
<a class="button is-light" href="{{ compose_page_url }}">
<span class="icon is-small"><i class="{{ manual_icon_class }}"></i></span>
<span>Manual Text Mode</span>
</a>
</div>
{% endif %}
</div>
<div class="notification is-{{ send_state.level }} is-light" style="padding: 0.5rem 0.75rem;">
<div class="is-flex is-justify-content-space-between is-align-items-center" style="gap: 0.4rem; flex-wrap: wrap;">
<div><strong>Send:</strong> {{ send_state.text }}</div>
<div class="buttons are-small" style="margin: 0;">
{% if not send_state.can_send %}
<button
type="button"
id="draft-override-top-btn-{{ person.id }}"
class="button is-warning is-light"
onclick="giaWorkspaceEnableSendOverride('{{ person.id }}', 'draft_reply'); return false;">
<span class="icon is-small"><i class="fa-solid fa-triangle-exclamation"></i></span>
<span>Allow Send In Pane</span>
</button>
{% endif %}
</div>
</div>
<div id="draft-top-status-{{ person.id }}" style="margin-top: 0.5rem;"></div>
</div>
<form id="ai-op-form-{{ person.id }}" style="margin-bottom: 0.75rem;">
<input type="hidden" name="limit" value="{{ limit }}">
<div class="field">
<label class="label is-small">Notes</label>
<div class="control">
<textarea class="textarea is-small" name="user_notes" rows="2" placeholder="Optional intent/context"></textarea>
</div>
</div>
</form>
<div id="ai-response-shell-{{ person.id }}" style="display: block; margin-bottom: 0.9rem;">
<div class="ai-response-capsule" style="margin-bottom: 0.5rem; border: 1px solid rgba(0, 0, 0, 0.16); border-radius: 8px; padding: 0.5rem 0.6rem;">
<div class="is-flex is-justify-content-space-between is-align-items-center" style="margin-bottom: 0.4rem;">
<div class="tags" style="margin-bottom: 0.25rem;">
<span class="tag is-info is-light is-small">Mitigation</span>
<span class="tag is-warning is-light is-small">AI Output</span>
</div>
<div class="tabs is-small is-toggle is-toggle-rounded ai-top-tabs" style="margin-bottom: 0;">
<ul>
<li id="ai-tab-{{ person.id }}-plan_board" class="is-active ai-top-tab-mitigation">
<a onclick="giaWorkspaceOpenTab('{{ person.id }}', 'plan_board', false); return false;">Plan</a>
</li>
<li id="ai-tab-{{ person.id }}-corrections" class="ai-top-tab-mitigation">
<a onclick="giaWorkspaceOpenTab('{{ person.id }}', 'corrections', false); return false;">Corrections</a>
</li>
<li id="ai-tab-{{ person.id }}-engage" class="ai-top-tab-mitigation">
<a onclick="giaWorkspaceOpenTab('{{ person.id }}', 'engage', false); return false;">Engage</a>
</li>
<li id="ai-tab-{{ person.id }}-fundamentals" class="ai-top-tab-mitigation">
<a onclick="giaWorkspaceOpenTab('{{ person.id }}', 'fundamentals', false); return false;">Fundamentals</a>
</li>
<li id="ai-tab-{{ person.id }}-auto" class="ai-top-tab-mitigation">
<a onclick="giaWorkspaceOpenTab('{{ person.id }}', 'auto', false); return false;">Auto</a>
</li>
<li id="ai-tab-{{ person.id }}-ask_ai" class="ai-top-tab-mitigation">
<a onclick="giaWorkspaceOpenTab('{{ person.id }}', 'ask_ai', false); return false;">Ask AI</a>
</li>
<li id="ai-tab-{{ person.id }}-summarise" class="ai-top-tab-output ai-top-tab-output-start">
<a onclick="giaWorkspaceOpenTab('{{ person.id }}', 'summarise', false); return false;">Summary</a>
</li>
<li id="ai-tab-{{ person.id }}-draft_reply" class="ai-top-tab-output">
<a onclick="giaWorkspaceOpenTab('{{ person.id }}', 'draft_reply', false); return false;">Draft</a>
</li>
<li id="ai-tab-{{ person.id }}-extract_patterns" class="ai-top-tab-output">
<a onclick="giaWorkspaceOpenTab('{{ person.id }}', 'extract_patterns', false); return false;">Patterns</a>
</li>
</ul>
</div>
<div class="is-flex is-align-items-center" style="gap: 0.35rem;">
<span id="ai-cache-indicator-{{ person.id }}" class="tag is-warning is-light is-small" style="display: none;">
Cached
</span>
<button
type="button"
class="button is-small is-ghost"
title="Refresh current tab"
onclick="giaWorkspaceRefresh('{{ person.id }}'); return false;">
<span class="icon is-small"><i class="fa-solid fa-rotate-right"></i></span>
</button>
</div>
</div>
<div id="ai-stage-{{ person.id }}" style="min-height: 7rem;">
<div id="ai-pane-{{ person.id }}-artifacts" class="ai-pane" style="display: none;"></div>
<div id="ai-pane-{{ person.id }}-summarise" class="ai-pane" style="display: none;"></div>
<div id="ai-pane-{{ person.id }}-draft_reply" class="ai-pane"></div>
<div id="ai-pane-{{ person.id }}-extract_patterns" class="ai-pane" style="display: none;"></div>
</div>
</div>
</div>
</div>
<style>
@keyframes aiFadeInUp {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: translateY(0); }
}
.ai-animate-in {
animation: aiFadeInUp 180ms ease-out;
}
.ai-response-capsule {
transition: all 180ms ease-out;
}
.ai-person-widget .ai-top-tabs li.ai-top-tab-output-start {
margin-left: 0.35rem;
}
.ai-person-widget .ai-top-tabs li.ai-top-tab-mitigation a {
background: rgba(51, 114, 209, 0.08);
border-color: rgba(51, 114, 209, 0.26);
}
.ai-person-widget .ai-top-tabs li.ai-top-tab-output a {
background: rgba(245, 171, 53, 0.1);
border-color: rgba(223, 145, 22, 0.3);
}
.ai-person-widget .ai-top-tabs li.ai-top-tab-mitigation.is-active a {
background: #3273dc;
border-color: #3273dc;
color: #fff;
}
.ai-person-widget .ai-top-tabs li.ai-top-tab-output.is-active a {
background: #e09116;
border-color: #e09116;
color: #fff;
}
.ai-person-widget .ai-top-tabs li a {
display: flex;
align-items: center;
justify-content: center;
min-height: 2.55rem;
line-height: 1.15;
text-align: center;
white-space: normal;
}
@media screen and (max-width: 768px) {
.ai-person-widget .ai-top-tabs li a {
min-height: 2.8rem;
}
}
</style>
<script>
(function() {
const personId = "{{ person.id }}";
const canSend = (document.getElementById("ai-person-widget-" + personId)?.dataset.canSend || "0") === "1";
const CACHE_TTL_MS = 15 * 60 * 1000; // 15 minutes
const widget = document.getElementById("ai-person-widget-" + personId);
if (!widget) {
return;
}
window.giaWorkspaceState = window.giaWorkspaceState || {};
window.giaWorkspaceCache = window.giaWorkspaceCache || (function() {
try {
// One-time migration flush to avoid stale cached pane HTML from earlier UI schema.
localStorage.removeItem("gia_workspace_cache_v1");
localStorage.removeItem("gia_workspace_cache_v2");
localStorage.removeItem("gia_workspace_cache_v3");
return JSON.parse(localStorage.getItem("gia_workspace_cache_v4") || "{}");
} catch (e) {
return {};
}
})();
function persistCache() {
try {
localStorage.setItem("gia_workspace_cache_v4", JSON.stringify(window.giaWorkspaceCache));
} catch (e) {
// Ignore storage write issues.
}
}
function runUrl(operation) {
const template = widget.dataset.runUrlTemplate || "";
if (template.indexOf("/summarise/") >= 0) {
return template.replace("/summarise/", "/" + operation + "/");
}
return template.replace("summarise", operation);
}
function formData() {
const form = document.getElementById("ai-op-form-" + personId);
const params = new URLSearchParams(new FormData(form));
return params;
}
function cacheKey(operation) {
return personId + "|" + operation + "|" + formData().toString();
}
function applyForceSendState(operation) {
const force = !!(window.giaWorkspaceState[personId] && window.giaWorkspaceState[personId].forceSend);
const forceInput = document.getElementById("draft-send-force-" + personId + "-" + operation);
const sendBtn = document.getElementById("draft-send-btn-" + personId + "-" + operation);
if (forceInput) {
forceInput.value = force ? "1" : "0";
}
if (sendBtn && !canSend) {
sendBtn.disabled = !force;
}
}
function getCacheEntry(operation) {
const key = cacheKey(operation);
const raw = window.giaWorkspaceCache[key];
if (!raw) {
return null;
}
function evict() {
delete window.giaWorkspaceCache[key];
persistCache();
}
if (typeof raw === "string") {
// Backward compatibility: old format has no timestamp; treat as expired.
evict();
return null;
}
if (raw && typeof raw === "object" && typeof raw.html === "string") {
const ts = typeof raw.ts === "number" ? raw.ts : null;
if (!ts) {
evict();
return null;
}
if ((Date.now() - ts) > CACHE_TTL_MS) {
evict();
return null;
}
return { html: raw.html, ts: ts };
}
evict();
return null;
}
function formatCacheAge(ts) {
if (!ts) {
return "Cached";
}
const deltaSec = Math.max(0, Math.floor((Date.now() - ts) / 1000));
if (deltaSec < 5) return "Cached just now";
if (deltaSec < 60) return "Cached " + deltaSec + "s ago";
if (deltaSec < 3600) return "Cached " + Math.floor(deltaSec / 60) + "m ago";
if (deltaSec < 86400) return "Cached " + Math.floor(deltaSec / 3600) + "h ago";
return "Cached " + Math.floor(deltaSec / 86400) + "d ago";
}
function executeInlineScripts(container) {
if (!container) {
return;
}
const scripts = container.querySelectorAll("script");
scripts.forEach(function(oldScript) {
const newScript = document.createElement("script");
if (oldScript.src) {
newScript.src = oldScript.src;
} else {
newScript.textContent = oldScript.textContent || "";
}
Array.from(oldScript.attributes || []).forEach(function(attr) {
if (attr.name !== "src") {
newScript.setAttribute(attr.name, attr.value);
}
});
oldScript.parentNode.replaceChild(newScript, oldScript);
});
}
function setCachedIndicator(show, ts) {
const indicator = document.getElementById("ai-cache-indicator-" + personId);
if (!indicator) {
return;
}
if (show) {
indicator.textContent = formatCacheAge(ts);
}
indicator.style.display = show ? "inline-flex" : "none";
}
const OPERATION_TABS = ["summarise", "draft_reply", "extract_patterns"];
const MITIGATION_TABS = ["plan_board", "corrections", "engage", "fundamentals", "auto", "ask_ai"];
const ALL_TOP_TABS = MITIGATION_TABS.concat(OPERATION_TABS);
function isMitigationTab(tabKey) {
return MITIGATION_TABS.indexOf(tabKey) !== -1;
}
function operationForTab(tabKey) {
return isMitigationTab(tabKey) ? "artifacts" : tabKey;
}
function setTopCapsuleActive(tabKey) {
ALL_TOP_TABS.forEach(function(name) {
const tab = document.getElementById("ai-tab-" + personId + "-" + name);
if (tab) {
tab.classList.toggle("is-active", name === tabKey);
}
});
}
function showOperationPane(operation) {
["artifacts", "summarise", "draft_reply", "extract_patterns"].forEach(function(op) {
const pane = document.getElementById("ai-pane-" + personId + "-" + op);
if (!pane) {
return;
}
pane.style.display = op === operation ? "block" : "none";
});
}
function applyMitigationTabSelection() {
const state = window.giaWorkspaceState[personId] || {};
const targetTab = state.currentMitigationTab || "plan_board";
setTopCapsuleActive(targetTab);
if (typeof window.giaMitigationShowTab === "function") {
window.giaMitigationShowTab(personId, targetTab);
}
}
function hydrateCachedIfAvailable(operation) {
if (operation === "artifacts") {
return false;
}
const entry = getCacheEntry(operation);
const pane = document.getElementById("ai-pane-" + personId + "-" + operation);
if (!pane) {
return false;
}
if (entry && !pane.dataset.loaded) {
pane.innerHTML = entry.html;
pane.dataset.loaded = "1";
executeInlineScripts(pane);
if (window.htmx) {
window.htmx.process(pane);
}
return true;
}
return false;
}
window.giaWorkspaceShowTab = function(pid, operation, tabKey) {
if (pid !== personId) {
return;
}
showOperationPane(operation);
const activeTab = tabKey || (
operation === "artifacts"
? ((window.giaWorkspaceState[personId] || {}).currentMitigationTab || "plan_board")
: operation
);
setTopCapsuleActive(activeTab);
const hydrated = hydrateCachedIfAvailable(operation);
const entry = operation === "artifacts" ? null : getCacheEntry(operation);
setCachedIndicator(hydrated || !!entry, entry ? entry.ts : null);
window.giaWorkspaceState[personId] = window.giaWorkspaceState[personId] || {};
window.giaWorkspaceState[personId].current = operation;
window.giaWorkspaceState[personId].currentTab = activeTab;
};
window.giaWorkspaceOpenTab = function(pid, tabKey, forceRefresh) {
if (pid !== personId) {
return;
}
window.giaWorkspaceState[personId] = window.giaWorkspaceState[personId] || {};
if (isMitigationTab(tabKey)) {
window.giaWorkspaceState[personId].currentMitigationTab = tabKey;
}
window.giaWorkspaceState[personId].pendingTabKey = tabKey;
window.giaWorkspaceRun(personId, operationForTab(tabKey), !!forceRefresh);
};
window.giaWorkspaceRun = function(pid, operation, forceRefresh) {
if (pid !== personId) {
return;
}
const cacheAllowed = operation !== "artifacts";
const shell = document.getElementById("ai-response-shell-" + personId);
const pane = document.getElementById("ai-pane-" + personId + "-" + operation);
if (!shell || !pane) {
return;
}
const currentState = window.giaWorkspaceState[personId] || {};
const targetTabKey = currentState.pendingTabKey || (
operation === "artifacts"
? (currentState.currentMitigationTab || "plan_board")
: operation
);
if (!forceRefresh && currentState.current === operation && pane.dataset.loaded === "1") {
window.giaWorkspaceShowTab(personId, operation, targetTabKey);
if (operation === "artifacts") {
applyMitigationTabSelection();
}
if (window.giaWorkspaceState[personId]) {
window.giaWorkspaceState[personId].pendingTabKey = "";
}
return;
}
window.giaWorkspaceShowTab(personId, operation, targetTabKey);
if (operation === "artifacts") {
applyMitigationTabSelection();
}
const key = cacheKey(operation);
const entry = getCacheEntry(operation);
if (cacheAllowed && !forceRefresh && entry) {
pane.innerHTML = entry.html;
pane.dataset.loaded = "1";
pane.classList.remove("ai-animate-in");
void pane.offsetWidth;
pane.classList.add("ai-animate-in");
setCachedIndicator(true, entry.ts);
if (window.htmx) {
window.htmx.process(pane);
}
if (operation === "draft_reply" && typeof window.giaWorkspaceUseDraft === "function") {
window.giaWorkspaceUseDraft(personId, operation, 0);
}
if (operation === "artifacts") {
applyMitigationTabSelection();
}
if (window.giaWorkspaceState[personId]) {
window.giaWorkspaceState[personId].pendingTabKey = "";
}
return;
}
setCachedIndicator(false, null);
pane.innerHTML = '<div class="notification is-light ai-animate-in">Loading...</div>';
const url = runUrl(operation) + "?" + formData().toString();
fetch(url, { method: "GET" })
.then(function(resp) { return resp.text(); })
.then(function(html) {
pane.innerHTML = html;
pane.dataset.loaded = "1";
executeInlineScripts(pane);
pane.classList.remove("ai-animate-in");
void pane.offsetWidth;
pane.classList.add("ai-animate-in");
if (cacheAllowed) {
window.giaWorkspaceCache[key] = {
html: html,
ts: Date.now(),
};
persistCache();
setCachedIndicator(true, window.giaWorkspaceCache[key].ts);
} else {
setCachedIndicator(false, null);
}
if (window.htmx) {
window.htmx.process(pane);
}
if (operation === "draft_reply" && typeof window.giaWorkspaceUseDraft === "function") {
window.giaWorkspaceUseDraft(personId, operation, 0);
}
if (operation === "artifacts") {
applyMitigationTabSelection();
}
if (window.giaWorkspaceState[personId]) {
window.giaWorkspaceState[personId].pendingTabKey = "";
}
})
.catch(function() {
pane.innerHTML = '<div class="notification is-danger is-light ai-animate-in">Failed to load AI response.</div>';
});
};
window.giaWorkspaceRefresh = function(pid) {
if (pid !== personId) {
return;
}
const state = window.giaWorkspaceState[personId] || {};
const currentTab = state.currentTab || (
state.current === "artifacts"
? (state.currentMitigationTab || "plan_board")
: (state.current || "plan_board")
);
window.giaWorkspaceOpenTab(personId, currentTab, true);
};
window.giaWorkspaceUseDraft = function(pid, operation, index) {
if (pid !== personId) {
return;
}
const host = document.getElementById("draft-host-" + personId + "-" + operation);
const optionCard = host ? host.querySelector('.draft-option-card[data-index="' + index + '"]') : null;
const option = optionCard ? optionCard.querySelector(".draft-text") : null;
if (!option) {
return;
}
const cards = host ? host.querySelectorAll(".draft-option-card") : [];
cards.forEach(function(el) { el.classList.remove("is-selected"); });
if (optionCard) {
optionCard.classList.add("is-selected");
}
host.dataset.selected = String(index);
const sendShell = document.getElementById("draft-send-shell-" + personId + "-" + operation);
const hiddenInput = document.getElementById("draft-send-input-" + personId + "-" + operation);
const preview = document.getElementById("draft-send-preview-" + personId + "-" + operation);
if (!sendShell || !hiddenInput || !preview) {
return;
}
hiddenInput.value = option.textContent.trim();
preview.value = option.textContent.trim();
applyForceSendState(operation);
sendShell.classList.remove("ai-animate-in");
void sendShell.offsetWidth;
sendShell.classList.add("ai-animate-in");
};
window.giaWorkspaceEnableSendOverride = function(pid, operation) {
if (pid !== personId) {
return;
}
window.giaWorkspaceState[personId] = window.giaWorkspaceState[personId] || {};
window.giaWorkspaceState[personId].forceSend = true;
applyForceSendState(operation);
if (typeof window.giaEngageSyncSendOverride === "function") {
window.giaEngageSyncSendOverride(personId);
}
const overrideBtn = document.getElementById("draft-override-top-btn-" + personId);
if (overrideBtn) {
overrideBtn.classList.remove("is-warning");
overrideBtn.classList.add("is-success");
const labelNode = overrideBtn.querySelector("span:last-child");
if (labelNode) {
labelNode.textContent = "Override Enabled";
}
}
const statusHost = document.getElementById("draft-top-status-" + personId);
if (statusHost) {
statusHost.innerHTML = '<div class="notification is-success is-light" style="padding: 0.45rem 0.6rem;">Send override enabled for this pane.</div>';
}
};
window.giaWorkspaceQueueSelectedDraft = function(pid) {
if (pid !== personId) {
return;
}
const queueUrl = widget.dataset.queueUrl;
const preview = document.getElementById("draft-send-preview-" + personId + "-draft_reply");
const statusHost = document.getElementById("draft-top-status-" + personId);
const text = preview ? preview.value.trim() : "";
if (!text) {
if (statusHost) {
statusHost.innerHTML = '<div class="notification is-warning is-light" style="padding: 0.45rem 0.6rem;">Select a draft first, then queue it.</div>';
}
return;
}
const payload = new URLSearchParams();
payload.append("draft_text", text);
fetch(queueUrl, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
"X-CSRFToken": "{{ csrf_token }}",
},
body: payload.toString(),
})
.then(function(resp) { return resp.text(); })
.then(function(html) {
if (statusHost) {
statusHost.innerHTML = html;
}
})
.catch(function() {
if (statusHost) {
statusHost.innerHTML = '<div class="notification is-danger is-light" style="padding: 0.45rem 0.6rem;">Failed to queue draft.</div>';
}
});
};
if (typeof window.giaMitigationShowTab !== "function") {
window.giaMitigationShowTab = function(pid, tabName) {
const names = ["plan_board", "corrections", "engage", "fundamentals", "auto", "ask_ai"];
names.forEach(function(name) {
const pane = document.getElementById("mitigation-tab-" + pid + "-" + name);
const tab = document.getElementById("mitigation-tab-btn-" + pid + "-" + name);
if (!pane) {
return;
}
const active = (name === tabName);
pane.style.display = active ? "block" : "none";
if (tab) {
tab.classList.toggle("is-active", active);
}
});
const shell = document.getElementById("mitigation-shell-" + pid);
if (!shell) {
return;
}
shell.querySelectorAll('input[name="active_tab"]').forEach(function(input) {
input.value = tabName;
});
};
}
if (typeof window.giaMitigationToggleEdit !== "function") {
window.giaMitigationToggleEdit = function(button) {
const form = button ? button.closest("form") : null;
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.classList.remove("is-light");
button.title = "Save";
button.innerHTML = '<span class="icon is-small"><i class="fa-solid fa-check"></i></span>';
} else {
form.requestSubmit();
}
};
}
if (typeof window.giaEngageSetAction !== "function") {
window.giaEngageSetAction = function(pid, action) {
const actionInput = document.getElementById("engage-action-input-" + pid);
if (actionInput) {
actionInput.value = action;
}
};
}
if (typeof window.giaEngageAutoPreview !== "function") {
window.giaEngageAutoPreview = function(pid) {
const form = document.getElementById("engage-form-" + pid);
if (!form) {
return;
}
window.giaEngageSetAction(pid, "preview");
form.requestSubmit();
};
}
if (typeof window.giaEngageSelect !== "function") {
window.giaEngageSelect = function(pid, kind, value, node) {
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 && li.parentElement) {
Array.from(li.parentElement.children).forEach(function(child) {
child.classList.remove("is-active");
});
li.classList.add("is-active");
}
window.giaEngageAutoPreview(pid);
};
}
window.giaWorkspaceOpenTab(personId, "plan_board", false);
})();
</script>