← back to AbramsOS
views/recalls.ejs
141 lines
<%- include('partials/header', { title: 'Recalls' }) %>
<section class="page-head">
<h1>Recall matches</h1>
<p class="subtle" style="margin:0;color:var(--text-dim);font-size:13px">CPSC recalls auto-matched to your purchases. Confidence ≥ 0.60 lands here for review.</p>
</section>
<% const ndcF = (typeof ndcFlags !== 'undefined') ? ndcFlags : []; %>
<% if (ndcF.length) { %>
<section class="page-head" style="margin-top:18px">
<div>
<h1 style="font-size:16px">Your dispensed products vs FDA recalls <span class="subtle" style="font-weight:400">(NDC + lot/date checked)</span></h1>
<p class="subtle" style="margin:0;color:var(--text-dim);font-size:13px">
The exact product you were dispensed (matched by <strong>NDC</strong>) appears in an FDA recall — then checked against the
recall's <strong>lot expiration + distribution dates</strong>. <span style="color:#7fd0a0">Green = your fills predate/postdate the recalled lot (clear)</span>;
amber = worth confirming your lot #.
</p>
</div>
</section>
<section class="glass" style="padding:8px 6px;overflow:auto;border:1px solid rgba(248,113,113,.35)">
<table class="mr-table">
<thead><tr><th>Your fills</th><th>Severity</th><th>Medication</th><th>Person</th><th>NDC</th><th>Recall #</th><th class="num">Fills</th><th>Fill dates</th><th>Assessment</th><th>Read about it</th></tr></thead>
<tbody>
<% ndcF.forEach(function(x){
var badge = x.ndc_recall_class==='Class I' ? '#f87171' : x.ndc_recall_class==='Class II' ? '#fbbf24' : '#9aa0aa';
var poss = x.status==='possible', conf = x.status==='confirm';
var sBg = poss ? '#f87171' : conf ? '#fbbf24' : '#7fd0a0';
var sTxt = poss ? '⚠ Possible match' : conf ? '⚠ Check lot' : '✓ Clear'; %>
<tr>
<td><span class="sev" style="background:<%= sBg %>22;color:<%= sBg %>;border:1px solid <%= sBg %>66"><%= sTxt %></span></td>
<td><span class="sev" style="background:<%= badge %>22;color:<%= badge %>;border:1px solid <%= badge %>55"><%= x.ndc_recall_class || '—' %></span></td>
<td><%= x.drug_name %></td>
<td><%= x.person %></td>
<td class="mono"><%= x.ndc %></td>
<td class="mono"><a href="https://www.accessdata.fda.gov/scripts/ires/index.cfm?Product=<%= encodeURIComponent(x.ndc_recall_number||'') %>" target="_blank" rel="noopener noreferrer"><%= x.ndc_recall_number || '' %></a></td>
<td class="num"><% if (x.relevant_fills > 0) { %><strong style="color:#f87171"><%= x.relevant_fills %></strong> of <% } %><%= x.fills %></td>
<td class="mono"><%= x.first_fill ? String(x.first_fill).slice(0,10) : '' %> → <%= x.last_fill ? String(x.last_fill).slice(0,10) : '' %></td>
<td class="reason"><%= x.assessment || ((x.ndc_recall_reason||'').slice(0,110)) %></td>
<td class="links"><% (x.links || []).forEach(function(l){ %><a href="<%= l.url %>" target="_blank" rel="noopener noreferrer"><%= l.label %></a><% }) %></td>
</tr>
<% }) %>
</tbody>
</table>
</section>
<% } %>
<% const medR = (typeof medRecalls !== 'undefined') ? medRecalls : []; %>
<% if (medR.length) { %>
<section class="page-head" style="margin-top:18px">
<div>
<h1 style="font-size:16px">💊 Medication recalls <span class="subtle" style="font-weight:400">(FDA openFDA)</span></h1>
<p class="subtle" style="margin:0;color:var(--text-dim);font-size:13px">
Your <strong><%= medR.length %></strong> medications cross-referenced against the FDA drug-enforcement database
(<strong><%= medRecallTotal %></strong> recall records). Matched by <em>drug name</em>, so these are
<strong>advisory</strong> — “this drug has had recalls,” not necessarily your specific lot.
</p>
</div>
</section>
<section class="glass" style="padding:8px 6px;overflow:auto">
<table class="mr-table">
<thead><tr><th>Severity</th><th>Medication</th><th>Person</th><th class="num">Recalls</th><th>Latest</th><th>Most-recent reason</th></tr></thead>
<tbody>
<% medR.forEach(function(x){
var badge = x.worst==='Class I' ? '#f87171' : x.worst==='Class II' ? '#fbbf24' : '#9aa0aa'; %>
<tr>
<td><span class="sev" style="background:<%= badge %>22;color:<%= badge %>;border:1px solid <%= badge %>55"><%= x.worst || '—' %></span></td>
<td><%= x.name %><% if (x.generic_name && x.generic_name.toUpperCase() !== (x.name||'').toUpperCase()) { %> <span class="subtle">· <%= x.generic_name %></span><% } %></td>
<td><%= x.person %></td>
<td class="num"><a href="<%= x.sample_url || 'https://www.fda.gov/drugs/drug-recalls' %>" target="_blank" rel="noopener noreferrer"><%= x.n %></a></td>
<td class="mono"><%= x.latest ? String(x.latest).slice(0,10) : '' %></td>
<td class="reason subtle"><%= (x.sample_reason || '').slice(0,90) %></td>
</tr>
<% }) %>
</tbody>
</table>
</section>
<style>
.mr-table{width:100%;border-collapse:collapse;font-size:12.5px}
.mr-table th,.mr-table td{text-align:left;padding:6px 10px;border-bottom:1px solid rgba(255,255,255,.06);vertical-align:top}
.mr-table th{font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--text-dim)}
.mr-table td.num,.mr-table th.num{text-align:right}
.mr-table td.mono{font-variant-numeric:tabular-nums;white-space:nowrap}
.mr-table td.reason{max-width:340px}
.sev{display:inline-block;padding:1px 8px;border-radius:999px;font-size:11px;font-weight:600;white-space:nowrap}
.mr-table td.links{max-width:220px}
.mr-table td.links a{display:block;font-size:11.5px;color:var(--accent,#7dd3fc);white-space:normal;margin:1px 0}
</style>
<% } %>
<section class="page-head" style="margin-top:22px"><h1 style="font-size:16px">🛒 Product recalls <span class="subtle" style="font-weight:400">(CPSC · purchases)</span></h1></section>
<section class="glass" style="padding:14px 18px">
<div style="display:flex;flex-wrap:wrap;gap:8px;align-items:center;font-size:12px">
<span style="color:var(--text-dim);margin-right:6px">Filter:</span>
<a href="/recalls" style="padding:3px 10px;border-radius:999px;background:<%= !activeStatus ? 'rgba(125,211,252,0.15)' : 'var(--surface-strong)' %>;color:<%= !activeStatus ? 'var(--accent)' : 'var(--text-dim)' %>;text-decoration:none">all</a>
<% counts.forEach(c => { %>
<a href="/recalls?status=<%= encodeURIComponent(c.status) %>" style="padding:3px 10px;border-radius:999px;background:<%= activeStatus === c.status ? 'rgba(125,211,252,0.15)' : 'var(--surface-strong)' %>;color:<%= activeStatus === c.status ? 'var(--accent)' : 'var(--text-dim)' %>;text-decoration:none">
<%= c.status %> · <%= c.n %>
</a>
<% }) %>
</div>
</section>
<% if (!matches.length) { %>
<section class="empty glass">
<p>No recall matches in this view. The CPSC sync runs daily at 03:17; matches will appear here as soon as one of your purchases overlaps.</p>
</section>
<% } else { %>
<section class="connector-grid">
<% matches.forEach(m => { %>
<article class="connector-card glass" style="display:flex;flex-direction:column;gap:10px">
<header>
<span class="name">
<span class="icon" style="background:<%= m.confidence >= 0.85 ? 'rgba(248,113,113,0.20)' : 'rgba(251,191,36,0.20)' %>;color:<%= m.confidence >= 0.85 ? 'var(--danger)' : 'var(--warn)' %>">!</span>
<%= m.title || (m.authority + ' ' + m.external_id) %>
</span>
<span class="status <%= m.status === 'confirmed' ? 'connected' : m.status === 'dismissed' ? 'not_connected' : 'syncing' %>"><%= m.status %></span>
</header>
<% if (m.merchant_name) { %>
<p class="desc">
Matched purchase: <strong><%= m.merchant_name %></strong>
<% if (m.purchase_date) { %> · <%= new Date(m.purchase_date).toLocaleDateString() %><% } %>
<% if (m.total_amount) { %> · <%= m.currency || 'USD' %> <%= Number(m.total_amount).toFixed(2) %><% } %>
</p>
<% } %>
<% if (m.hazard) { %>
<p class="desc"><strong>Hazard:</strong> <%= m.hazard %></p>
<% } %>
<% if (m.remedy) { %>
<p class="desc"><strong>Remedy:</strong> <%= m.remedy %></p>
<% } %>
<p class="subtle" style="font-size:11px">
Confidence: <%= Math.round(m.confidence * 100) %>% · matched <%= new Date(m.matched_at).toLocaleString() %>
<% if (m.url) { %> · <a href="<%= m.url %>" target="_blank" rel="noopener noreferrer" style="margin-left:6px">CPSC notice ↗</a><% } %>
</p>
</article>
<% }) %>
</section>
<% } %>
<%- include('partials/footer') %>