@@ -496,23 +496,41 @@ document.getElementById('enquirySearch')?.addEventListener('input', (e) => {
496496 } , 300 ) ;
497497} ) ;
498498
499+ /**
500+ * The number beside Inquiries in the sidebar: how many nobody has opened.
501+ *
502+ * Unread, not unresolved. An enquiry stays Contacted for days while somebody
503+ * waits on a callback, and a badge that sits on 1 the whole time is a badge
504+ * nobody reads -- the morning it says 3 looks like every other morning. The
505+ * server counts it, because the list on screen is filtered and the badge is
506+ * about the whole table.
507+ */
508+ function setInquiryBadge ( unread ) {
509+ const badge = document . getElementById ( 'inquiryCount' ) ;
510+ if ( ! badge ) return ;
511+ // Emptied rather than zeroed: the stylesheet hides an empty badge, and a
512+ // grey "0" beside Inquiries is a thing to read every morning that never
513+ // means anything.
514+ badge . textContent = Number ( unread ) || '' ;
515+ }
516+
499517async function renderInquiries ( ) {
500518 const wrap = document . getElementById ( 'inquiriesWrap' ) ;
501- const countBadge = document . getElementById ( 'inquiryCount' ) ;
502519 if ( ! wrap ) return ;
503520
504- let enquiries ;
521+ let answer ;
505522 try {
506- enquiries = ( await api . enquiries . list ( enquiryStatusFilter , enquirySearch ) ) . enquiries ;
523+ answer = await api . enquiries . list ( enquiryStatusFilter , enquirySearch ) ;
507524 } catch ( err ) {
508525 wrap . innerHTML = '<div class="empty-state">Could not load enquiries.</div>' ;
509526 showError ( err ) ;
510527 return ;
511528 }
512-
513- // The badge counts what still needs attention, not everything ever received.
514- const outstanding = enquiries . filter ( ( e ) => [ 'New' , 'Contacted' , 'Pending' ] . includes ( e . status ) ) . length ;
515- if ( countBadge ) countBadge . textContent = outstanding || '' ;
529+ const enquiries = answer . enquiries ;
530+ setInquiryBadge ( answer . unread ) ;
531+ // Only mark cards when the database knows what has been read. Otherwise
532+ // every viewed_at is null and the whole list would claim to be unread.
533+ const tracksRead = answer . tracks_read !== false ;
516534
517535 if ( ! enquiries . length ) {
518536 wrap . innerHTML = enquirySearch || enquiryStatusFilter
@@ -522,7 +540,7 @@ async function renderInquiries() {
522540 }
523541
524542 wrap . innerHTML = enquiries . map ( ( e ) => `
525- <div class="enquiry-card" data-id="${ e . id } ">
543+ <div class="enquiry-card${ tracksRead && ! e . viewed_at ? ' is-unread' : '' } " data-id="${ e . id } ">
526544 <div class="row">
527545 <span class="num">${ e . enquiry_number } </span>
528546 <span class="enquiry-name">${ escapeHTML ( e . name ) } </span>
@@ -576,7 +594,12 @@ async function renderEnquiry(id) {
576594 const body = document . getElementById ( 'enquiryModalBody' ) ;
577595 let e ;
578596 try {
579- e = ( await api . enquiries . get ( id ) ) . enquiry ;
597+ const answer = await api . enquiries . get ( id ) ;
598+ e = answer . enquiry ;
599+ // Opening it is what marks it read, so the badge is right the moment the
600+ // modal appears rather than after the next refresh of the list behind it.
601+ setInquiryBadge ( answer . unread ) ;
602+ document . querySelector ( `.enquiry-card[data-id="${ id } "]` ) ?. classList . remove ( 'is-unread' ) ;
580603 } catch ( err ) {
581604 body . innerHTML = '<p class="detail-empty">Could not load this enquiry.</p>' ;
582605 showError ( err ) ;
0 commit comments