feat: refactor proforma creation components and remove unused empty lines card

This commit is contained in:
David Arranz 2026-08-18 12:39:46 +02:00
parent 4411b8f07e
commit cf5a797532
5 changed files with 106 additions and 126 deletions

View File

@ -1,6 +1,5 @@
export * from "./proforma-create-auto-applied-info"; export * from "./proforma-create-auto-applied-info";
export * from "./proforma-create-customer-field"; export * from "./proforma-create-customer-field";
export * from "./proforma-create-empty-lines-card";
export * from "./proforma-create-form"; export * from "./proforma-create-form";
export * from "./proforma-create-header"; export * from "./proforma-create-header";
export * from "./proforma-create-initial-info-card"; export * from "./proforma-create-initial-info-card";

View File

@ -1,80 +1,97 @@
import { import {
AlertCircleIcon,
BanknoteIcon, BanknoteIcon,
CheckCircle2Icon, CalculatorIcon,
CircleDollarSignIcon, CircleDollarSignIcon,
ReceiptTextIcon, CreditCardIcon,
InfoIcon,
ShieldCheckIcon,
SparklesIcon,
} from "lucide-react"; } from "lucide-react";
export const ProformaCreateAutoAppliedInfo = () => { export const ProformaCreateAutoAppliedInfo = () => {
return ( return (
<section className="rounded-xl border border-blue-200 bg-blue-50/60 p-6 text-slate-800"> <section className="rounded-2xl border border-primary/15 bg-primary/5 p-5 md:p-6">
<div className="flex items-start gap-3"> <div className="space-y-5">
<AlertCircleIcon className="mt-0.5 size-5 text-blue-600" /> <div className="flex items-start gap-3">
<div className="space-y-2"> <div className="flex size-10 shrink-0 items-center justify-center rounded-xl bg-background text-primary shadow-xs">
<div> <SparklesIcon className="size-5" />
<h2 className="text-lg font-semibold">Se preparará automáticamente</h2> </div>
<p className="text-sm text-slate-600"> <div className="min-w-0 space-y-1.5">
<h2 className="text-lg font-semibold text-foreground">Se preparará automáticamente</h2>
<p className="max-w-3xl text-sm leading-6 text-muted-foreground">
Estos valores iniciales se tomarán del cliente o de la configuración de empresa. Estos valores iniciales se tomarán del cliente o de la configuración de empresa.
Podrás revisarlos y modificarlos después en la edición. Podrás revisarlos y modificarlos después.
</p> </p>
</div> </div>
</div>
<div className="rounded-lg border border-blue-200/80 bg-white/60 p-4"> <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
<p className="text-sm font-medium text-slate-900">Datos que se guardarán al crear</p> <div className="rounded-xl border border-border/60 bg-background/80 p-4 shadow-xs">
<p className="mt-1 text-sm text-slate-600"> <div className="mb-3 flex size-9 items-center justify-center rounded-lg bg-primary/10 text-primary">
Fecha, cliente y serie de proforma para numerar el documento inicial. <CircleDollarSignIcon className="size-4.5" />
</p> </div>
<p className="text-sm font-medium text-foreground">Moneda</p>
<p className="mt-1 text-sm text-muted-foreground">EUR</p>
</div> </div>
<div className="grid gap-4 border-y border-blue-200/80 py-4 md:grid-cols-3"> <div className="rounded-xl border border-border/60 bg-background/80 p-4 shadow-xs">
<div className="flex items-start gap-3"> <div className="mb-3 flex size-9 items-center justify-center rounded-lg bg-primary/10 text-primary">
<CircleDollarSignIcon className="mt-0.5 size-5 text-blue-600" /> <CreditCardIcon className="size-4.5" />
<div>
<p className="font-medium">Moneda</p>
<p className="text-sm text-slate-600">EUR</p>
</div>
</div>
<div className="flex items-start gap-3">
<BanknoteIcon className="mt-0.5 size-5 text-blue-600" />
<div>
<p className="font-medium">Método de pago</p>
<p className="text-sm text-slate-600">Según cliente</p>
</div>
</div>
<div className="flex items-start gap-3">
<ReceiptTextIcon className="mt-0.5 size-5 text-blue-600" />
<div>
<p className="font-medium">Condiciones de pago</p>
<p className="text-sm text-slate-600">Según cliente</p>
</div>
</div> </div>
<p className="text-sm font-medium text-foreground">Método de pago</p>
<p className="mt-1 text-sm text-muted-foreground">Según cliente</p>
</div> </div>
<div className="flex items-start gap-3 pt-1"> <div className="rounded-xl border border-border/60 bg-background/80 p-4 shadow-xs md:col-span-2 xl:col-span-1">
<AlertCircleIcon className="mt-0.5 size-5 text-blue-600" /> <div className="mb-3 flex size-9 items-center justify-center rounded-lg bg-primary/10 text-primary">
<div> <BanknoteIcon className="size-4.5" />
<p className="font-medium">Configuración fiscal inicial</p> </div>
<p className="text-sm text-slate-600"> <p className="text-sm font-medium text-foreground">Condiciones de pago</p>
<p className="mt-1 text-sm text-muted-foreground">Según cliente</p>
</div>
</div>
<div className="grid gap-4 lg:grid-cols-2">
<div className="flex items-start gap-3 rounded-xl border border-border/50 bg-background/50 p-4">
<div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
<ShieldCheckIcon className="size-4.5" />
</div>
<div className="min-w-0 space-y-1">
<p className="text-sm font-medium text-foreground">Configuración fiscal inicial</p>
<p className="text-sm leading-6 text-muted-foreground">
Se tomará del cliente o de la configuración de empresa. Los impuestos y totales se Se tomará del cliente o de la configuración de empresa. Los impuestos y totales se
calcularán cuando añadas líneas a la proforma. calcularán al añadir líneas.
</p> </p>
</div> </div>
</div> </div>
<div className="flex items-start gap-3 rounded-lg bg-white/60 p-4"> <div className="flex items-start gap-3 rounded-xl border border-border/50 bg-background/50 p-4">
<CheckCircle2Icon className="mt-0.5 size-5 text-blue-600" /> <div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
<div> <CalculatorIcon className="size-4.5" />
<p className="font-medium">Cálculo pendiente</p> </div>
<p className="text-sm text-slate-600"> <div className="min-w-0 space-y-1">
Al crear sin líneas se guarda la cabecera. El desglose fiscal real se materializa <p className="text-sm font-medium text-foreground">Cálculo pendiente</p>
en la edición, cuando existan líneas valoradas. <p className="text-sm leading-6 text-muted-foreground">
Los impuestos y totales se calcularán cuando añadas líneas a la proforma.
</p> </p>
</div> </div>
</div> </div>
</div> </div>
<div className="flex items-start gap-3 rounded-xl border border-primary/10 bg-background/70 p-4">
<div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
<InfoIcon className="size-4.5" />
</div>
<div className="min-w-0 space-y-1">
<p className="text-sm font-medium text-foreground">
Las líneas de detalle se añadirán en la siguiente pantalla
</p>
<p className="text-sm leading-6 text-muted-foreground">
La proforma se creará sin líneas. Podrás añadir productos o servicios tras crear el
borrador.
</p>
</div>
</div>
</div> </div>
</section> </section>
); );

View File

@ -1,31 +0,0 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@repo/shadcn-ui/components";
import { ListIcon } from "lucide-react";
export const ProformaCreateEmptyLinesCard = () => {
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-xl">
<ListIcon className="size-5 text-primary" />
Líneas de detalle
</CardTitle>
<CardDescription className="text-base">
La proforma se creará sin líneas. Podrás añadir productos o servicios en la siguiente
pantalla.
</CardDescription>
</CardHeader>
<CardContent className="flex min-h-64 flex-col items-center justify-center gap-4 text-center">
<div className="flex size-20 items-center justify-center rounded-full bg-muted text-muted-foreground">
<ListIcon className="size-9" />
</div>
<div className="space-y-1">
<p className="text-2xl font-semibold tracking-tight">Sin líneas de detalle</p>
<p className="text-muted-foreground">
Añade productos o servicios después de crear el borrador.
</p>
</div>
</CardContent>
</Card>
);
};

View File

@ -2,7 +2,6 @@ import type { CustomerSelectionOption } from "@erp/customers";
import { preventEnterKeySubmitForm } from "@repo/rdx-ui/helpers"; import { preventEnterKeySubmitForm } from "@repo/rdx-ui/helpers";
import { ProformaCreateAutoAppliedInfo } from "./proforma-create-auto-applied-info"; import { ProformaCreateAutoAppliedInfo } from "./proforma-create-auto-applied-info";
import { ProformaCreateEmptyLinesCard } from "./proforma-create-empty-lines-card";
import { ProformaCreateInitialInfoCard } from "./proforma-create-initial-info-card"; import { ProformaCreateInitialInfoCard } from "./proforma-create-initial-info-card";
interface ProformaCreateFormProps { interface ProformaCreateFormProps {
@ -54,8 +53,6 @@ export const ProformaCreateForm = ({
/> />
<ProformaCreateAutoAppliedInfo /> <ProformaCreateAutoAppliedInfo />
<ProformaCreateEmptyLinesCard />
</div> </div>
</form> </form>
); );

View File

@ -32,46 +32,44 @@ export const ProformaCreatePage = () => {
<div className="flex-1 overflow-y-auto bg-muted/20"> <div className="flex-1 overflow-y-auto bg-muted/20">
<RdxEditorPageLayout complexity="simple"> <RdxEditorPageLayout complexity="simple">
<div className="rounded-2xl border border-border/60 bg-background px-4 py-4 shadow-xs md:px-6 md:py-6"> <div className="flex min-w-0 flex-col gap-6">
<div className="flex min-w-0 flex-col gap-6"> {createCtrl.isCreateError && (
{createCtrl.isCreateError && ( <ErrorAlert
<ErrorAlert message={
message={ createCtrl.createError instanceof Error
createCtrl.createError instanceof Error ? createCtrl.createError.message
? createCtrl.createError.message : "Revisa los datos e inténtalo de nuevo."
: "Revisa los datos e inténtalo de nuevo." }
} title="No se pudo crear la proforma"
title="No se pudo crear la proforma"
/>
)}
{createCtrl.isSeriesLoadError && (
<ErrorAlert
message={
createCtrl.seriesLoadError instanceof Error
? createCtrl.seriesLoadError.message
: "Inténtalo de nuevo más tarde."
}
title="No se pudieron cargar las series de facturación"
/>
)}
<ProformaCreateForm
formId={createCtrl.formId}
isPaymentTermsLoading={createCtrl.isPaymentTermsLoading}
isProformaSeriesLoading={createCtrl.isProformaSeriesLoading}
isSubmitting={createCtrl.isCreating}
isTargetInvoiceSeriesLoading={createCtrl.isTargetInvoiceSeriesLoading}
onClearCustomer={createCtrl.clearCustomer}
onPaymentTermChange={createCtrl.handlePaymentTermChange}
onSelectCustomer={selectCustomerCtrl.selectCtrl.openDialog}
onSubmit={createCtrl.onSubmit}
paymentTermOptions={createCtrl.paymentTermOptions}
proformaSeriesOptions={createCtrl.proformaSeriesOptions}
selectedCustomer={createCtrl.selectedCustomer}
targetInvoiceSeriesOptions={createCtrl.targetInvoiceSeriesOptions}
/> />
</div> )}
{createCtrl.isSeriesLoadError && (
<ErrorAlert
message={
createCtrl.seriesLoadError instanceof Error
? createCtrl.seriesLoadError.message
: "Inténtalo de nuevo más tarde."
}
title="No se pudieron cargar las series de facturación"
/>
)}
<ProformaCreateForm
formId={createCtrl.formId}
isPaymentTermsLoading={createCtrl.isPaymentTermsLoading}
isProformaSeriesLoading={createCtrl.isProformaSeriesLoading}
isSubmitting={createCtrl.isCreating}
isTargetInvoiceSeriesLoading={createCtrl.isTargetInvoiceSeriesLoading}
onClearCustomer={createCtrl.clearCustomer}
onPaymentTermChange={createCtrl.handlePaymentTermChange}
onSelectCustomer={selectCustomerCtrl.selectCtrl.openDialog}
onSubmit={createCtrl.onSubmit}
paymentTermOptions={createCtrl.paymentTermOptions}
proformaSeriesOptions={createCtrl.proformaSeriesOptions}
selectedCustomer={createCtrl.selectedCustomer}
targetInvoiceSeriesOptions={createCtrl.targetInvoiceSeriesOptions}
/>
</div> </div>
</RdxEditorPageLayout> </RdxEditorPageLayout>
</div> </div>