correct sort implemented
parent
b0b73bdf4a
commit
31742c2c49
|
|
@ -822,7 +822,7 @@ class Inbox_Messages(SQLModel, table=True):
|
||||||
# (page-1)*top so page 1 of 25 -> 0..24, page 2 -> 25..49. Newest first
|
# (page-1)*top so page 1 of 25 -> 0..24, page 2 -> 25..49. Newest first
|
||||||
# via created_at.
|
# via created_at.
|
||||||
statement = cls._apply_filters(
|
statement = cls._apply_filters(
|
||||||
select(cls).order_by(cls.created_at.desc()),
|
select(cls).order_by(cls.created_at.desc(),cls.id.desc()),
|
||||||
search, isread, application_status, assigned, is_duplicate,
|
search, isread, application_status, assigned, is_duplicate,
|
||||||
no_suggestions, processing_state,
|
no_suggestions, processing_state,
|
||||||
)
|
)
|
||||||
|
|
@ -1213,22 +1213,7 @@ class Inbox_Messages(SQLModel, table=True):
|
||||||
|
|
||||||
|
|
||||||
class Inbox_Message_Triage(SQLModel, table=True):
|
class Inbox_Message_Triage(SQLModel, table=True):
|
||||||
"""One intake verdict per upstream message id — the gate before inbox_messages.
|
|
||||||
|
|
||||||
Rows land here for BOTH outcomes. Rejections are the point: inbox_messages stays
|
|
||||||
application-only, and a repeated /email/fetch never re-pays for the same
|
|
||||||
classification. Acceptances are recorded too, so a round that classified and then
|
|
||||||
failed to insert does not pay twice either.
|
|
||||||
|
|
||||||
Deliberately no message_body column: the body is what this feature keeps out of the
|
|
||||||
database, and the override route re-reads the mail from upstream by message_id.
|
|
||||||
message_subject is kept (capped in inbox_classifier.decorators.triage_fields)
|
|
||||||
because a review screen without it is unusable — it is stored, never logged.
|
|
||||||
|
|
||||||
server_default is load-bearing on every NOT NULL column: alembic_setup runs with
|
|
||||||
compare_server_default=True, so a model default without a matching server default
|
|
||||||
autogenerates a drift revision on every boot.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__tablename__ = "inbox_message_triage"
|
__tablename__ = "inbox_message_triage"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -945,24 +945,6 @@ export default function Inbox() {
|
||||||
markDuplicate.mutate({ id: item.id, isDuplicate: !item.duplicate, kind: item.kind })
|
markDuplicate.mutate({ id: item.id, isDuplicate: !item.duplicate, kind: item.kind })
|
||||||
}
|
}
|
||||||
|
|
||||||
const sync = useMutation({
|
|
||||||
mutationFn: () => inboxApi.syncMailbox(),
|
|
||||||
onSuccess: async (res) => {
|
|
||||||
await qc.invalidateQueries({ queryKey: qk.mailbox.all() })
|
|
||||||
// /email/fetch reports what the intake gate did with the page. The key is
|
|
||||||
// additive and absent when the gate is disabled, so fall back to the old
|
|
||||||
// message rather than rendering "undefined filtered out".
|
|
||||||
const t = res?.triage
|
|
||||||
toast(
|
|
||||||
t
|
|
||||||
? `Mailbox synced — ${t.ingested} imported, ${t.skipped} filtered out`
|
|
||||||
: 'Mailbox synced',
|
|
||||||
'success',
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onError: (err) => toast(friendlyAuthError(err, 'Sync failed'), 'error'),
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
|
|
@ -993,18 +975,6 @@ export default function Inbox() {
|
||||||
{isForms && (
|
{isForms && (
|
||||||
<span className="integration-status"><span className="pulse" />Google Sheets · Form data</span>
|
<span className="integration-status"><span className="pulse" />Google Sheets · Form data</span>
|
||||||
)}
|
)}
|
||||||
{!isForms && (
|
|
||||||
<button
|
|
||||||
className="btn btn-secondary"
|
|
||||||
disabled={sync.isPending}
|
|
||||||
onClick={() => {
|
|
||||||
toast('Fetching from Outlook…', 'info')
|
|
||||||
sync.mutate()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon name="refresh" /> {sync.isPending ? 'Syncing…' : 'Sync'}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<button className="btn btn-primary" onClick={() => navigate('/import')}>
|
<button className="btn btn-primary" onClick={() => navigate('/import')}>
|
||||||
<Icon name="upload" /> Upload CVs
|
<Icon name="upload" /> Upload CVs
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue