'use client' import Image from 'next/image' import { useTranslations } from 'next-intl' import { useState, useEffect, useCallback } from 'react' import { createClient } from '@/lib/supabase/client' import { BankIdAuth } from '@/components/auth/BankIdAuth' import type { BankIdResult } from '@/components/auth/BankIdAuth' import { Button } from '@/components/ui/button' import { Loader2 } from 'lucide-react' import { useToast } from '@/components/ui/use-toast' import { formatDateLong } from '@/lib/utils' import { SettingsRow, SettingsRowEnd, SettingsRowNote, } from '@/components/settings/SettingsRows' interface BankIdIdentity { given_name: string | null surname: string | null linked_at: string } export function BankIdSettings() { const t = useTranslations('settings_bankid') const [identity, setIdentity] = useState(null) const [isLoading, setIsLoading] = useState(true) const [isLinking, setIsLinking] = useState(false) const [isUnlinking, setIsUnlinking] = useState(false) const { toast } = useToast() const fetchIdentity = useCallback(async () => { const supabase = createClient() const { data: { user } } = await supabase.auth.getUser() if (!user) { setIsLoading(false); return } const { data } = await supabase .from('bankid_identities') .select('given_name, surname, linked_at') .eq('user_id', user.id) .maybeSingle() setIdentity(data) setIsLoading(false) }, []) useEffect(() => { fetchIdentity() }, [fetchIdentity]) const handleLinkComplete = async (result: BankIdResult) => { if (result.error) { const message = result.error === 'already_linked' ? t('toast_already_linked') : t('toast_link_failed') toast({ title: message, variant: 'destructive' }) setIsLinking(false) return } toast({ title: t('toast_linked') }) setIsLinking(false) fetchIdentity() } const handleUnlink = async () => { if (!confirm(t('confirm_unlink'))) return setIsUnlinking(true) try { const res = await fetch('/api/extensions/ext/tic/bankid/unlink', { method: 'POST' }) if (!res.ok) throw new Error('Unlink failed') setIdentity(null) toast({ title: t('toast_unlinked') }) } catch { toast({ title: t('toast_unlink_failed'), variant: 'destructive' }) } finally { setIsUnlinking(false) } } if (isLoading) { return ( ) } return ( <> {t('title')} } help={identity ? t('linked_description') : t('not_linked_description')} borderless={isLinking} > {identity ? ( <> {identity.given_name} {identity.surname} {t('linked_on', { date: formatDateLong(identity.linked_at) })} ) : isLinking ? ( // Active flow: the scan instruction is the actionable content and // stays visible while the QR block below is open. {t('link_bankid_description')} ) : ( )} {/* QR flow: an expanding block below the row. Mounted only while linking so the BankID session starts exactly when requested. */} {isLinking && (
{/* BankIdAuth's own Avbryt only resets its internal session; give the row an exit so isLinking can't get stuck. */}
)} ) }