feat: add Recapt analytics, remove push-notifications extension, UI fixes

- Integrate Recapt session tracking with user identity in dashboard layout
- Remove push-notifications extension from settings, panel registry, and toggle list
- Fix journal entry preview overflow on narrow viewports
- Update transaction manual booking button label
- Clarify invoice email error message to reference env vars

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-03-05 10:12:26 +01:00
parent 325c930839
commit 497ef876bb
9 changed files with 64 additions and 32 deletions
+6
View File
@@ -2,6 +2,7 @@ import { createClient } from '@/lib/supabase/server'
import { redirect } from 'next/navigation'
import DashboardNav from '@/components/dashboard/DashboardNav'
import { ChatWidget } from '@/components/chat'
import { RecaptIdentify } from '@/components/RecaptIdentify'
import type { EntityType } from '@/types'
export default async function DashboardLayout({
@@ -55,6 +56,11 @@ export default async function DashboardLayout({
</div>
</main>
<ChatWidget />
<RecaptIdentify
userId={user.id}
email={user.email}
displayName={settings.company_name || undefined}
/>
</div>
)
}
+1 -23
View File
@@ -35,7 +35,6 @@ import type { CompanySettings } from '@/types'
import { CalendarFeedSettings } from '@/components/settings/CalendarFeedSettings'
import { getSettingsPanel } from '@/lib/extensions/settings-panel-registry'
const NotificationPanel = getSettingsPanel('push-notifications')
const BankingPanel = getSettingsPanel('enable-banking')
export default function SettingsPage() {
@@ -48,7 +47,6 @@ export default function SettingsPage() {
const [isSaving, setIsSaving] = useState(false)
const [settings, setSettings] = useState<CompanySettings | null>(null)
const [hasBankingExtension, setHasBankingExtension] = useState(false)
const [hasNotificationExtension, setHasNotificationExtension] = useState(false)
const [hasCalendarExtension, setHasCalendarExtension] = useState(false)
const [showDeleteDialog, setShowDeleteDialog] = useState(false)
const [deleteConfirmText, setDeleteConfirmText] = useState('')
@@ -127,9 +125,8 @@ export default function SettingsPage() {
setSettings(settingsData)
// Check extension toggles in parallel
const [bankingToggleRes, notificationToggleRes, calendarToggleRes] = await Promise.all([
const [bankingToggleRes, calendarToggleRes] = await Promise.all([
fetch('/api/extensions/toggles/general/enable-banking').catch(() => null),
fetch('/api/extensions/toggles/general/push-notifications').catch(() => null),
fetch('/api/extensions/toggles/general/calendar').catch(() => null),
])
@@ -147,11 +144,6 @@ export default function SettingsPage() {
setHasBankingExtension((connections && connections.length > 0) || false)
}
if (notificationToggleRes?.ok) {
const { data } = await notificationToggleRes.json()
setHasNotificationExtension(data?.enabled || false)
}
if (calendarToggleRes?.ok) {
const { data } = await calendarToggleRes.json()
setHasCalendarExtension(data?.enabled || false)
@@ -271,11 +263,6 @@ export default function SettingsPage() {
<TabsTrigger value="banking">
Bank (PSD2)
</TabsTrigger>
{hasNotificationExtension && (
<TabsTrigger value="notifications">
Aviseringar
</TabsTrigger>
)}
{hasCalendarExtension && (
<TabsTrigger value="calendar">
Kalender
@@ -532,15 +519,6 @@ export default function SettingsPage() {
)}
</TabsContent>
{/* Notification settings — loaded dynamically from extension */}
{hasNotificationExtension && (
<TabsContent value="notifications">
{NotificationPanel ? (
<NotificationPanel />
) : null}
</TabsContent>
)}
{/* Calendar feed settings */}
{hasCalendarExtension && (
<TabsContent value="calendar">
+1 -1
View File
@@ -748,7 +748,7 @@ export default function TransactionsPage() {
/>
<div className="pt-2 border-t">
<Button variant="ghost" size="sm" className="w-full text-muted-foreground" onClick={handleManualBooking}>
Bokför manuellt utan mall...
Ange konton manuellt...
</Button>
</div>
</DialogContent>
+1 -1
View File
@@ -33,7 +33,7 @@ export async function POST(
const emailService = getEmailService()
if (!emailService.isConfigured()) {
return NextResponse.json(
{ error: 'E-posttjänsten är inte konfigurerad. Aktivera e-posttillägget i inställningar.' },
{ error: 'E-posttjänsten är inte konfigurerad. Kontrollera att RESEND_API_KEY och RESEND_FROM_EMAIL är satta i miljövariablerna.' },
{ status: 503 }
)
}
+7
View File
@@ -62,6 +62,13 @@ export default function RootLayout({
<Toaster />
</ThemeProvider>
<Script src="/sw-register.js" strategy="afterInteractive" />
<Script
src="https://cdn.recapt.app/browser/glimt.js"
strategy="afterInteractive"
data-public-key="pk_8de220ce34c81413de154d10ff681a9eb3a5a9c12d28bd6c7bc2613c9f5acfbb"
data-persist
data-enable-user-comments
/>
</body>
</html>
);
+45
View File
@@ -0,0 +1,45 @@
'use client'
import { useEffect } from 'react'
declare global {
interface Window {
Recapt?: {
session: {
setIdentity: (identity: {
uid: string
email?: string
nickname?: string
fullName?: string
}) => void
}
}
}
}
export function RecaptIdentify({
userId,
email,
displayName,
}: {
userId: string
email?: string
displayName?: string
}) {
useEffect(() => {
const interval = setInterval(() => {
if (window.Recapt) {
window.Recapt.session.setIdentity({
uid: userId,
email: email,
fullName: displayName,
})
clearInterval(interval)
}
}, 500)
return () => clearInterval(interval)
}, [userId, email, displayName])
return null
}
@@ -98,11 +98,11 @@ export default function JournalEntryPreview({
if (lines.length === 0) return null
return (
<div className="rounded-lg border bg-muted/30 px-3 py-2.5">
<div className="rounded-lg border bg-muted/30 px-3 py-2.5 overflow-hidden">
<p className="text-xs font-medium text-muted-foreground mb-1.5">Verifikation</p>
<div className="space-y-0.5 font-mono text-xs">
<div className="space-y-0.5 font-mono text-xs min-w-0">
{lines.map((line, i) => (
<div key={i} className="flex items-baseline gap-2">
<div key={i} className="flex items-baseline gap-2 min-w-0">
<span className={`w-12 text-right flex-shrink-0 ${line.side === 'debet' ? 'text-foreground' : 'text-muted-foreground'}`}>
{line.side === 'debet' ? 'Debet' : 'Kredit'}
</span>
@@ -10,9 +10,6 @@ import type { ComponentType } from 'react'
*/
const SETTINGS_PANELS: Record<string, ComponentType> = {
'push-notifications': dynamic(
() => import('@/extensions/general/push-notifications/NotificationSettings').then(m => ({ default: m.NotificationSettings }))
),
'enable-banking': dynamic(
() => import('@/extensions/general/enable-banking/components/BankingSettingsPanel')
),
-1
View File
@@ -12,7 +12,6 @@ const LEGACY_GENERAL_EXTENSIONS = [
'receipt-ocr',
'ai-categorization',
'ai-chat',
'push-notifications',
'enable-banking',
]