fix(skattekonto): stop conflating live-call auth failures with "inte anslutet" + surface SKV connection first in tax settings (#887)
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 <noreply@anthropic.com>
This commit is contained in:
@@ -67,6 +67,10 @@ export default function SkattekontoPage() {
|
||||
const [bookingId, setBookingId] = useState<string | null>(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<string | null>(null)
|
||||
const [matchOpenFor, setMatchOpenFor] = useState<StoredSkattekontoTransaction | null>(
|
||||
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 && (
|
||||
<div className="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-destructive/20 bg-destructive/5 px-4 py-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<AlertCircle className="h-4 w-4 shrink-0 text-destructive" />
|
||||
<p className="text-sm">{reconnectMessage}</p>
|
||||
</div>
|
||||
<Button asChild variant="outline" size="sm">
|
||||
<Link href="/settings/tax">
|
||||
<ExternalLink className="mr-2 h-3.5 w-3.5" />
|
||||
Anslut igen
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<BalanceHero saldo={saldo} loading={loading} onCopyOcr={copyOcr} />
|
||||
|
||||
<Card>
|
||||
|
||||
@@ -77,11 +77,14 @@ export function TaxSettingsContent() {
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{/* 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 && <SkatteverketConnectPanel />}
|
||||
|
||||
<SettingsFormWrapper onSave={handleSave} className="space-y-0">
|
||||
<TaxSettingsForm settings={settings} />
|
||||
</SettingsFormWrapper>
|
||||
|
||||
{showSkatteverket && <SkatteverketConnectPanel />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user