Files
accounted/components/invoices/NewInvoiceDialog.tsx
T
Jakob WennbergandClaude Fable 5 86071334cb feat(invoices): create customer & supplier invoices in modals, matching the verifikat pattern (#861)
Invoice and supplier-invoice creation now open as pop-up dialogs on their
list pages instead of navigating to standalone form pages — the same UX as
NewJournalEntryDialog (capped-height scroll, explicit-close-only so a
half-typed invoice survives stray Escape/backdrop clicks).

- InvoiceEditor gains a `bare` variant (page chrome stripped, inline actions
  replacing the fixed mobile bar, live document-type title kept) hosted by
  the new NewInvoiceDialog (sm:max-w-5xl). Draft editing pages unchanged.
- The 2,057-line supplier form moves out of the route page into
  components/supplier-invoices/NewSupplierInvoiceForm.tsx with
  bare/inboxItemId/onCreated/onCancel props, hosted by
  NewSupplierInvoiceDialog (sm:max-w-4xl).
- Modals are URL-driven (?new=1): header buttons, empty states, command
  palette, and the reports CTA all open the same dialog; browser back
  closes it. /invoices/new and /supplier-invoices/new survive as redirects
  (bookmarks, agent intents, /expenses/new alias, inbox deep links).
- The invoice-inbox "Skapa leverantörsfaktura" action opens the modal in
  place and refreshes the inbox on success instead of navigating away.

No new i18n keys; dialog titles reuse existing strings.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 14:04:52 +02:00

44 lines
1.7 KiB
TypeScript

'use client'
import { useTranslations } from 'next-intl'
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'
import InvoiceEditor from '@/components/invoices/InvoiceEditor'
interface Props {
open: boolean
onOpenChange: (open: boolean) => void
}
/**
* "Ny faktura" as a modal — mirrors NewJournalEntryDialog. Wraps the bare
* InvoiceEditor; the editor's own review/confirm/send dialogs stack on top of
* this one, and every successful create navigates to the invoice detail page
* (unmounting the host list page and this dialog with it).
*
* The accessible title is visually hidden: the bare editor renders its own
* live heading, which tracks document type (faktura/proforma/följesedel) and
* shows the invoice-number preview — a static DialogTitle would duplicate or
* contradict it.
*/
export default function NewInvoiceDialog({ open, onOpenChange }: Props) {
const t = useTranslations('invoice_editor')
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent
className="sm:max-w-5xl max-h-[95dvh] sm:max-h-[90vh] overflow-y-auto"
// A half-typed invoice must survive an accidental backdrop click or a
// stray Escape (nested comboboxes and date pickers portal outside the
// dialog). Closing is explicit — the header X. Same convention as
// NewJournalEntryDialog.
onEscapeKeyDown={(e) => e.preventDefault()}
onPointerDownOutside={(e) => e.preventDefault()}
onInteractOutside={(e) => e.preventDefault()}
>
<DialogTitle className="sr-only">{t('title_invoice')}</DialogTitle>
<InvoiceEditor mode="create" bare />
</DialogContent>
</Dialog>
)
}