From 70e893b8d4c2d5348e7c6f7ab7407fa1b2eabbed Mon Sep 17 00:00:00 2001 From: Jakob Wennberg <149234542+jakobwennberg@users.noreply.github.com> Date: Fri, 3 Jul 2026 16:35:09 +0200 Subject: [PATCH] fix(skattekonto): stop conflating live-call auth failures with "inte anslutet" + surface SKV connection first in tax settings (#887) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sync endpoint returns 401 for six distinct auth states (handleSkvError): NOT_CONNECTED, SESSION_EXPIRED, REFRESH_EXHAUSTED, MISSING_SCOPE, TOKEN_CORRUPTED, TOKEN_REVOKED. The page treated every 401 as "Skatteverket är inte anslutet" — directly contradicting Inställningar, which reads the stored token metadata and truthfully shows "Ansluten" for all but the first. - syncNow now checks the structured error code: only NOT_CONNECTED flips to the not-connected empty state. Every other auth failure renders the server's actual Swedish message as a persistent banner with an "Anslut igen" CTA, keeping the stored saldo/transactions visible. - SkatteverketConnectPanel moves to the top of /settings/tax — the skattekonto and momsdeklaration pages send users there specifically to (re)connect, and below the tax form it sat out of view. Co-authored-by: Claude Fable 5 --- app/(dashboard)/skattekonto/page.tsx | 37 ++++++++++++++++++- .../settings/sections/TaxSettingsContent.tsx | 7 +++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/app/(dashboard)/skattekonto/page.tsx b/app/(dashboard)/skattekonto/page.tsx index 0052a854..b7c654c4 100644 --- a/app/(dashboard)/skattekonto/page.tsx +++ b/app/(dashboard)/skattekonto/page.tsx @@ -67,6 +67,10 @@ export default function SkattekontoPage() { const [bookingId, setBookingId] = useState(null) const [notConnected, setNotConnected] = useState(false) const [loadError, setLoadError] = useState(false) + // Set when a sync fails with an auth error while a connection exists + // (expired session, missing scope, revoked token). Rendered as a banner — + // the stored data below stays visible and usable. + const [reconnectMessage, setReconnectMessage] = useState(null) const [matchOpenFor, setMatchOpenFor] = useState( null, ) @@ -122,12 +126,28 @@ export default function SkattekontoPage() { }) const json = await res.json() if (!res.ok) { + // 401 covers several distinct auth states (see handleSkvError in the + // skatteverket extension). Only NOT_CONNECTED means "no connection + // exists" — the rest (SESSION_EXPIRED, MISSING_SCOPE, TOKEN_REVOKED, + // …) fire while Inställningar truthfully shows the stored token as + // "Ansluten". Showing the full "inte anslutet"-tomvy for those + // contradicts the settings panel; show the server's actual reason + // with a reconnect CTA instead. if (res.status === 401) { - setNotConnected(true) + if (json.code === 'NOT_CONNECTED') { + setNotConnected(true) + } else { + setReconnectMessage( + typeof json.error === 'string' && json.error + ? json.error + : 'Anslutningen mot Skatteverket behöver förnyas. Anslut igen med BankID.', + ) + } return } throw new Error(json.error || 'Synk misslyckades') } + setReconnectMessage(null) toast({ title: 'Skattekonto synkroniserat', description: `${json.data.booked} bokförda, ${json.data.upcoming} kommande`, @@ -292,6 +312,21 @@ export default function SkattekontoPage() { } /> + {reconnectMessage && ( +
+
+ +

{reconnectMessage}

+
+ +
+ )} + diff --git a/components/settings/sections/TaxSettingsContent.tsx b/components/settings/sections/TaxSettingsContent.tsx index f1cd4af6..f37d8e55 100644 --- a/components/settings/sections/TaxSettingsContent.tsx +++ b/components/settings/sections/TaxSettingsContent.tsx @@ -77,11 +77,14 @@ export function TaxSettingsContent() { return (
+ {/* Connection panel first: the skattekonto and momsdeklaration pages + send users here specifically to (re)connect — below the long tax + form it sat out of view. */} + {showSkatteverket && } + - - {showSkatteverket && }
) }