fix(worklist): count unbooked skattekonto rows in the Att göra badge and Hem list (#2180) (#2227)

The Transaktioner inbox lists unbooked skattekonto rows next to unbooked
bank rows, but the sidebar badge and the Hem "Att göra" list counted bank
rows only, so a migrated tax account waited in the inbox unseen.

- lib/worklist: new category book_skattekonto with the inbox's own
  predicate (status = 'booked', no verifikat, not ignored); aggregate
  includes it in counts and total.
- Hem: a "Bokföra skattekontohändelser" row under Bokför, deep-linking to
  /transactions?source=skatteverket.
- Sidebar badge hook: the same third head-count query, summed into the
  /transactions badge.
- DashboardNav subscribes to skattekonto_transactions realtime changes;
  migration adds the table to the supabase_realtime publication so a
  booked or ignored row drops the badge without a manual refresh.

Closes #2180


Claude-Session: https://claude.ai/code/session_01QPQLwHNEiQfiCNLSMzXMiQ

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-09-03 17:17:41 +02:00
committed by GitHub
co-authored by Jakob Wennberg Claude Fable 5.1
parent aeff998b90
commit 1e91c126ff
11 changed files with 136 additions and 9 deletions
+13 -1
View File
@@ -200,7 +200,11 @@ export default function AttGoraSection({
}
const showInboxDocuments = hasAi && counts.inbox_document > 0
const bokforRows = counts.book_transaction > 0 || showInboxDocuments || matches.length > 0
const bokforRows =
counts.book_transaction > 0 ||
counts.book_skattekonto > 0 ||
showInboxDocuments ||
matches.length > 0
const granskaRows =
counts.supplier_invoice_approval > 0 ||
counts.verifikat_missing_document > 0 ||
@@ -277,6 +281,14 @@ export default function AttGoraSection({
count={counts.book_transaction}
/>
)}
{counts.book_skattekonto > 0 && (
<WorklistRow
href="/transactions?source=skatteverket"
icon={Landmark}
label={t('row_book_skattekonto')}
count={counts.book_skattekonto}
/>
)}
{matches.length > 0 && (
<div className="px-4 py-3">
<p className="text-xs text-muted-foreground mb-2">
+14 -3
View File
@@ -509,9 +509,10 @@ export default function DashboardNav({ companyName: _companyName, entityType, pa
useEffect(() => {
if (!company?.id) return
// Realtime keeps the badges live; a trailing debounce collapses event
// bursts (bulk booking / bulk approvals emit one event per row) into a
// single SWR revalidation instead of a request stampede.
// Realtime keeps the badges live (bank rows, skattekonto rows, staged
// operations); a trailing debounce collapses event bursts (bulk booking
// / bulk approvals emit one event per row) into a single SWR
// revalidation instead of a request stampede.
let debounce: ReturnType<typeof setTimeout> | null = null
const queueRefresh = () => {
if (debounce) clearTimeout(debounce)
@@ -530,6 +531,16 @@ export default function DashboardNav({ companyName: _companyName, entityType, pa
},
queueRefresh,
)
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'skattekonto_transactions',
filter: `company_id=eq.${company.id}`,
},
queueRefresh,
)
.on(
'postgres_changes',
{