mark as read and unread woth correct position
parent
63a0fbf857
commit
8e95adb320
|
|
@ -25,8 +25,8 @@ import { qk } from '../lib/queryKeys'
|
|||
import { friendlyAuthError } from '../lib/errors'
|
||||
import * as inboxApi from '../api/inbox'
|
||||
import {
|
||||
atsRecommendationClass, avatarColor, fmtDate, fmtShort, initials as initialsOf,
|
||||
inboxSources, relTime, sourceMeta,
|
||||
atsRecommendationClass, avatarColor, fmtDate, initials as initialsOf,
|
||||
inboxSources, sourceMeta,
|
||||
} from '../data/seed'
|
||||
|
||||
const TABS = ['All Applications', 'Unread', 'Processed', 'Rejected', 'Duplicates', 'Email']
|
||||
|
|
@ -57,9 +57,6 @@ const TAB_FILTERS = {
|
|||
*/
|
||||
const SERVER_SCOPED_TABS = new Set(['All Applications', 'Unread'])
|
||||
|
||||
/** The prototype computed "time ago" against a fixed 2026-07-09T20:00. */
|
||||
const NOW = new Date('2026-07-09T20:00')
|
||||
|
||||
/**
|
||||
* message_received_time / message_sent_time are plain string columns
|
||||
* (backend/inbox/models.py:54-56), not timestamps. An unparseable value yields
|
||||
|
|
@ -72,6 +69,33 @@ function parseDate(value) {
|
|||
return Number.isNaN(d.getTime()) ? null : d
|
||||
}
|
||||
|
||||
function startOfDay(d) {
|
||||
return new Date(d.getFullYear(), d.getMonth(), d.getDate())
|
||||
}
|
||||
|
||||
/**
|
||||
* Outlook-style list timestamps in the client's local timezone.
|
||||
* Within 7 days: weekday + AM/PM time. Older: dd/mm/yyyy + AM/PM time.
|
||||
*/
|
||||
function outlookListTime(value) {
|
||||
if (!value) return '—'
|
||||
const d = value instanceof Date ? value : new Date(value)
|
||||
if (Number.isNaN(d.getTime())) return '—'
|
||||
const time = d.toLocaleTimeString(undefined, {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true,
|
||||
})
|
||||
const daysAgo = Math.round((startOfDay(new Date()) - startOfDay(d)) / 86400000)
|
||||
if (daysAgo < 7) {
|
||||
const weekday = d.toLocaleDateString(undefined, { weekday: 'short' })
|
||||
return `${weekday} ${time}`
|
||||
}
|
||||
const dd = String(d.getDate()).padStart(2, '0')
|
||||
const mm = String(d.getMonth() + 1).padStart(2, '0')
|
||||
return `${dd}/${mm}/${d.getFullYear()} ${time}`
|
||||
}
|
||||
|
||||
/**
|
||||
* `source` arrives as the raw To address, because that is where the board tag
|
||||
* lands — careers-rozee@, employee-referral@, mustakbil@ and so on. Strip
|
||||
|
|
@ -487,7 +511,7 @@ function BulkReadBar({ rows, selection, onSetRead, onSetAllRead, busy, canEdit,
|
|||
onClick={() => flashRead('selected', () => onSetRead(true))}
|
||||
>
|
||||
{ticked === 'selected' && <Icon name="check" />}
|
||||
Read
|
||||
Mark as Read
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-secondary btn-sm"
|
||||
|
|
@ -495,7 +519,7 @@ function BulkReadBar({ rows, selection, onSetRead, onSetAllRead, busy, canEdit,
|
|||
title={readCount === 0 ? nothingTo('unread') : tip || 'Mark selected unread'}
|
||||
onClick={() => onSetRead(false)}
|
||||
>
|
||||
Unread
|
||||
Mark as Unread
|
||||
</button>
|
||||
<button className="btn btn-ghost btn-sm" onClick={clear}>Clear</button>
|
||||
</>
|
||||
|
|
@ -508,7 +532,7 @@ function BulkReadBar({ rows, selection, onSetRead, onSetAllRead, busy, canEdit,
|
|||
onClick={() => flashRead('all', () => onSetAllRead(true))}
|
||||
>
|
||||
{ticked === 'all' && <Icon name="check" />}
|
||||
Read
|
||||
Mark all Read
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-secondary btn-sm"
|
||||
|
|
@ -516,7 +540,7 @@ function BulkReadBar({ rows, selection, onSetRead, onSetAllRead, busy, canEdit,
|
|||
title={readCount === 0 ? nothingTo('unread') : scopeTip}
|
||||
onClick={() => onSetAllRead(false)}
|
||||
>
|
||||
Unread
|
||||
Mark all Unread
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
|
@ -816,7 +840,7 @@ export default function Inbox() {
|
|||
</div>
|
||||
<div style={{ textAlign: 'right', flexShrink: 0 }}>
|
||||
<div className="ii-time">
|
||||
{i.received ? relTime(Math.round((NOW - i.received) / 60000)) : '—'}
|
||||
{outlookListTime(i.received)}
|
||||
</div>
|
||||
{/* No ATS score exists server-side — the agent returns a
|
||||
verdict, not a number. The chip stays off rather than
|
||||
|
|
@ -1473,7 +1497,7 @@ function EmailTab({ query, toast }) {
|
|||
{isImported(e) && <Badge className="b-green">Imported</Badge>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="ii-time">{e.when ? fmtShort(e.when) : '—'}</div>
|
||||
<div className="ii-time">{outlookListTime(e.when)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue