Fix case tabs fallback and harden sag email-links loading
This commit is contained in:
parent
9be8b57303
commit
ae6217b976
@ -3342,50 +3342,120 @@ async def add_sag_email_link(sag_id: int, payload: dict):
|
||||
@router.get("/sag/{sag_id}/email-links")
|
||||
async def get_sag_emails(sag_id: int):
|
||||
"""Get emails linked to a case."""
|
||||
query = """
|
||||
WITH linked_emails AS (
|
||||
if not _table_exists("sag_emails") or not _table_exists("email_messages"):
|
||||
logger.warning("⚠️ Email links requested for SAG-%s but required tables are missing", sag_id)
|
||||
return []
|
||||
|
||||
has_thread_key = table_has_column("email_messages", "thread_key")
|
||||
has_email_references = table_has_column("email_messages", "email_references")
|
||||
has_in_reply_to = table_has_column("email_messages", "in_reply_to")
|
||||
has_subject = table_has_column("email_messages", "subject")
|
||||
has_message_id = table_has_column("email_messages", "message_id")
|
||||
has_folder = table_has_column("email_messages", "folder")
|
||||
has_status = table_has_column("email_messages", "status")
|
||||
has_received_date = table_has_column("email_messages", "received_date")
|
||||
|
||||
thread_key_expr = "e.thread_key" if has_thread_key else "NULL"
|
||||
email_references_expr = "e.email_references" if has_email_references else "NULL"
|
||||
in_reply_to_expr = "e.in_reply_to" if has_in_reply_to else "NULL"
|
||||
subject_expr = "e.subject" if has_subject else "NULL"
|
||||
message_id_expr = "e.message_id" if has_message_id else "NULL"
|
||||
|
||||
outgoing_checks = []
|
||||
if has_folder:
|
||||
outgoing_checks.append("LOWER(COALESCE(linked_emails.folder, '')) LIKE 'sent%%'")
|
||||
if has_status:
|
||||
outgoing_checks.append("LOWER(COALESCE(linked_emails.status, '')) = 'sent'")
|
||||
is_outgoing_expr = " OR ".join(outgoing_checks) if outgoing_checks else "FALSE"
|
||||
|
||||
if has_received_date:
|
||||
query = f"""
|
||||
WITH linked_emails AS (
|
||||
SELECT
|
||||
e.*,
|
||||
COALESCE(
|
||||
NULLIF(REGEXP_REPLACE(TRIM(COALESCE({thread_key_expr}, '')), '[<>\\s]', '', 'g'), ''),
|
||||
NULLIF(REGEXP_REPLACE((REGEXP_SPLIT_TO_ARRAY(COALESCE({email_references_expr}, ''), E'[\\s,]+'))[1], '[<>\\s]', '', 'g'), ''),
|
||||
NULLIF(
|
||||
REGEXP_REPLACE(
|
||||
(REGEXP_SPLIT_TO_ARRAY(COALESCE({in_reply_to_expr}, ''), E'[\\s,]+'))[1],
|
||||
'[<>\\s]',
|
||||
'',
|
||||
'g'
|
||||
),
|
||||
''
|
||||
),
|
||||
NULLIF(
|
||||
REGEXP_REPLACE(
|
||||
LOWER(TRIM(COALESCE({subject_expr}, ''))),
|
||||
'^(?:(?:re|fw|fwd|sv|aw)\\s*:\\s*)+',
|
||||
'',
|
||||
'i'
|
||||
),
|
||||
''
|
||||
),
|
||||
NULLIF(REGEXP_REPLACE(TRIM(COALESCE({message_id_expr}, '')), '[<>\\s]', '', 'g'), ''),
|
||||
CONCAT('email-', e.id::text)
|
||||
) AS resolved_thread_key
|
||||
FROM email_messages e
|
||||
JOIN sag_emails se ON e.id = se.email_id
|
||||
WHERE se.sag_id = %s
|
||||
)
|
||||
SELECT
|
||||
e.*,
|
||||
COALESCE(
|
||||
NULLIF(REGEXP_REPLACE(TRIM(COALESCE(e.thread_key, '')), '[<>\\s]', '', 'g'), ''),
|
||||
NULLIF(REGEXP_REPLACE((REGEXP_SPLIT_TO_ARRAY(COALESCE(e.email_references, ''), E'[\\s,]+'))[1], '[<>\\s]', '', 'g'), ''),
|
||||
NULLIF(
|
||||
REGEXP_REPLACE(
|
||||
(REGEXP_SPLIT_TO_ARRAY(COALESCE(e.in_reply_to, ''), E'[\\s,]+'))[1],
|
||||
'[<>\\s]',
|
||||
'',
|
||||
'g'
|
||||
linked_emails.*,
|
||||
({is_outgoing_expr}) AS is_outgoing,
|
||||
COUNT(*) OVER (PARTITION BY linked_emails.resolved_thread_key) AS thread_message_count,
|
||||
MAX(linked_emails.received_date) OVER (PARTITION BY linked_emails.resolved_thread_key) AS thread_last_received_date
|
||||
FROM linked_emails
|
||||
ORDER BY thread_last_received_date DESC NULLS LAST, received_date DESC
|
||||
"""
|
||||
else:
|
||||
query = f"""
|
||||
WITH linked_emails AS (
|
||||
SELECT
|
||||
e.*,
|
||||
COALESCE(
|
||||
NULLIF(REGEXP_REPLACE(TRIM(COALESCE({thread_key_expr}, '')), '[<>\\s]', '', 'g'), ''),
|
||||
NULLIF(REGEXP_REPLACE((REGEXP_SPLIT_TO_ARRAY(COALESCE({email_references_expr}, ''), E'[\\s,]+'))[1], '[<>\\s]', '', 'g'), ''),
|
||||
NULLIF(
|
||||
REGEXP_REPLACE(
|
||||
(REGEXP_SPLIT_TO_ARRAY(COALESCE({in_reply_to_expr}, ''), E'[\\s,]+'))[1],
|
||||
'[<>\\s]',
|
||||
'',
|
||||
'g'
|
||||
),
|
||||
''
|
||||
),
|
||||
''
|
||||
),
|
||||
NULLIF(
|
||||
REGEXP_REPLACE(
|
||||
LOWER(TRIM(COALESCE(e.subject, ''))),
|
||||
'^(?:(?:re|fw|fwd|sv|aw)\\s*:\\s*)+',
|
||||
'',
|
||||
'i'
|
||||
NULLIF(
|
||||
REGEXP_REPLACE(
|
||||
LOWER(TRIM(COALESCE({subject_expr}, ''))),
|
||||
'^(?:(?:re|fw|fwd|sv|aw)\\s*:\\s*)+',
|
||||
'',
|
||||
'i'
|
||||
),
|
||||
''
|
||||
),
|
||||
''
|
||||
),
|
||||
NULLIF(REGEXP_REPLACE(TRIM(COALESCE(e.message_id, '')), '[<>\\s]', '', 'g'), ''),
|
||||
CONCAT('email-', e.id::text)
|
||||
) AS resolved_thread_key
|
||||
FROM email_messages e
|
||||
JOIN sag_emails se ON e.id = se.email_id
|
||||
WHERE se.sag_id = %s
|
||||
)
|
||||
SELECT
|
||||
linked_emails.*,
|
||||
(
|
||||
LOWER(COALESCE(linked_emails.folder, '')) LIKE 'sent%%'
|
||||
OR LOWER(COALESCE(linked_emails.status, '')) = 'sent'
|
||||
) AS is_outgoing,
|
||||
COUNT(*) OVER (PARTITION BY linked_emails.resolved_thread_key) AS thread_message_count,
|
||||
MAX(linked_emails.received_date) OVER (PARTITION BY linked_emails.resolved_thread_key) AS thread_last_received_date
|
||||
FROM linked_emails
|
||||
ORDER BY thread_last_received_date DESC NULLS LAST, received_date DESC
|
||||
"""
|
||||
return execute_query(query, (sag_id,)) or []
|
||||
NULLIF(REGEXP_REPLACE(TRIM(COALESCE({message_id_expr}, '')), '[<>\\s]', '', 'g'), ''),
|
||||
CONCAT('email-', e.id::text)
|
||||
) AS resolved_thread_key
|
||||
FROM email_messages e
|
||||
JOIN sag_emails se ON e.id = se.email_id
|
||||
WHERE se.sag_id = %s
|
||||
)
|
||||
SELECT
|
||||
linked_emails.*,
|
||||
({is_outgoing_expr}) AS is_outgoing,
|
||||
COUNT(*) OVER (PARTITION BY linked_emails.resolved_thread_key) AS thread_message_count,
|
||||
NULL::timestamp AS thread_last_received_date
|
||||
FROM linked_emails
|
||||
ORDER BY linked_emails.id DESC
|
||||
"""
|
||||
|
||||
try:
|
||||
return execute_query(query, (sag_id,)) or []
|
||||
except Exception as exc:
|
||||
logger.error("❌ Failed loading linked emails for SAG-%s: %s", sag_id, exc)
|
||||
return []
|
||||
|
||||
@router.delete("/sag/{sag_id}/email-links/{email_id}")
|
||||
async def remove_sag_email_link(sag_id: int, email_id: int):
|
||||
|
||||
@ -2497,6 +2497,33 @@
|
||||
</div>
|
||||
<!-- ═══════════════ END CASE HEADER ═══════════════ -->
|
||||
|
||||
<script>
|
||||
// Defensive bootstrap: keep core tab/module globals available even if a later
|
||||
// inline script fails to parse due template edge-cases.
|
||||
var caseTypeModuleDefaults = window.caseTypeModuleDefaults || {};
|
||||
window.caseTypeModuleDefaults = caseTypeModuleDefaults;
|
||||
|
||||
window.forceCaseTabActivation = window.forceCaseTabActivation || function(tabId) {
|
||||
if (!tabId) return;
|
||||
const tabContent = document.getElementById('caseTabsContent');
|
||||
const targetPane = document.getElementById(tabId);
|
||||
if (!tabContent || !targetPane) return;
|
||||
|
||||
tabContent.querySelectorAll(':scope > .tab-pane').forEach((pane) => {
|
||||
pane.classList.remove('show', 'active');
|
||||
pane.style.display = 'none';
|
||||
});
|
||||
|
||||
targetPane.classList.add('show', 'active');
|
||||
targetPane.style.display = 'block';
|
||||
|
||||
const tabButtons = document.querySelectorAll('#caseTabs [data-bs-target]');
|
||||
tabButtons.forEach((btn) => {
|
||||
btn.classList.toggle('active', btn.getAttribute('data-bs-target') === `#${tabId}`);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- Tabs Navigation -->
|
||||
<ul class="nav nav-tabs mb-4" id="caseTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user