Rename invoice-settings to company-settings, add its own Live Preview, and refine invoice PDF layout
Company data now has its own Payload admin group and a live in-browser PDF preview (react-pdf's PDFViewer) instead of just a plain settings form. Invoice header is a brand-colored rule instead of a filled band, and the footer is now pinned to the page bottom instead of following content flow.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
"use client";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import { useLivePreview } from "@payloadcms/live-preview-react";
|
||||
import { InvoiceDocument, SAMPLE_INVOICE_ORDER } from "../../lib/invoicePdf";
|
||||
import type { CompanySettings } from "../../lib/payload";
|
||||
|
||||
const PAYLOAD_URL = process.env.NEXT_PUBLIC_PAYLOAD_URL || "https://payload.mk360.de";
|
||||
|
||||
// @react-pdf/renderer's PDFViewer renders into an <iframe> via direct DOM
|
||||
// access — it has to be excluded from the server render pass entirely
|
||||
// (ssr: false), unlike the HTML-string email previews elsewhere in this
|
||||
// app, which can render server-side fine since they're just
|
||||
// dangerouslySetInnerHTML.
|
||||
const PDFViewer = dynamic(() => import("@react-pdf/renderer").then((mod) => mod.PDFViewer), { ssr: false });
|
||||
|
||||
// Same useLivePreview() mechanism as LiveEmailPreviewClient.tsx — connects
|
||||
// to the Payload admin's iframe via postMessage and updates `data` as the
|
||||
// admin edits company-settings fields, no save required. Renders through
|
||||
// the exact same InvoiceDocument component the real invoice PDF uses
|
||||
// (app/lib/invoicePdf.tsx), against a fixed sample order
|
||||
// (SAMPLE_INVOICE_ORDER) — there's no "current" real order to preview
|
||||
// against generically, same reasoning as the email-templates preview's
|
||||
// own SAMPLE_ORDER.
|
||||
export function LiveCompanySettingsPreviewClient({ initialSettings }: { initialSettings: CompanySettings }) {
|
||||
const { data } = useLivePreview<CompanySettings>({
|
||||
initialData: initialSettings,
|
||||
serverURL: PAYLOAD_URL,
|
||||
depth: 0,
|
||||
});
|
||||
|
||||
return (
|
||||
<PDFViewer style={{ width: "100%", height: "100vh", border: "none" }}>
|
||||
<InvoiceDocument order={SAMPLE_INVOICE_ORDER} seller={data} />
|
||||
</PDFViewer>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { Metadata } from "next";
|
||||
import { draftMode } from "next/headers";
|
||||
import { getCompanySettings, type CompanySettings } from "../lib/payload";
|
||||
import { LiveCompanySettingsPreviewClient } from "./components/LiveCompanySettingsPreviewClient";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Firmendaten-Vorschau",
|
||||
robots: { index: false, follow: false },
|
||||
};
|
||||
|
||||
const FALLBACK: CompanySettings = {
|
||||
sellerName: "",
|
||||
sellerStreet: "",
|
||||
sellerZip: "",
|
||||
sellerCity: "",
|
||||
sellerCountry: "",
|
||||
sellerEmail: "",
|
||||
vatId: "",
|
||||
taxRatePercent: 19,
|
||||
bankDetails: null,
|
||||
};
|
||||
|
||||
// Entered exclusively via CompanySettings.ts's admin.livePreview.url (a
|
||||
// Payload-admin-only iframe target, see buildPreviewUrl()/api/preview) —
|
||||
// not a page a real visitor would ever land on. Unlike email-templates
|
||||
// there's no draft/published distinction here (company-settings has no
|
||||
// content-versioning concept, it's just the current row) — the initial
|
||||
// fetch is the same live data getCompanySettings() always returns,
|
||||
// useLivePreview() takes over from there as the admin edits fields.
|
||||
export default async function CompanySettingsPreviewPage() {
|
||||
await draftMode();
|
||||
const initialSettings = (await getCompanySettings()) ?? FALLBACK;
|
||||
return <LiveCompanySettingsPreviewClient initialSettings={initialSettings} />;
|
||||
}
|
||||
Reference in New Issue
Block a user