Add local iOS release pipeline, fix shortcuts, and improve invoice UX.
Enable App Store builds without EAS, iOS 18 App Intents plugins, and signing fixes for distribution export. Add mobile invoice PDF preview, compact line items, and more reliable shortcut deep-link handling. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
import type { AppRouter } from "beenvoice/server/api/root";
|
||||
import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
|
||||
|
||||
export type InvoicePdfPreviewInput = inferRouterInputs<AppRouter>["invoices"]["previewPdf"];
|
||||
type InvoiceDetail = NonNullable<inferRouterOutputs<AppRouter>["invoices"]["getById"]>;
|
||||
|
||||
export type InvoicePdfFormFields = {
|
||||
invoiceNumber: string;
|
||||
invoicePrefix?: string | null;
|
||||
businessId?: string | null;
|
||||
clientId: string;
|
||||
issueDate: Date;
|
||||
dueDate: Date;
|
||||
status?: "draft" | "sent" | "paid";
|
||||
notes?: string | null;
|
||||
emailMessage?: string | null;
|
||||
taxRate: number;
|
||||
currency: string;
|
||||
items: Array<{
|
||||
date: Date;
|
||||
description: string;
|
||||
hours: number | string;
|
||||
rate: number | string;
|
||||
}>;
|
||||
};
|
||||
|
||||
export function buildPreviewPdfInput(fields: InvoicePdfFormFields): InvoicePdfPreviewInput | null {
|
||||
if (!fields.clientId.trim()) return null;
|
||||
|
||||
const items = fields.items.map((item) => ({
|
||||
date: item.date,
|
||||
description: item.description.trim() || "Service",
|
||||
hours: Number(item.hours) || 0,
|
||||
rate: Number(item.rate) || 0,
|
||||
}));
|
||||
|
||||
if (items.length === 0) return null;
|
||||
|
||||
return {
|
||||
invoiceNumber: fields.invoiceNumber.trim() || "DRAFT",
|
||||
invoicePrefix: fields.invoicePrefix?.trim() || "#",
|
||||
businessId: fields.businessId?.trim() || "",
|
||||
clientId: fields.clientId,
|
||||
issueDate: fields.issueDate,
|
||||
dueDate: fields.dueDate,
|
||||
status: fields.status ?? "draft",
|
||||
notes: fields.notes?.trim() ?? "",
|
||||
emailMessage: fields.emailMessage?.trim() ?? "",
|
||||
taxRate: fields.taxRate,
|
||||
currency: fields.currency,
|
||||
items,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildPreviewPdfInputFromInvoice(invoice: InvoiceDetail): InvoicePdfPreviewInput {
|
||||
return {
|
||||
invoiceNumber: invoice.invoiceNumber,
|
||||
invoicePrefix: invoice.invoicePrefix ?? "#",
|
||||
businessId: invoice.businessId ?? "",
|
||||
clientId: invoice.clientId,
|
||||
issueDate: new Date(invoice.issueDate),
|
||||
dueDate: new Date(invoice.dueDate),
|
||||
status: invoice.status as "draft" | "sent" | "paid",
|
||||
notes: invoice.notes ?? "",
|
||||
emailMessage: invoice.emailMessage ?? "",
|
||||
taxRate: invoice.taxRate,
|
||||
currency: invoice.currency ?? "USD",
|
||||
items: invoice.items.map((item) => ({
|
||||
date: new Date(item.date),
|
||||
description: item.description,
|
||||
hours: item.hours,
|
||||
rate: item.rate,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
export function canPreviewPdfInput(input: InvoicePdfPreviewInput | null): input is InvoicePdfPreviewInput {
|
||||
if (!input?.clientId) return false;
|
||||
return input.items.every((item) => item.description.trim().length > 0);
|
||||
}
|
||||
Reference in New Issue
Block a user