This commit is contained in:
David Arranz 2026-04-12 21:58:11 +02:00
parent 723f58293a
commit 1167f5bc14
14 changed files with 49 additions and 183 deletions

View File

@ -1,4 +1,3 @@
export * from "./items";
export * from "./proforma-basic-info-fields";
export * from "./proforma-totals";
export * from "./recipient";

View File

@ -1,2 +0,0 @@
export * from "./proforma-recipient";
export * from "./proforma-recipient-modal-selector-field";

View File

@ -1,73 +0,0 @@
import { CustomerModalSelector } from "@erp/customers/components";
import { Field, FieldLabel } from "@repo/shadcn-ui/components";
import { cn } from "@repo/shadcn-ui/lib/utils";
import type { CustomerSummary } from "node_modules/@erp/customers/src/web/schemas";
import { type Control, Controller, type FieldPath, type FieldValues } from "react-hook-form";
type RecipientModalSelectorFieldProps<TFormValues extends FieldValues> = {
control: Control<TFormValues>;
name: FieldPath<TFormValues>;
label?: string;
description?: string;
orientation?: "vertical" | "horizontal" | "responsive";
disabled?: boolean;
required?: boolean;
readOnly?: boolean;
className?: string;
initialRecipient?: unknown;
};
export function RecipientModalSelectorField<TFormValues extends FieldValues>({
control,
name,
label,
description,
orientation = "vertical",
disabled = false,
required = false,
readOnly = false,
className,
initialRecipient = {},
}: RecipientModalSelectorFieldProps<TFormValues>) {
const isDisabled = disabled;
const isReadOnly = readOnly && !disabled;
return (
<Controller
control={control}
name={name}
render={({ field, fieldState }) => {
const { name, value, onChange, onBlur, ref } = field;
return (
<Field
className={cn("gap-1", className)}
data-invalid={fieldState.invalid}
orientation={orientation}
>
{label && (
<FieldLabel className="text-xs text-muted-foreground text-nowrap" htmlFor={name}>
{label}
</FieldLabel>
)}
<CustomerModalSelector
disabled={isDisabled}
initialCustomer={{
...(initialRecipient as CustomerSummary),
}}
onValueChange={onChange}
readOnly={isReadOnly}
value={value}
/>
</Field>
);
}}
/>
);
}

View File

@ -1,34 +0,0 @@
import { FieldDescription, FieldGroup, FieldLegend, FieldSet } from "@repo/shadcn-ui/components";
import type { ComponentProps } from "react";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "../../../../../../i18n";
import { RecipientModalSelectorField } from "./proforma-recipient-modal-selector-field";
export const ProformaRecipient = (props: ComponentProps<"fieldset">) => {
const { t } = useTranslation();
const { control, getValues } = useFormContext();
const recipient = getValues("recipient");
return (
<FieldSet {...props}>
<FieldLegend className="hidden text-foreground" variant="label">
{t("form_groups.recipient.title")}
</FieldLegend>
<FieldDescription className="hidden">
{t("form_groups.recipient.description")}
</FieldDescription>
<FieldGroup className="flex flex-row flex-wrap gap-6 xl:flex-nowrap">
<RecipientModalSelectorField
control={control}
initialRecipient={recipient}
label={t("form_groups.customer.title")}
name="customer_id"
/>
</FieldGroup>
</FieldSet>
);
};

View File

@ -3,4 +3,4 @@ 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 "./selected-recipient-summary";
export * from "./selected-recipient";

View File

@ -5,7 +5,7 @@ import { cn } from "@repo/shadcn-ui/lib/utils";
import type { ReactNode } from "react";
interface ProformaSectionCardProps {
title: string;
title?: string;
description?: string;
children: ReactNode;
className?: string;
@ -20,14 +20,16 @@ export const ProformaSectionCard = ({
return (
<section className={cn("rounded-xl border bg-background p-4 md:p-6", className)}>
<FieldSet>
<FieldLegend className="mb-4 space-y-1 md:mb-6">
<h2 className="text-base font-semibold tracking-tight">{title}</h2>
{description ? (
<FieldDescription className="text-sm text-muted-foreground">
{description}
</FieldDescription>
) : null}
</FieldLegend>
{title || description ? (
<FieldLegend className="mb-4 space-y-1 md:mb-6">
{title ? <h2 className="text-base font-semibold tracking-tight">{title}</h2> : null}
{description ? (
<FieldDescription className="text-sm text-muted-foreground">
{description}
</FieldDescription>
) : null}
</FieldLegend>
) : null}
{children}
</FieldSet>

View File

@ -3,7 +3,7 @@ import { cn } from "@repo/shadcn-ui/lib/utils";
import { EyeIcon, MapPinIcon, RefreshCwIcon, UserPlusIcon } from "lucide-react";
import React, { useMemo } from "react";
import type { CustomerSummary } from "../../schemas";
import type { CustomerSummary } from "../../../../../../../../customers/src/web/_archived/schemas";
interface CustomerCardProps {
customer: CustomerSummary;

View File

@ -0,0 +1 @@
export * from "./selected-recipient-summary";

View File

@ -1,15 +1,21 @@
import type { CustomerSelectionOption } from "@erp/customers";
import { Button } from "@repo/shadcn-ui/components";
import { useTranslation } from "../../../../i18n";
import { useTranslation } from "../../../../../i18n";
type SelectedRecipientSummaryProps = {
disabled?: boolean;
readOnly?: boolean;
recipient?: CustomerSelectionOption | null;
onChangeClick: () => void;
onCreateClick?: () => void;
};
export const SelectedRecipientSummary = ({
disabled = false,
readOnly = false,
recipient,
onChangeClick,
onCreateClick,
@ -17,11 +23,9 @@ export const SelectedRecipientSummary = ({
const { t } = useTranslation();
return (
<div className="rounded-lg border p-4">
<div>
<div className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
<div className="min-w-0">
<p className="text-sm font-medium">{t("customers.selected_customer.title", "Cliente")}</p>
{recipient ? (
<div className="mt-2 space-y-1">
<p className="truncate font-medium">{recipient.name}</p>
@ -37,17 +41,19 @@ export const SelectedRecipientSummary = ({
</div>
<div className="flex flex-wrap items-center gap-2">
{onCreateClick ? (
{onCreateClick && !readOnly && !disabled && (
<Button onClick={onCreateClick} type="button" variant="outline">
{t("customers.selected_customer.new_customer", "Nuevo cliente")}
</Button>
) : null}
)}
<Button onClick={onChangeClick} type="button" variant="outline">
{recipient
? t("customers.selected_customer.change", "Cambiar cliente")
: t("customers.selected_customer.select", "Seleccionar cliente")}
</Button>
{onChangeClick && !readOnly && !disabled && (
<Button onClick={onChangeClick} type="button" variant="secondary">
{recipient
? t("customers.selected_customer.change", "Cambiar cliente")
: t("customers.selected_customer.select", "Seleccionar cliente")}
</Button>
)}
</div>
</div>
</div>

View File

@ -1,9 +1,7 @@
import type { CustomerSelectionOption } from "@erp/customers";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "../../../../i18n";
import type { ProformaUpdateForm } from "../../entities";
import { ProformaHeaderFormGrid, ProformaSectionCard, SelectedRecipientSummary } from "../blocks";
import { ProformaSectionCard, SelectedRecipientSummary } from "../blocks";
interface ProformaUpdateRecipientEditorProps {
disabled?: boolean;
@ -24,26 +22,15 @@ export const ProformaUpdateRecipientEditor = ({
}: ProformaUpdateRecipientEditorProps) => {
const { t } = useTranslation();
const {
register,
control,
formState: { errors },
} = useFormContext<ProformaUpdateForm>();
const isFieldLocked = disabled || readOnly;
return (
<ProformaSectionCard
description={t("form_groups.proformas.customer.description")}
title={t("form_groups.proformas.customer.title")}
>
<ProformaHeaderFormGrid>
<SelectedRecipientSummary
onChangeClick={onChangeCustomerClick}
onCreateClick={onCreateCustomerClick}
recipient={selectedCustomer}
/>
</ProformaHeaderFormGrid>
<ProformaSectionCard title={t("form_groups.proformas.customer.title")}>
<SelectedRecipientSummary
disabled={disabled}
onChangeClick={onChangeCustomerClick}
onCreateClick={onCreateCustomerClick}
readOnly={readOnly}
recipient={selectedCustomer}
/>
</ProformaSectionCard>
);
};

View File

@ -1,27 +0,0 @@
import { UserSearchIcon } from "lucide-react";
export const CustomerEmptyCard = (props: React.ComponentProps<"button">) => {
return (
<button
aria-label="Seleccionar cliente"
className="group w-full cursor-pointer rounded-lg border border-border bg-card p-4 transition hover:bg-accent/50 hover:border-primary"
tabIndex={0}
type="button"
{...props}
>
<div className="flex items-center gap-4 group-hover:text-primary">
<div className="flex size-12 items-center justify-center rounded-full bg-muted group-hover:bg-primary/15">
<UserSearchIcon className="size-6 text-muted-foreground group-hover:text-primary" />
</div>
<div className="flex-1 text-left">
<h3 className="font-medium text-muted-foreground mb-1 group-hover:text-primary">
Seleccionar Cliente
</h3>
<p className="text-sm text-muted-foreground/70 group-hover:text-primary">
Haz clic para buscar un cliente existente o crear uno nuevo
</p>
</div>
</div>
</button>
);
};

View File

@ -1,5 +1,6 @@
import { useEffect, useId, useMemo, useState } from "react";
import { CustomerEmptyCard } from "../../../../../../customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/selected-recipient-empty-card";
import { useCustomerListQuery } from "../../hooks";
import {
type CustomerFormData,
@ -7,9 +8,10 @@ import {
defaultCustomerFormData,
} from "../../schemas";
../../../../../../customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/customer-card
import { CustomerCard } from "./customer-card";
import { CustomerCreateModal } from "./customer-create-modal";
import { CustomerEmptyCard } from "./customer-empty-card";
import { CustomerSearchDialog } from "./customer-search-dialog";
import { CustomerViewDialog } from "./customer-view-dialog";

View File

@ -28,6 +28,11 @@
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"],
"include": [
"src",
"../customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/customer-view-dialog.tsx",
"../customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/customer-card.tsx",
"../customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/selected-recipient-empty-card.tsx"
],
"exclude": ["node_modules"]
}