'use client'
import * as React from 'react'
import Link from 'next/link'
import { useTranslations } from 'next-intl'
import { Button } from '@/components/ui/button'
import { cn } from '@/lib/utils'
import {
ReceiptText,
Users,
ArrowLeftRight,
Building2,
FileText,
Calendar,
Plus,
TrendingUp,
type LucideIcon,
} from 'lucide-react'
import { SupportLink } from '@/components/ui/support-link'
interface EmptyStateProps {
icon?: LucideIcon
/**
* Usually static i18n strings. The empty state is data-ph-unmask chrome in
* session replays, so a title or description carrying user data (e.g. an
* interpolated search term) must wrap that part in a data-ph-mask element.
*/
title: React.ReactNode
description: React.ReactNode
actionLabel?: string
actionHref?: string
onAction?: () => void
secondaryActionLabel?: string
secondaryActionHref?: string
supportHint?: boolean
className?: string
children?: React.ReactNode
}
/**
* EmptyState: friendly placeholder shown when there is no data.
*/
export function EmptyState({
icon: Icon,
title,
description,
actionLabel,
actionHref,
onAction,
secondaryActionLabel,
secondaryActionHref,
supportHint,
className,
children,
}: EmptyStateProps) {
const t = useTranslations('empty')
return (
// data-ph-unmask: empty states are static i18n chrome in session replays.
)
}
// Preset empty states for common pages
export function EmptyInvoices({ onAction }: { onAction?: () => void } = {}) {
const t = useTranslations('empty')
return (
)
}
export function EmptyCustomers({ onAction }: { onAction?: () => void } = {}) {
const t = useTranslations('empty')
return (
)
}
export function EmptyTransactions() {
const t = useTranslations('empty')
return (
)
}
export function EmptyDeadlines() {
const t = useTranslations('empty')
return (
)
}
export function NoBankConnected() {
const t = useTranslations('empty')
return (
)
}
export function EmptyReports() {
const t = useTranslations('empty')
return (
)
}
/**
* Byrå cockpit: no client companies yet. A preset, not a bare , because the only caller is a Server Component: a
* lucide icon is a forwardRef object that cannot cross the RSC boundary as a
* prop, while a reference to this client component can. The copy lives in the
* byra namespace, where the byrå surfaces already keep it.
*/
export function EmptyByraClients() {
// Named tByra, not t: this is the only preset here that reads from a
// namespace other than `empty`, and i18n/__tests__/message-keys.test.ts maps
// one variable name to one namespace per file. Reusing `t` would silently
// re-point every other preset's key in this file at `byra`.
const tByra = useTranslations('byra')
return (
)
}