feat(invoices): expose the automatic reminder kill switch in settings (#1476)

* feat(invoices): expose the automatic reminder kill switch in settings

The send_invoice_reminders column, API schema, and cron processor check
already existed, but no UI ever exposed the toggle. Add a switch in
Settings -> Fakturering (day thresholds fold away when off, values
preserved), and make the invoice detail Paminnelser card say reminders
are off instead of promising emails that will never be sent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(invoices): do not assume reminders enabled before settings load

CodeRabbit finding on PR 1476: with no company_settings row loaded, the
Paminnelser card defaulted to promising the reminder schedule. Track the
toggle as boolean | null and render no schedule text until the settings
row has actually resolved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mattsson
2026-08-10 11:24:15 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent 741cdcc60e
commit d4b0bd3df1
5 changed files with 81 additions and 48 deletions
+14 -7
View File
@@ -169,6 +169,8 @@ export default function InvoiceDetailPage({ params }: { params: Promise<{ id: st
const [showBookConfirm, setShowBookConfirm] = useState(false)
const [bookVoucherPreview, setBookVoucherPreview] = useState<string | null>(null)
const [reminderDays, setReminderDays] = useState<[number, number, number]>([15, 30, 45])
// null = settings row not loaded; don't promise a reminder schedule then.
const [autoRemindersEnabled, setAutoRemindersEnabled] = useState<boolean | null>(null)
const statusLabel = (status: InvoiceStatus): string => t(`status_${status}`)
const reminderLevelLabel = (level: 1 | 2 | 3): string => t(`reminder_level_${level}`)
@@ -213,7 +215,7 @@ export default function InvoiceDetailPage({ params }: { params: Promise<{ id: st
const settingsPromise = company?.id
? supabase
.from('company_settings')
.select('ore_rounding, vat_registered, accounting_method, defer_invoice_booking, reminder_days_level_1, reminder_days_level_2, reminder_days_level_3')
.select('ore_rounding, vat_registered, accounting_method, defer_invoice_booking, reminder_days_level_1, reminder_days_level_2, reminder_days_level_3, send_invoice_reminders')
.eq('company_id', company.id)
.maybeSingle()
: Promise.resolve(null)
@@ -311,6 +313,9 @@ export default function InvoiceDetailPage({ params }: { params: Promise<{ id: st
settings?.reminder_days_level_2 ?? 30,
settings?.reminder_days_level_3 ?? 45,
])
if (settings) {
setAutoRemindersEnabled(settings.send_invoice_reminders ?? true)
}
}
// Related documents need the invoice row but do not gate the main detail
@@ -1480,13 +1485,15 @@ export default function InvoiceDetailPage({ params }: { params: Promise<{ id: st
<Bell className="h-5 w-5" />
{t('reminders_card_title')}
</CardTitle>
{reminders.length === 0 && (
{reminders.length === 0 && autoRemindersEnabled !== null && (
<CardDescription>
{t('reminders_description', {
day1: reminderDays[0],
day2: reminderDays[1],
day3: reminderDays[2],
})}
{autoRemindersEnabled
? t('reminders_description', {
day1: reminderDays[0],
day2: reminderDays[1],
day3: reminderDays[2],
})
: t('reminders_disabled')}
</CardDescription>
)}
</CardHeader>