Add Cora Reports to Run queue as first section on Link Building tab

Filters out automation-touched tasks (error, automation underway, complete,
closed, done, internal review) and sorts by due date. Renders with 10-at-a-time
pagination and responsive mobile layout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cora-start
PeninsulaInd 2026-02-20 20:55:07 -06:00
parent 3f2798d338
commit ee7b4cc256
3 changed files with 90 additions and 0 deletions

View File

@ -192,10 +192,17 @@ async def get_link_building_tasks():
] ]
# need_cora: open LB tasks where LB Method = "Cora Backlinks" # need_cora: open LB tasks where LB Method = "Cora Backlinks"
# Exclude tasks that automation already touched
automation_touched = {
"error", "automation underway", "complete",
"closed", "done", "internal review",
}
need_cora = [ need_cora = [
t for t in active_lb t for t in active_lb
if t["custom_fields"].get("LB Method") == "Cora Backlinks" if t["custom_fields"].get("LB Method") == "Cora Backlinks"
and t["status"] not in automation_touched
] ]
need_cora.sort(key=lambda t: int(t.get("due_date") or "9999999999999"))
# recently_completed: closed/complete tasks with date_done in last 7 days # recently_completed: closed/complete tasks with date_done in last 7 days
seven_days_ago_ms = (time.time() - 7 * 86400) * 1000 seven_days_ago_ms = (time.time() - 7 * 86400) * 1000

View File

@ -178,6 +178,17 @@
<div class="stats-row" id="lb-stats"></div> <div class="stats-row" id="lb-stats"></div>
<!-- Cora Reports to Run -->
<div class="section">
<div class="section__header">
<h2 class="section__title"><span class="icon">&#128203;</span> Cora Reports to Run</h2>
<span class="section__badge" id="lb-cora-count">-</span>
</div>
<div class="task-table-wrap" id="lb-cora-table">
<p style="padding:1rem;color:var(--text-muted);">Loading...</p>
</div>
</div>
<!-- Recently Completed (Past 7 Days) --> <!-- Recently Completed (Past 7 Days) -->
<div class="section"> <div class="section">
<div class="section__header"> <div class="section__header">
@ -523,6 +534,69 @@ function renderRecentTable(containerId, tasks) {
container.innerHTML = html; container.innerHTML = html;
} }
function renderCoraQueue(containerId, tasks) {
const container = document.getElementById(containerId);
if (!tasks || tasks.length === 0) {
container.innerHTML = '<p style="padding:1rem;color:var(--text-muted);">No Cora reports queued.</p>';
return;
}
const PAGE_SIZE = 10;
let showAll = false;
function render() {
const visible = showAll ? tasks : tasks.slice(0, PAGE_SIZE);
let html = `<table class="task-table task-table--dense">
<thead><tr>
<th class="cora-hide-mobile">#</th>
<th class="cora-hide-mobile">Task</th>
<th>Keyword</th>
<th>Company</th>
<th class="cora-hide-mobile">Due Date</th>
</tr></thead><tbody>`;
visible.forEach((t, i) => {
const company = t.custom_fields?.Customer || 'Unassigned';
const keyword = t.custom_fields?.Keyword || '';
const link = t.url ? `<a href="${esc(t.url)}" target="_blank" style="color:var(--cream-light);text-decoration:none;">${esc(t.name)}</a>` : esc(t.name);
let dueStr = '-';
if (t.due_date) {
const d = new Date(parseInt(t.due_date, 10));
dueStr = d.toLocaleDateString('en-US', {month:'short', day:'numeric'});
}
html += `<tr>
<td class="task-table__num cora-hide-mobile">${i + 1}</td>
<td class="task-table__title cora-hide-mobile">${link}</td>
<td class="task-table__keyword">${esc(keyword)}</td>
<td class="task-table__company">${esc(company)}</td>
<td class="cora-hide-mobile" style="white-space:nowrap;">${esc(dueStr)}</td>
</tr>`;
});
html += '</tbody></table>';
if (tasks.length > PAGE_SIZE) {
const remaining = tasks.length - PAGE_SIZE;
const label = showAll ? 'Show less' : `Show more (${remaining} remaining)`;
html += `<div style="text-align:center;padding:0.75rem;">
<button id="cora-toggle-btn" style="background:none;border:1px solid var(--border);color:var(--gold-light);padding:0.4rem 1.2rem;border-radius:var(--radius);cursor:pointer;font-size:0.8rem;">${label}</button>
</div>`;
}
container.innerHTML = html;
const btn = document.getElementById('cora-toggle-btn');
if (btn) {
btn.addEventListener('click', () => {
showAll = !showAll;
render();
});
}
}
render();
}
function renderOverviewTable(containerId, tasks, showDueDate) { function renderOverviewTable(containerId, tasks, showDueDate) {
const container = document.getElementById(containerId); const container = document.getElementById(containerId);
if (!tasks || tasks.length === 0) { if (!tasks || tasks.length === 0) {
@ -644,6 +718,11 @@ async function loadLinkBuilding() {
`; `;
document.getElementById('lb-stats').innerHTML = statsHtml; document.getElementById('lb-stats').innerHTML = statsHtml;
// Cora Reports to Run
const coraQueue = data.need_cora || [];
document.getElementById('lb-cora-count').textContent = coraQueue.length;
renderCoraQueue('lb-cora-table', coraQueue);
// Recently Completed // Recently Completed
const recent = data.recently_completed || []; const recent = data.recently_completed || [];
document.getElementById('lb-recent-count').textContent = recent.length; document.getElementById('lb-recent-count').textContent = recent.length;

View File

@ -1014,6 +1014,10 @@ body {
min-width: 700px; min-width: 700px;
} }
.cora-hide-mobile {
display: none;
}
.page-header__greeting { .page-header__greeting {
font-size: 1.35rem; font-size: 1.35rem;
} }