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")
|
@router.get("/sag/{sag_id}/email-links")
|
||||||
async def get_sag_emails(sag_id: int):
|
async def get_sag_emails(sag_id: int):
|
||||||
"""Get emails linked to a case."""
|
"""Get emails linked to a case."""
|
||||||
query = """
|
if not _table_exists("sag_emails") or not _table_exists("email_messages"):
|
||||||
WITH linked_emails AS (
|
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
|
SELECT
|
||||||
e.*,
|
linked_emails.*,
|
||||||
COALESCE(
|
({is_outgoing_expr}) AS is_outgoing,
|
||||||
NULLIF(REGEXP_REPLACE(TRIM(COALESCE(e.thread_key, '')), '[<>\\s]', '', 'g'), ''),
|
COUNT(*) OVER (PARTITION BY linked_emails.resolved_thread_key) AS thread_message_count,
|
||||||
NULLIF(REGEXP_REPLACE((REGEXP_SPLIT_TO_ARRAY(COALESCE(e.email_references, ''), E'[\\s,]+'))[1], '[<>\\s]', '', 'g'), ''),
|
MAX(linked_emails.received_date) OVER (PARTITION BY linked_emails.resolved_thread_key) AS thread_last_received_date
|
||||||
NULLIF(
|
FROM linked_emails
|
||||||
REGEXP_REPLACE(
|
ORDER BY thread_last_received_date DESC NULLS LAST, received_date DESC
|
||||||
(REGEXP_SPLIT_TO_ARRAY(COALESCE(e.in_reply_to, ''), E'[\\s,]+'))[1],
|
"""
|
||||||
'[<>\\s]',
|
else:
|
||||||
'',
|
query = f"""
|
||||||
'g'
|
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(
|
||||||
NULLIF(
|
LOWER(TRIM(COALESCE({subject_expr}, ''))),
|
||||||
REGEXP_REPLACE(
|
'^(?:(?:re|fw|fwd|sv|aw)\\s*:\\s*)+',
|
||||||
LOWER(TRIM(COALESCE(e.subject, ''))),
|
'',
|
||||||
'^(?:(?:re|fw|fwd|sv|aw)\\s*:\\s*)+',
|
'i'
|
||||||
'',
|
),
|
||||||
'i'
|
''
|
||||||
),
|
),
|
||||||
''
|
NULLIF(REGEXP_REPLACE(TRIM(COALESCE({message_id_expr}, '')), '[<>\\s]', '', 'g'), ''),
|
||||||
),
|
CONCAT('email-', e.id::text)
|
||||||
NULLIF(REGEXP_REPLACE(TRIM(COALESCE(e.message_id, '')), '[<>\\s]', '', 'g'), ''),
|
) AS resolved_thread_key
|
||||||
CONCAT('email-', e.id::text)
|
FROM email_messages e
|
||||||
) AS resolved_thread_key
|
JOIN sag_emails se ON e.id = se.email_id
|
||||||
FROM email_messages e
|
WHERE se.sag_id = %s
|
||||||
JOIN sag_emails se ON e.id = se.email_id
|
)
|
||||||
WHERE se.sag_id = %s
|
SELECT
|
||||||
)
|
linked_emails.*,
|
||||||
SELECT
|
({is_outgoing_expr}) AS is_outgoing,
|
||||||
linked_emails.*,
|
COUNT(*) OVER (PARTITION BY linked_emails.resolved_thread_key) AS thread_message_count,
|
||||||
(
|
NULL::timestamp AS thread_last_received_date
|
||||||
LOWER(COALESCE(linked_emails.folder, '')) LIKE 'sent%%'
|
FROM linked_emails
|
||||||
OR LOWER(COALESCE(linked_emails.status, '')) = 'sent'
|
ORDER BY linked_emails.id DESC
|
||||||
) 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
|
try:
|
||||||
FROM linked_emails
|
return execute_query(query, (sag_id,)) or []
|
||||||
ORDER BY thread_last_received_date DESC NULLS LAST, received_date DESC
|
except Exception as exc:
|
||||||
"""
|
logger.error("❌ Failed loading linked emails for SAG-%s: %s", sag_id, exc)
|
||||||
return execute_query(query, (sag_id,)) or []
|
return []
|
||||||
|
|
||||||
@router.delete("/sag/{sag_id}/email-links/{email_id}")
|
@router.delete("/sag/{sag_id}/email-links/{email_id}")
|
||||||
async def remove_sag_email_link(sag_id: int, email_id: int):
|
async def remove_sag_email_link(sag_id: int, email_id: int):
|
||||||
|
|||||||
@ -2497,6 +2497,33 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- ═══════════════ END CASE HEADER ═══════════════ -->
|
<!-- ═══════════════ 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 -->
|
<!-- Tabs Navigation -->
|
||||||
<ul class="nav nav-tabs mb-4" id="caseTabs" role="tablist">
|
<ul class="nav nav-tabs mb-4" id="caseTabs" role="tablist">
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user