From 482dd5ef569aa5678a88cd0d41d3e884da223641 Mon Sep 17 00:00:00 2001 From: david Date: Wed, 29 Apr 2026 23:03:57 +0200 Subject: [PATCH] . --- .../selected-recipient/customer-card.tsx | 12 +- .../selected-recipient-summary.tsx | 236 ++++++++++++++++-- .../editors/proforma-update-header-editor.tsx | 4 +- .../editors/proforma-update-items-editor.tsx | 3 + .../proforma-update-recipient-editor.tsx | 11 +- .../update/ui/pages/proforma-update-page.tsx | 2 +- .../customer-selection-option.entity.ts | 30 +-- .../utils/build-customer-selection-option.ts | 13 +- .../src/components/form/form-section-card.tsx | 39 ++- 9 files changed, 287 insertions(+), 63 deletions(-) diff --git a/modules/customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/customer-card.tsx b/modules/customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/customer-card.tsx index 1d45932d..a77c7cd1 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/customer-card.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/customer-card.tsx @@ -15,20 +15,20 @@ interface CustomerCardProps { function buildAddress(customer: CustomerSummary) { // Línea 1: calle(s) - const line1 = [customer.street, customer.street2].filter(Boolean).join(", "); + const line1 = [customer.address.street, customer.address.street2].filter(Boolean).join(", "); // Línea 2: CP + ciudad con espacio no rompible entre CP y ciudad const line2Parts: string[] = []; - if (customer.postal_code && customer.city) { - line2Parts.push(`${customer.postal_code}\u00A0${customer.city}`); // CP Ciudad + if (customer.address.postal_code && customer.address.city) { + line2Parts.push(`${customer.address.postal_code}\u00A0${customer.address.city}`); // CP Ciudad } else { - if (customer.postal_code) line2Parts.push(customer.postal_code); - if (customer.city) line2Parts.push(customer.city); + if (customer.address.postal_code) line2Parts.push(customer.address.postal_code); + if (customer.address.city) line2Parts.push(customer.address.city); } const line2 = line2Parts.join(" "); // Línea 3: provincia + país - const line3 = [customer.province, customer.country].filter(Boolean).join(", "); + const line3 = [customer.address.province, customer.address.country].filter(Boolean).join(", "); const stack = [line1, line2, line3].filter(Boolean); const inline = stack.join(" · "); // separador compacto diff --git a/modules/customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/selected-recipient-summary.tsx b/modules/customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/selected-recipient-summary.tsx index 4fd1a77a..fa8a826b 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/selected-recipient-summary.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/selected-recipient-summary.tsx @@ -1,5 +1,23 @@ import type { CustomerSelectionOption } from "@erp/customers"; -import { Button } from "@repo/shadcn-ui/components"; +import { + Badge, + Button, + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@repo/shadcn-ui/components"; +import { cn } from "@repo/shadcn-ui/lib/utils"; +import { + Building2Icon, + ExternalLinkIcon, + MailIcon, + MapPinIcon, + PhoneIcon, + PlusIcon, + RefreshCwIcon, + UserIcon, +} from "lucide-react"; import { useTranslation } from "../../../../../i18n"; @@ -8,8 +26,40 @@ type SelectedRecipientSummaryProps = { readOnly?: boolean; recipient?: CustomerSelectionOption | null; + onChangeClick: () => void; onCreateClick?: () => void; + onViewClick?: (recipient: CustomerSelectionOption) => void; +}; + +const buildCustomerAddress = (recipient: CustomerSelectionOption): string => { + return [ + recipient.street, + recipient.street2, + [recipient.postalCode, recipient.city].filter(Boolean).join(" "), + recipient.province, + recipient.country, + ] + .filter(Boolean) + .join(", "); +}; + +const getCustomerStatusBadgeClassName = (status: string): string => { + const normalizedStatus = status.toLowerCase(); + + if (["active", "activo", "enabled"].includes(normalizedStatus)) { + return "border-emerald-200 bg-emerald-50 text-emerald-700"; + } + + if (["inactive", "inactivo", "disabled"].includes(normalizedStatus)) { + return "border-muted bg-muted text-muted-foreground"; + } + + if (["blocked", "bloqueado"].includes(normalizedStatus)) { + return "border-destructive/20 bg-destructive/10 text-destructive"; + } + + return "border-border bg-muted/50 text-muted-foreground"; }; export const SelectedRecipientSummary = ({ @@ -19,43 +69,177 @@ export const SelectedRecipientSummary = ({ recipient, onChangeClick, onCreateClick, + onViewClick, }: SelectedRecipientSummaryProps) => { const { t } = useTranslation(); + const showActions = !(readOnly || disabled); + const address = recipient ? buildCustomerAddress(recipient) : ""; + const phone = recipient?.primaryPhone ?? recipient?.primaryMobile; + return ( -
-
-
- {recipient ? ( -
-

{recipient.name}

- {recipient.tin ? ( -

{recipient.tin}

- ) : null} -
- ) : ( -

- {t("customers.selected_customer.empty", "No hay ningún cliente seleccionado")} -

- )} +
+
+
+
+ {recipient?.isCompany ? ( + + ) : ( + + )} +
+ +
+ {recipient ? ( +
+
+
+
+

{recipient.name}

+ + {recipient.status ? ( + + {recipient.status} + + ) : null} +
+ + {recipient.tradeName && recipient.tradeName !== recipient.name ? ( +

+ {recipient.tradeName} +

+ ) : null} + + {recipient.tin ? ( +

{recipient.tin}

+ ) : null} +
+ +
+ {recipient.primaryEmail ? ( +
+ + {recipient.primaryEmail} +
+ ) : null} + + {phone ? ( +
+ + {phone} +
+ ) : null} +
+
+ +
+ {address ? ( + + + + + {address} +
+ } + /> + + + {address} + + + + ) : ( +

+ {t("customers.selected_customer.no_address", "Sin dirección registrada")} +

+ )} +
+
+ ) : ( +
+

+ {t("customers.selected_customer.empty_title", "Cliente no seleccionado")} +

+

+ {t("customers.selected_customer.empty", "Selecciona o crea un cliente")} +

+
+ )} +
-
- {onCreateClick && !readOnly && !disabled && ( - - )} + {showActions ? ( +
+ {recipient && onViewClick ? ( + + ) : null} - {onChangeClick && !readOnly && !disabled && ( - + ) : null} + + - )} -
+
+ ) : null}
); }; + +const buildAddress = (recipient: CustomerSelectionOption): string => { + // Línea 1: calle(s) + const line1 = [recipient.street, recipient.street2].filter(Boolean).join(", "); + + // Línea 2: CP + ciudad con espacio no rompible entre CP y ciudad + const line2Parts: string[] = []; + if (recipient.postalCode && recipient.city) { + line2Parts.push(`${recipient.postalCode}\u00A0${recipient.city}`); // CP Ciudad + } else { + if (recipient.postalCode) line2Parts.push(recipient.postalCode); + if (recipient.city) line2Parts.push(recipient.city); + } + const line2 = line2Parts.join(" "); + + // Línea 3: provincia + país + const line3 = [recipient.province, recipient.country].filter(Boolean).join(", "); + + const stack = [line1, line2, line3].filter(Boolean); + const inline = stack.join(" · "); // separador compacto + const full = stack.join(", "); + + return { has: stack.length > 0, stack, inline, full }; +}; diff --git a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-header-editor.tsx b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-header-editor.tsx index aca168a3..2daa188d 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-header-editor.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-header-editor.tsx @@ -6,6 +6,7 @@ import { TextAreaField, TextField, } from "@repo/rdx-ui/components"; +import { FileTextIcon } from "lucide-react"; import { useTranslation } from "../../../../i18n"; @@ -23,6 +24,8 @@ export const ProformaUpdateHeaderEditor = ({ return ( } title={t("form_groups.proformas.basic_info.title")} > @@ -83,7 +86,6 @@ export const ProformaUpdateHeaderEditor = ({ placeholder={t("form_fields.proformas.notes.placeholder")} readOnly={readOnly} /> - ); diff --git a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-items-editor.tsx b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-items-editor.tsx index 52f82a77..1a7563d3 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-items-editor.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-items-editor.tsx @@ -1,4 +1,5 @@ import { FormSectionCard } from "@repo/rdx-ui/components"; +import { ListIcon } from "lucide-react"; import type { ComponentProps } from "react"; import { useTranslation } from "../../../../i18n"; @@ -19,6 +20,8 @@ export const ProformaUpdateItemsEditor = ({ return ( } title={t("form_groups.items.title")} > + } + title={t("form_groups.proformas.customer.title", "Cliente")} + > { }} /> } - title={t("pages.proformas.update.title")} + title={<>{t("pages.proformas.update.title")}} /> diff --git a/modules/customers/src/common/features/customer-selection/entities/customer-selection-option.entity.ts b/modules/customers/src/common/features/customer-selection/entities/customer-selection-option.entity.ts index 3ce47d45..31bf0245 100644 --- a/modules/customers/src/common/features/customer-selection/entities/customer-selection-option.entity.ts +++ b/modules/customers/src/common/features/customer-selection/entities/customer-selection-option.entity.ts @@ -5,25 +5,21 @@ */ export interface CustomerSelectionOption { id: string; - //status: string; + status: string; - //isCompany: boolean; + isCompany: boolean; name: string; - //tradeName: string; - tin: string; + tradeName: string | null; + tin: string | null; - /*street: string; - street2: string; - city: string; - province: string; - postalCode: string; - country: string; - */ + street: string | null; + street2: string | null; + city: string | null; + province: string | null; + postalCode: string | null; + country: string | null; - //emailPrimary: string; - //primaryPhone: string; - //primaryMobile: string; - - languageCode: string; - currencyCode: string; + primaryEmail: string | null; + primaryPhone: string | null; + primaryMobile: string | null; } diff --git a/modules/customers/src/common/features/customer-selection/utils/build-customer-selection-option.ts b/modules/customers/src/common/features/customer-selection/utils/build-customer-selection-option.ts index dc105c62..c7a1550a 100644 --- a/modules/customers/src/common/features/customer-selection/utils/build-customer-selection-option.ts +++ b/modules/customers/src/common/features/customer-selection/utils/build-customer-selection-option.ts @@ -13,11 +13,18 @@ export const buildCustomerSelectionOption = ( isCompany: customer.isCompany, name: customer.name, + tradeName: customer.tradeName, tin: customer.tin, - EmailPrimary: customer.EmailPrimary, + street: customer.address.street, + street2: null, + city: customer.address.city, + province: customer.address.province, + postalCode: customer.address.postalCode, + country: customer.address.country, - languageCode: customer.languageCode, - currencyCode: customer.currencyCode, + primaryEmail: customer.contact.primaryEmail, + primaryPhone: customer.contact.primaryPhone, + primaryMobile: customer.contact.primaryMobile, }; }; diff --git a/packages/rdx-ui/src/components/form/form-section-card.tsx b/packages/rdx-ui/src/components/form/form-section-card.tsx index de28fe13..0565effe 100644 --- a/packages/rdx-ui/src/components/form/form-section-card.tsx +++ b/packages/rdx-ui/src/components/form/form-section-card.tsx @@ -13,7 +13,11 @@ import type { ReactNode } from "react"; interface FormSectionCardProps { title?: string; description?: string; + icon?: ReactNode; + disabled?: boolean; + children: ReactNode; + className?: string; headerClassName?: string; contentClassName?: string; @@ -22,7 +26,11 @@ interface FormSectionCardProps { export const FormSectionCard = ({ title, description, + icon, + disabled = false, + children, + className, headerClassName, contentClassName, @@ -30,17 +38,32 @@ export const FormSectionCard = ({ const hasHeader = Boolean(title || description); return ( - -
+ +
{hasHeader ? ( - - -
- {title ? ( - {title} + + +
+ {icon ? ( +
+ {icon} +
) : null} - {description ? {description} : null} +
+ {title ? ( + + {title} + + ) : null} + + {description ? {description} : null} +