'use client' import { useState, useRef, useEffect } from 'react' import { useTranslations } from 'next-intl' import { Deadline } from '@/types' import { Button } from '@/components/ui/button' import { cn } from '@/lib/utils' import { isDeadlineOverdue, parseDate } from '@/lib/calendar/utils' import { Check, Pencil } from 'lucide-react' /** * System tax deadlines that legally share the skattekonto date ("den 12:e"): * for a small monthly-moms employer, moms + AGI (+ debiterad preliminärskatt) * all fall due the same day. Rendering them as one grouped card instead of * 2-3 identical-date rows keeps the list scannable. */ export const SKATTEKONTO_GROUP_TYPES = new Set([ 'moms_monthly', 'moms_quarterly', 'moms_yearly', 'f_skatt', 'arbetsgivardeklaration', 'skatteinbetalning', ]) export function isSkattekontoDeadline(d: Deadline): boolean { return ( !d.is_completed && d.source === 'system' && d.tax_deadline_type !== null && SKATTEKONTO_GROUP_TYPES.has(d.tax_deadline_type) ) } const SWEDISH_MONTHS_SHORT = [ 'jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec', ] interface DeadlineGroupCardProps { /** Two or more skattekonto deadlines sharing the same due_date. */ deadlines: Deadline[] onToggle: (deadline: Deadline) => void onEdit?: (deadline: Deadline) => void } /** * One card for all skattekonto obligations on a shared due date, with the * date block rendered once and each obligation as a sub-row that keeps its * own "Markera klar" confirmation. Visual language mirrors DeadlineCard. */ export function DeadlineGroupCard({ deadlines, onToggle, onEdit }: DeadlineGroupCardProps) { const t = useTranslations('deadlines') const [confirmingId, setConfirmingId] = useState(null) const confirmRef = useRef(null) const first = deadlines[0] const dueDate = parseDate(first.due_date) const dayNum = dueDate.getDate() const monthStr = SWEDISH_MONTHS_SHORT[dueDate.getMonth()] const overdue = deadlines.some(isDeadlineOverdue) useEffect(() => { if (!confirmingId) return function handleClickOutside(e: MouseEvent) { if (confirmRef.current && !confirmRef.current.contains(e.target as Node)) { setConfirmingId(null) } } document.addEventListener('mousedown', handleClickOutside) return () => document.removeEventListener('mousedown', handleClickOutside) }, [confirmingId]) const confirming = deadlines.find((d) => d.id === confirmingId) ?? null return (
{/* Shared date block */}
{dayNum} {monthStr}
{/* Group content */}

{t('group_skattekonto_title')}

{t('group_same_day', { count: deadlines.length })}
{deadlines.map((deadline) => (
{ if (!confirmingId && onEdit) onEdit(deadline) }} className={cn( 'group flex items-center gap-3 rounded-md py-1.5 -mx-2 px-2 transition-colors', onEdit && !confirmingId && 'cursor-pointer hover:bg-accent/40', )} >

{deadline.title}

{onEdit && !confirmingId && ( )}
))}
{/* Inline confirmation bar (one at a time, mirrors DeadlineCard) */}
{confirming && (

{t.rich('group_confirm_question', { title: confirming.title, hl: (chunks) => ( {chunks} ), })}

)}
) }