diff --git a/app/(auth)/mfa/enroll/page.tsx b/app/(auth)/mfa/enroll/page.tsx
index 50c08839..16694131 100644
--- a/app/(auth)/mfa/enroll/page.tsx
+++ b/app/(auth)/mfa/enroll/page.tsx
@@ -7,7 +7,7 @@ import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { useToast } from '@/components/ui/use-toast'
-import { Loader2, ShieldCheck, Copy, Check } from 'lucide-react'
+import { Loader2, ShieldCheck, Copy, Check, ArrowLeft } from 'lucide-react'
export default function MfaEnrollPage() {
return (
@@ -31,12 +31,23 @@ function MfaEnrollContent() {
const searchParams = useSearchParams()
const supabase = createClient()
- const returnTo = searchParams.get('returnTo') || '/'
+ const rawReturnTo = searchParams.get('returnTo') || '/'
+ const returnTo = rawReturnTo.startsWith('/') && !rawReturnTo.startsWith('//') ? rawReturnTo : '/'
const handleEnroll = async () => {
setIsEnrolling(true)
try {
+ // Clean up any stale unverified factors from previous abandoned attempts
+ const { data: existingFactors } = await supabase.auth.mfa.listFactors()
+ if (existingFactors?.totp) {
+ for (const factor of existingFactors.totp) {
+ if (factor.status !== 'verified') {
+ await supabase.auth.mfa.unenroll({ factorId: factor.id })
+ }
+ }
+ }
+
const { data, error } = await supabase.auth.mfa.enroll({
factorType: 'totp',
friendlyName: 'gnubok',
@@ -45,7 +56,7 @@ function MfaEnrollContent() {
if (error) {
toast({
title: 'Kunde inte aktivera 2FA',
- description: 'Försök igen senare.',
+ description: error.message,
variant: 'destructive',
})
setIsEnrolling(false)
@@ -174,6 +185,15 @@ function MfaEnrollContent() {
+
+
)
@@ -264,6 +284,15 @@ function MfaEnrollContent() {
+
+
)
diff --git a/app/(auth)/mfa/verify/page.tsx b/app/(auth)/mfa/verify/page.tsx
index 20a2ea7c..45d8112e 100644
--- a/app/(auth)/mfa/verify/page.tsx
+++ b/app/(auth)/mfa/verify/page.tsx
@@ -8,6 +8,7 @@ import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { useToast } from '@/components/ui/use-toast'
import { Loader2, ShieldCheck, LogOut } from 'lucide-react'
+import { SupportLink } from '@/components/ui/support-link'
export default function MfaVerifyPage() {
const [code, setCode] = useState('')
@@ -151,6 +152,13 @@ export default function MfaVerifyPage() {
+ Förlorat din autentiseringsapp?{' '}
+
Årsbokslut är under utveckling och kommer att finnas tillgängligt i en kommande version.
+
- Ett oväntat fel uppstod. Försök igen eller kontakta support om problemet
- kvarstår.
+ Ett oväntat fel uppstod. Försök igen eller{' '}
+
Enligt bokföringslagen (BFL 7 kap. 2§) ska räkenskapsinformation bevaras i 7 år. Du ansvarar själv för att exportera och arkivera din bokföringsdata innan du raderar kontot.
+
+ Har du frågor?{' '}
+
Från: ${escapeHtml(user.email || '')}
+User ID: ${user.id}
+Ämne: ${safeSubject}
+${safeMessage}
+ `, + text: `Från: ${user.email}\nUser ID: ${user.id}\nÄmne: ${subject}\n\n${message}`, + }) + + if (!result.success) { + return NextResponse.json( + { error: 'Kunde inte skicka meddelandet. Försök igen.' }, + { status: 500 } + ) + } + + return NextResponse.json({ data: { sent: true } }) +} diff --git a/components/dashboard/DashboardNav.tsx b/components/dashboard/DashboardNav.tsx index 9a8e54d6..7b4c3ce9 100644 --- a/components/dashboard/DashboardNav.tsx +++ b/components/dashboard/DashboardNav.tsx @@ -28,6 +28,7 @@ import { ClipboardCheck, } from 'lucide-react' import { resolveIcon } from '@/lib/extensions/icon-resolver' +import { SupportLink } from '@/components/ui/support-link' import type { EntityType } from '@/types' interface ExtensionNavItem { @@ -304,8 +305,11 @@ export default function DashboardNav({ companyName, entityType, uncategorizedTra - {/* Logout button */} -