PROFORMAS UDPATE

This commit is contained in:
David Arranz 2026-04-10 10:05:45 +02:00
parent 89ab94bb95
commit 2eab047e90
9 changed files with 304 additions and 269 deletions

View File

@ -176,9 +176,15 @@
"description": "Create a new customer proforma",
"back_to_list": "Back to the list"
},
"edit": {
"update": {
"title": "Edit customer proforma",
"description": "Edit the selected customer proforma"
"description": "Edit the selected customer proforma",
"load_error.message": "Please try again later",
"load_error.title": "Unable to load the proforma",
"not_found_msg": "Check the identifier or go back to the list",
"not_found_title": "Proforma not found",
"error_msg": "Check the data and try again.",
"error_title": "Unable to save the changes"
},
"delete": {
"title": "Delete customer proforma",
@ -222,137 +228,141 @@
}
},
"form_groups": {
"customer": {
"title": "Customer",
"description": "Select the customer for this proforma."
},
"items": {
"title": "Proforma details",
"description": ""
},
"basic_info": {
"title": "Proforma information",
"description": "Basic proforma information"
},
"totals": {
"title": "Proforma totals",
"description": "Breakdown of proforma amounts with discounts and taxes."
},
"tax_resume": {
"title": "Resumen de impuestos",
"description": ""
},
"preferences": {
"title": "Preferences",
"description": "Additional proforma settings"
"proformas": {
"customer": {
"title": "Customer",
"description": "Select the customer for this proforma."
},
"items": {
"title": "Proforma details",
"description": ""
},
"basic_info": {
"title": "Proforma information",
"description": "Basic proforma information"
},
"totals": {
"title": "Proforma totals",
"description": "Breakdown of proforma amounts with discounts and taxes."
},
"tax_resume": {
"title": "Resumen de impuestos",
"description": ""
},
"preferences": {
"title": "Preferences",
"description": "Additional proforma settings"
}
}
},
"form_fields": {
"status": {
"label": "Estado",
"placeholder": "",
"description": ""
},
"invoice_number": {
"label": "Proforma number",
"placeholder": "",
"description": ""
},
"invoice_date": {
"label": "Proforma date",
"placeholder": "Select a date",
"description": "Proforma date"
},
"series": {
"label": "Serie",
"placeholder": "",
"description": "Proforma serie"
},
"operation_date": {
"label": "Operation date",
"placeholder": "Select a date",
"description": "Proforma operation date"
},
"reference": {
"label": "Reference",
"placeholder": "Reference of the proforma",
"description": "Reference of the proforma"
},
"description": {
"label": "Description",
"placeholder": "Description of the proforma",
"description": "General description of the proforma"
},
"subtotal_amount": {
"label": "Subtotal",
"placeholder": "",
"desc": "Proforma subtotal"
},
"discount": {
"label": "Discount (%)",
"placeholder": "",
"desc": "Percentage discount"
},
"discount_amount": {
"label": "Discount price",
"placeholder": "",
"desc": "Percentage discount price"
},
"total_amount": {
"label": "Total price",
"placeholder": "",
"desc": "Proforma total price"
},
"notes": {
"label": "Notes",
"placeholder": "Additional notes about the proforma",
"description": "Additional notes that can be included in the proforma"
},
"item": {
"quantity": {
"label": "Quantity",
"proformas": {
"status": {
"label": "Estado",
"placeholder": "",
"description": ""
},
"invoice_number": {
"label": "Proforma number",
"placeholder": "",
"description": ""
},
"invoice_date": {
"label": "Proforma date",
"placeholder": "Select a date",
"description": "Proforma date"
},
"series": {
"label": "Serie",
"placeholder": "",
"description": "Proforma serie"
},
"operation_date": {
"label": "Operation date",
"placeholder": "Select a date",
"description": "Proforma operation date"
},
"reference": {
"label": "Reference",
"placeholder": "Reference of the proforma",
"description": "Reference of the proforma"
},
"description": {
"label": "Description",
"placeholder": "",
"description": ""
"placeholder": "Description of the proforma",
"description": "General description of the proforma"
},
"unit_amount": {
"label": "Unit price",
"subtotal_amount": {
"label": "Subtotal",
"placeholder": "",
"description": "Item unit price"
"desc": "Proforma subtotal"
},
"discount_percentage": {
"label": "Dto (%)",
"discount": {
"label": "Discount (%)",
"placeholder": "",
"description": "Percentage discount"
"desc": "Percentage discount"
},
"discount_amount": {
"label": "Discount amount",
"label": "Discount price",
"placeholder": "",
"description": "Percentage discount amount"
},
"taxable_amount": {
"label": "Taxable amount",
"placeholder": "",
"description": ""
},
"tax_codes": {
"label": "Taxes",
"placeholder": "",
"description": "Taxes"
},
"taxes_amount": {
"label": "Taxes amount",
"placeholder": "",
"description": "Percentage taxes amount"
"desc": "Percentage discount price"
},
"total_amount": {
"label": "Total amount",
"label": "Total price",
"placeholder": "",
"description": "Proforma line total"
"desc": "Proforma total price"
},
"notes": {
"label": "Notes",
"placeholder": "Additional notes about the proforma",
"description": "Additional notes that can be included in the proforma"
},
"item": {
"quantity": {
"label": "Quantity",
"placeholder": "",
"description": ""
},
"description": {
"label": "Description",
"placeholder": "",
"description": ""
},
"unit_amount": {
"label": "Unit price",
"placeholder": "",
"description": "Item unit price"
},
"discount_percentage": {
"label": "Dto (%)",
"placeholder": "",
"description": "Percentage discount"
},
"discount_amount": {
"label": "Discount amount",
"placeholder": "",
"description": "Percentage discount amount"
},
"taxable_amount": {
"label": "Taxable amount",
"placeholder": "",
"description": ""
},
"tax_codes": {
"label": "Taxes",
"placeholder": "",
"description": "Taxes"
},
"taxes_amount": {
"label": "Taxes amount",
"placeholder": "",
"description": "Percentage taxes amount"
},
"total_amount": {
"label": "Total amount",
"placeholder": "",
"description": "Proforma line total"
}
}
}
},

View File

@ -177,9 +177,15 @@
"description": "Crear una nueva proforma",
"back_to_list": "Volver al listado"
},
"edit": {
"update": {
"title": "Editar proforma",
"description": "Editar la proforma seleccionada"
"description": "Editar la proforma seleccionada",
"load_error.message": "Inténtalo de nuevo más tarde",
"load_error.title": "No se pudo cargar la proforma",
"not_found_msg": "Revisa el identificador o vuelve al listado",
"not_found_title": "Proforma no encontrada",
"error_msg": "Revisa los datos e inténtalo de nuevo.",
"error_title": "No se pudo guardar los cambios"
},
"delete": {
"title": "Eliminar proforma",
@ -223,128 +229,132 @@
}
},
"form_groups": {
"customer": {
"title": "Cliente",
"description": "Selecciona el cliente para esta proforma"
},
"items": {
"title": "Detalles de la proforma",
"description": ""
},
"basic_info": {
"title": "Información de la proforma",
"description": "Información básica de la proforma"
},
"totals": {
"title": "Totales de la proforma",
"description": "Desglose de los importes de la proforma con descuentos e impuestos."
},
"preferences": {
"title": "Preferencias",
"description": "Configuraciones adicionales de la proforma"
"proformas": {
"customer": {
"title": "Cliente",
"description": "Selecciona el cliente para esta proforma"
},
"items": {
"title": "Detalles de la proforma",
"description": ""
},
"basic_info": {
"title": "Información de la proforma",
"description": "Información básica de la proforma"
},
"totals": {
"title": "Totales de la proforma",
"description": "Desglose de los importes de la proforma con descuentos e impuestos."
},
"preferences": {
"title": "Preferencias",
"description": "Configuraciones adicionales de la proforma"
}
}
},
"form_fields": {
"invoice_number": {
"label": "Número de proforma",
"placeholder": "",
"description": ""
},
"invoice_date": {
"label": "Fecha",
"placeholder": "Selecciona una fecha",
"description": "Fecha de emisión de la proforma"
},
"series": {
"label": "Serie",
"placeholder": "",
"description": ""
},
"operation_date": {
"label": "Fecha de operación",
"placeholder": "Selecciona una fecha",
"description": "Fecha de la operación de la proforma"
},
"reference": {
"label": "Referencia",
"placeholder": "Referencia de la proforma",
"description": "Referencia de la proforma"
},
"description": {
"label": "Descripción",
"placeholder": "Descripción de la proforma",
"description": "Descripción general de la proforma"
},
"subtotal_amount": {
"label": "Subtotal",
"placeholder": "",
"desc": "Subtotal de la proforma"
},
"discount": {
"label": "Descuento (%)",
"placeholder": "",
"desc": "Porcentaje de descuento"
},
"discount_amount": {
"label": "Importe del descuento",
"placeholder": "",
"desc": "Importe del descuento porcentual"
},
"total_amount": {
"label": "Precio total",
"placeholder": "",
"desc": "Precio total de la proforma"
},
"notes": {
"label": "Notas",
"placeholder": "Notas adicionales sobre la proforma",
"description": "Notas adicionales que se pueden incluir en la proforma"
},
"item": {
"quantity": {
"label": "Cantidad",
"proformas": {
"invoice_number": {
"label": "Número de proforma",
"placeholder": "",
"description": ""
},
"invoice_date": {
"label": "Fecha",
"placeholder": "Selecciona una fecha",
"description": "Fecha de emisión de la proforma"
},
"series": {
"label": "Serie",
"placeholder": "",
"description": ""
},
"operation_date": {
"label": "Fecha de operación",
"placeholder": "Selecciona una fecha",
"description": "Fecha de la operación de la proforma"
},
"reference": {
"label": "Referencia",
"placeholder": "Referencia de la proforma",
"description": "Referencia de la proforma"
},
"description": {
"label": "Descripción",
"placeholder": "",
"description": ""
"placeholder": "Descripción de la proforma",
"description": "Descripción general de la proforma"
},
"unit_amount": {
"label": "Precio unitario",
"subtotal_amount": {
"label": "Subtotal",
"placeholder": "",
"description": "Precio unitario del producto"
"desc": "Subtotal de la proforma"
},
"discount_percentage": {
"label": "Dto (%)",
"discount": {
"label": "Descuento (%)",
"placeholder": "",
"description": "Porcentaje de descuento"
"desc": "Porcentaje de descuento"
},
"discount_amount": {
"label": "Importe del descuento",
"placeholder": "",
"description": "Importe del descuento porcentual"
},
"taxable_amount": {
"label": "Subtotal",
"placeholder": "",
"description": ""
},
"tax_codes": {
"label": "Impuestos",
"placeholder": "",
"description": "Impuestos"
},
"taxes_amount": {
"label": "Importe impuestos",
"placeholder": "",
"description": "Importe porcentual de los impuestos"
"desc": "Importe del descuento porcentual"
},
"total_amount": {
"label": "Precio total",
"placeholder": "",
"description": "Precio total con descuento porcentual"
"desc": "Precio total de la proforma"
},
"notes": {
"label": "Notas",
"placeholder": "Notas adicionales sobre la proforma",
"description": "Notas adicionales que se pueden incluir en la proforma"
},
"item": {
"quantity": {
"label": "Cantidad",
"placeholder": "",
"description": ""
},
"description": {
"label": "Descripción",
"placeholder": "",
"description": ""
},
"unit_amount": {
"label": "Precio unitario",
"placeholder": "",
"description": "Precio unitario del producto"
},
"discount_percentage": {
"label": "Dto (%)",
"placeholder": "",
"description": "Porcentaje de descuento"
},
"discount_amount": {
"label": "Importe del descuento",
"placeholder": "",
"description": "Importe del descuento porcentual"
},
"taxable_amount": {
"label": "Subtotal",
"placeholder": "",
"description": ""
},
"tax_codes": {
"label": "Impuestos",
"placeholder": "",
"description": "Impuestos"
},
"taxes_amount": {
"label": "Importe impuestos",
"placeholder": "",
"description": "Importe porcentual de los impuestos"
},
"total_amount": {
"label": "Precio total",
"placeholder": "",
"description": "Precio total con descuento porcentual"
}
}
}
},

View File

@ -2,4 +2,3 @@ export * from "./proforma-form-field-shell";
export * from "./proforma-header-fields-card";
export * from "./proforma-header-form-grid";
export * from "./proforma-section-card";
export * from "./proforma-update-editor-form";

View File

@ -1 +1,2 @@
export * from "./proforma-update-editor-form";
export * from "./proforma-update-header-editor";

View File

@ -3,11 +3,11 @@
import { Button } from "@repo/shadcn-ui/components";
import { cn } from "@repo/shadcn-ui/lib/utils";
import { ProformaUpdateHeaderEditor } from ".";
import { useTranslation } from "../../../../i18n";
import type { Proforma } from "../../../shared/entities";
import { ProformaUpdateHeaderEditor } from "../editors";
import { ProformaHeaderFieldsCard } from "./proforma-header-fields-card";
import { ProformaHeaderFieldsCard } from "../blocks/proforma-header-fields-card";
type ProformaUpdateEditorProps = {
formId: string;
@ -37,6 +37,7 @@ export const ProformaUpdateEditorForm = ({
paymentMethodOptions={[]}
priceListOptions={[]}
salesPersonOptions={[]}
serieOptions={[]}
statusOptions={[]}
warehouseOptions={[]}
/>

View File

@ -1,4 +1,4 @@
import { DatePickerField, TextField } from "@repo/rdx-ui/components";
import { DatePickerField, SelectField, TextField } from "@repo/rdx-ui/components";
import {
Input,
Select,
@ -21,6 +21,7 @@ interface SelectOption {
interface ProformaUpdateHeaderEditorProps {
statusOptions: SelectOption[];
serieOptions: SelectOption[];
customerOptions: SelectOption[];
currencyOptions: SelectOption[];
paymentMethodOptions: SelectOption[];
@ -33,6 +34,7 @@ interface ProformaUpdateHeaderEditorProps {
export const ProformaUpdateHeaderEditor = ({
statusOptions,
serieOptions,
customerOptions,
currencyOptions,
paymentMethodOptions,
@ -55,46 +57,46 @@ export const ProformaUpdateHeaderEditor = ({
return (
<div className="space-y-6">
<ProformaSectionCard
description={t("proformas.update.sections.document_description")}
title={t("proformas.update.sections.document")}
description={t("form_groups.proformas.basic_info.description")}
title={t("form_groups.proformas.basic_info.title")}
>
<ProformaHeaderFormGrid>
<TextField
<SelectField
className="md:col-span-2"
label={t("form_fields.series.label")}
label={t("form_fields.proformas.series.label")}
name="series"
placeholder={t("form_fields.series.placeholder")}
placeholder={t("form_fields.proformas.series.placeholder")}
/>
<DatePickerField
className="md:col-span-3"
label={t("form_fields.invoice_date.label")}
label={t("form_fields.proformas.invoice_date.label")}
name="invoice_date"
placeholder={t("form_fields.invoice_date.placeholder")}
placeholder={t("form_fields.proformas.invoice_date.placeholder")}
required
/>
<DatePickerField
className="md:col-span-3"
label={t("form_fields.operation_date.label")}
label={t("form_fields.proformas.operation_date.label")}
name="operation_date"
placeholder={t("form_fields.operation_date.placeholder")}
placeholder={t("form_fields.proformas.operation_date.placeholder")}
/>
<TextField
className="md:col-span-4"
label={t("form_fields.reference.label")}
label={t("form_fields.proformas.reference.label")}
maxLength={256}
name="reference"
placeholder={t("form_fields.reference.placeholder")}
placeholder={t("form_fields.proformas.reference.placeholder")}
/>
<TextField
className="md:col-span-12"
label={t("form_fields.description.label")}
label={t("form_fields.proformas.description.label")}
maxLength={256}
name="description"
placeholder={t("form_fields.description.placeholder")}
placeholder={t("form_fields.proformas.description.placeholder")}
/>
</ProformaHeaderFormGrid>
</ProformaSectionCard>

View File

@ -8,8 +8,8 @@ import { FormProvider } from "react-hook-form";
import { useTranslation } from "../../../../i18n";
import { useUpdateProformaPageController } from "../../controllers/use-update-proforma-page-controller";
import { ProformaUpdateEditorForm } from "../blocks";
import { ProformaUpdateSkeleton } from "../components";
import { ProformaUpdateEditorForm } from "../editors";
export const ProformaUpdatePage = () => {
const { t } = useTranslation();
@ -28,9 +28,9 @@ export const ProformaUpdatePage = () => {
message={
updateCtrl.loadError instanceof Error
? updateCtrl.loadError.message
: t("proformas.update.load_error.message", "Inténtalo de nuevo más tarde")
: t("pages.proformas.update.load_error.message", "Inténtalo de nuevo más tarde")
}
title={t("proformas.update.load_error.title", "No se pudo cargar la proforma")}
title={t("pages.proformas.update.load_error.title", "No se pudo cargar la proforma")}
/>
<div className="flex items-center justify-end">
@ -45,8 +45,11 @@ export const ProformaUpdatePage = () => {
<>
<AppContent>
<NotFoundCard
message={t("pages.update.notFoundMsg", "Revisa el identificador o vuelve al listado.")}
title={t("pages.update.notFoundTitle", "Proforma no encontrada")}
message={t(
"pages.proformas.update.not_found_msg",
"Revisa el identificador o vuelve al listado."
)}
title={t("pages.proformas.update.not_found_title", "Proforma no encontrada")}
/>
</AppContent>
</>
@ -57,7 +60,7 @@ export const ProformaUpdatePage = () => {
<AppHeader className="space-y-4 max-w-5xl mx-auto">
<PageHeader
backIcon
description={t("pages.update.description")}
description={t("pages.proformas.update.description")}
rightSlot={
<UpdateCommitButtonGroup
cancel={{
@ -74,7 +77,7 @@ export const ProformaUpdatePage = () => {
}}
/>
}
title={t("pages.update.title")}
title={t("pages.proformas.update.title")}
/>
</AppHeader>
<AppContent className="space-y-4 max-w-5xl mx-auto">
@ -83,9 +86,9 @@ export const ProformaUpdatePage = () => {
<ErrorAlert
message={
(updateCtrl.updateError as Error)?.message ??
t("pages.update.errorMsg", "Revisa los datos e inténtalo de nuevo.")
t("pages.proformas.update.error_msg", "Revisa los datos e inténtalo de nuevo.")
}
title={t("pages.update.errorTitle", "No se pudo guardar los cambios")}
title={t("pages.proformas.update.error_title", "No se pudo guardar los cambios")}
/>
)}

View File

@ -31,7 +31,7 @@ type SelectFieldProps<TFormValues extends FieldValues> = {
readOnly?: boolean;
placeholder?: string;
items: SelectFieldItem[];
items?: SelectFieldItem[];
orientation?: "vertical" | "horizontal" | "responsive";
@ -50,7 +50,7 @@ export function SelectField<TFormValues extends FieldValues>({
readOnly = false,
placeholder,
items,
items = [],
orientation = "vertical",
@ -66,6 +66,13 @@ export function SelectField<TFormValues extends FieldValues>({
control={control}
name={name}
render={({ field, fieldState }) => {
const fieldValue = typeof field.value === "string" ? field.value.trim() : "";
const normalizedItems =
fieldValue && !items.some((item) => item.value === fieldValue)
? [{ value: fieldValue, label: fieldValue }, ...items]
: items;
return (
<Field
className={cn("gap-1", className)}
@ -103,7 +110,7 @@ export function SelectField<TFormValues extends FieldValues>({
</SelectTrigger>
</FormControl>
<SelectContent>
{items.map((item) => (
{normalizedItems.map((item) => (
<SelectItem key={`key-${item.value}`} value={item.value}>
{item.label}
</SelectItem>

View File

@ -15,9 +15,9 @@ export function SiteHeader() {
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
@ -39,9 +39,9 @@ export function SiteHeader() {
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
@ -57,6 +57,7 @@ export function SiteHeader() {
data-size="icon"
data-slot="button"
data-variant="ghost"
type="button"
>
<svg
aria-hidden="true"
@ -64,9 +65,9 @@ export function SiteHeader() {
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
@ -139,9 +140,9 @@ export function SiteHeader() {
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
@ -156,6 +157,7 @@ export function SiteHeader() {
data-size="icon-sm"
data-slot="button"
data-variant="ghost"
type="button"
>
<svg
aria-hidden="true"
@ -163,9 +165,9 @@ export function SiteHeader() {
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
@ -191,9 +193,9 @@ export function SiteHeader() {
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
@ -211,7 +213,7 @@ export function SiteHeader() {
data-slot="separator"
role="none"
/>
<span
<button
aria-expanded="false"
aria-haspopup="menu"
className="group/avatar relative flex size-8 shrink-0 overflow-hidden rounded-full select-none data-[size=lg]:size-10 data-[size=sm]:size-6"
@ -227,7 +229,7 @@ export function SiteHeader() {
data-slot="avatar-image"
src="/images/avatars/01.png"
/>
</span>
</button>
</div>
</div>
</header>